• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { assert, expect } from 'chai';
17import { before } from 'mocha';
18import {
19  decodeSourcemap,
20  mergeSourceMap,
21  Source,
22  SourceMapLink
23} from '../../../src/utils/SourceMapMergingUtil';
24import type {
25  ExistingDecodedSourceMap,
26  SourceMapSegmentObj
27} from '../../../src/utils/SourceMapMergingUtil';
28import type { RawSourceMap } from 'typescript';
29
30describe('test for SourceMapMergingUtil', function () {
31  it('test the sourcemap merging', function () {
32    /**
33     * // source ts code:
34     * function foo(){
35     *   console.log("hello world");
36     *   return 1;
37     * }
38     */
39    const previousMap = {
40      "version": 3,
41      "file": "index.js",
42      "sources": [
43        "index.ts"
44      ],
45      "names": [],
46      "mappings": "AAAA,SAAS,GAAG;IACV,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAC3B,OAAO,CAAC,CAAC;AACX,CAAC",
47      "sourceRoot": "",
48    }
49    /**
50     * // transformed ts code:
51     * function foo() {
52     *     console.log("hello world");
53     *     return 1;
54     * }
55     */
56    const currentMap = {
57      "version": 3,
58      "file": "index.js",
59      "sources": [
60        "index.js"
61      ],
62      "names": [],
63      "mappings": "AAAA;IAEI,OAAO,CAAC,CAAC;CACZ",
64      "sourceRoot": "",
65    }
66    /**
67     * // obfuscated code:
68     * function a(){
69     *     return 1;
70     * }
71     */
72    const actual = mergeSourceMap(previousMap as RawSourceMap, currentMap as RawSourceMap);
73    const expect = '{"version":3,"file":"index.js","sources":["index.ts"],"names":[],"mappings":"AAAA;IAEE,OAAO,CAAC,CAAC;CACV","sourceRoot":""}'
74    assert.isTrue(JSON.stringify(actual) === expect);
75  });
76
77  describe('test for SourceMapLink', function () {
78    let source: Source;
79    let mappings: ExistingDecodedSourceMap;
80
81    before(() => {
82      source = new Source('example.js', 'console.log("Hello, World!");');
83      mappings = {
84        version: 3,
85        file: 'out.js',
86        sources: ['example.js'],
87        names: ['console', 'log'],
88        mappings: [
89          [[0, 0, 0, 0, 1]]
90        ],
91        sourcesContent: [],
92      };
93    });
94
95    it('should trace mappings correctly', () => {
96      const sourceMapLink = new SourceMapLink(mappings, [source]);
97      const traced = sourceMapLink.traceMappings();
98
99      expect(traced.mappings).to.have.lengthOf(1);
100      expect(traced.names).to.include('log');
101      expect(traced.sources).to.include('example.js');
102    });
103  });
104
105  describe('test for decodeSourcemap', function () {
106    it('should return null for null map', () => {
107      const result = decodeSourcemap(null);
108      expect(result).to.be.null;
109    });
110
111    it('should return empty mappings for empty mappings string', () => {
112      const map: RawSourceMap = {
113        version: 3,
114        file: 'out.js',
115        sources: ['example.js'],
116        names: [],
117        mappings: '', // Empty mappings
118        sourcesContent: ['Hello, World!'],
119      };
120
121      const result = decodeSourcemap(map);
122      expect(result).to.deep.equal({
123        names: [],
124        mappings: [],
125        sources: [],
126        version: 3
127      });
128    });
129  });
130
131  describe('test for traceMappings' ,function () {
132    it('segment.length === 1 && source == true', function () {
133      let source: Source = new Source('filename1', 'content1');
134      let mappings: ExistingDecodedSourceMap = {
135        version: 3,
136        sources: ['abc', 'def'],
137        mappings: [[[0]]]
138      }
139      const sourceMapLink = new SourceMapLink(mappings, [source]);
140      const tracedMappings = sourceMapLink.traceMappings();
141      assert.deepEqual(tracedMappings.mappings, [[]]);
142    });
143
144    it('segment.length !== 1 && source == true', function () {
145      let source: Source = new Source('filename2', 'content2');
146      let mappings: ExistingDecodedSourceMap = {
147        version: 3,
148        sources: ['abc', 'efg'],
149        mappings: [
150          [[0]], [[1]]
151        ]
152      }
153      const sourceMapLink = new SourceMapLink(mappings, [source]);
154      const tracedMappings = sourceMapLink.traceMappings();
155      assert.deepEqual(tracedMappings.mappings, [[], []]);
156    });
157
158    it('segment.length === 1 && source == false', function () {
159      const source = new Source('example.js', 'console.log("Hello, World!");');
160      const mappings: ExistingDecodedSourceMap = {
161        version: 3,
162        file: 'out.js',
163        sources: ['example.js'],
164        names: ['console', 'log'],
165        mappings: [
166          [[0, 5, 0, 0, 1]]
167        ],
168        sourcesContent: ['sourcesContent1']
169      };
170      const sourceMapLink = new SourceMapLink(mappings, [source]);
171      const tracedMappings = sourceMapLink.traceMappings();
172      assert.deepEqual(tracedMappings.mappings, [[]]);
173    });
174
175    it('segment.length !== 1 && source == false', function () {
176      const source = new Source('example.js', 'console.log("Hello, World!");');
177      const mappings: ExistingDecodedSourceMap = {
178        version: 3,
179        file: 'out.js',
180        sources: ['example.js'],
181        names: ['console', 'log'],
182        mappings: [
183          [[0, 5, 0, 0, 1]], [[24]]
184        ],
185        sourcesContent: ['sourcesContent2']
186      };
187      const sourceMapLink = new SourceMapLink(mappings, [source]);
188      const tracedMappings = sourceMapLink.traceMappings();
189      assert.deepEqual(tracedMappings.mappings, [[], []]);
190    });
191  });
192
193  describe('test for analyzeTracedSource' ,function () {
194    let source: Source = new Source('', null);
195    let mappings: ExistingDecodedSourceMap = {
196      version: 3,
197      sources: ['sources'],
198      mappings: [[[0]]]
199    }
200    const sourceMapLink = new SourceMapLink(mappings, [source]);
201
202    it('sourceIndex === undefined', function () {
203      const traced: SourceMapSegmentObj = {
204        column: 6,
205        line: 87,
206        name: 'name_abc',
207        source: new Source('filename', 'content1')
208      }
209      const tracedSources: string[] = ['traced', 'sources'];
210      const sourceIndexMap: Map<string, number> = new Map();
211      const sourcesContent: (string | null)[] = ['sources', 'content'];
212      sourceMapLink.analyzeTracedSource(traced, tracedSources, sourceIndexMap, sourcesContent);
213      assert.deepEqual(sourceMapLink.mappings, [[[0]]]);
214    });
215
216    it('sourcesContent[sourceIndex] == null', function () {
217      const traced: SourceMapSegmentObj = {
218        column: 34,
219        line: 23,
220        name: 'name_bcd',
221        source: new Source('filename', 'content2')
222      }
223      const tracedSources: string[] = ['traced', 'sources'];
224      const sourceIndexMap: Map<string, number> = new Map();
225      sourceIndexMap.set('filename', 1);
226      const sourcesContent: (string | null)[] = ['sources'];
227      sourceMapLink.analyzeTracedSource(traced, tracedSources, sourceIndexMap, sourcesContent);
228      assert.deepEqual(sourceMapLink.mappings, [[[0]]]);
229    });
230
231    it('content != null && sourcesContent[sourceIndex] !== content', function () {
232      const traced: SourceMapSegmentObj = {
233        column: 7,
234        line: 65,
235        name: 'name_efg',
236        source: new Source('filename', 'content3')
237      }
238      const tracedSources: string[] = ['traced', 'sources'];
239      const sourceIndexMap: Map<string, number> = new Map();
240      sourceIndexMap.set('filename', 1);
241      const sourcesContent: (string | null)[] = ['content3', 'content'];
242      assert.throws(() => {
243        sourceMapLink.analyzeTracedSource(traced, tracedSources, sourceIndexMap, sourcesContent);
244      }, Error, `Multiple conflicting contents for sourcemap source: filename`);
245    });
246  });
247
248  describe('test for function traceSegment', function () {
249    let source: Source = new Source('filename', '// traceSegment');
250    let mappings: ExistingDecodedSourceMap = {
251      version: 3,
252      sources: ['sources', '1', 'xx'],
253      mappings: [
254        [[0]], [[1, 2, 3, 4, 5]], [[0, 0, 0, 0]]
255      ],
256      names: ['names1', 'names2', 'names3', 'names4', 'names5']
257    }
258    const sourceMapLink = new SourceMapLink(mappings, [source]);
259
260    it('segment is false', function (){
261      const sourceMapSegmentObj = sourceMapLink.traceSegment(3, 1, 'segment_undefined');
262      assert.strictEqual(sourceMapSegmentObj, null);
263    });
264
265    it('segments is true && tempSegment.length === 1', function () {
266      const sourceMapSegmentObj = sourceMapLink.traceSegment(0, 1, 'segment_one');
267      assert.strictEqual(sourceMapSegmentObj, null);
268    });
269
270    it('segments is true && source is false', function () {
271      const sourceMapSegmentObj = sourceMapLink.traceSegment(1, 0, 'source_false');
272      assert.strictEqual(sourceMapSegmentObj, null);
273    });
274
275    it('segments is true && source is true', function () {
276      const sourceMapSegmentObj = sourceMapLink.traceSegment(2, 0, 'source_true');
277      assert.strictEqual(sourceMapSegmentObj?.column, 0);
278      assert.strictEqual(sourceMapSegmentObj?.line, 0);
279      assert.strictEqual(sourceMapSegmentObj?.name, 'source_true');
280      assert.strictEqual(sourceMapSegmentObj?.source.isOriginal, true);
281      assert.strictEqual(sourceMapSegmentObj?.source.filename, 'filename');
282      assert.strictEqual(sourceMapSegmentObj?.source.content, '// traceSegment');
283    });
284  });
285});
286