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