1/* 2 * Copyright (c) 2024 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 { describe, it } from 'mocha'; 17import { assert, expect } from 'chai'; 18import * as fs from 'fs'; 19import { TypeUtils } from '../../../src/utils/TypeUtils'; 20import { 21 createLabel, 22 createScopeManager, 23 getNameWithScopeLoc, 24 isClassScope, 25 isEnumScope, 26 isFunctionScope, 27 isGlobalScope, 28 isInterfaceScope, 29 isObjectLiteralScope, 30 Scope, 31 ScopeKind, 32} from '../../../src/utils/ScopeAnalyzer'; 33import type { 34 Label, 35 ScopeManager 36} from '../../../src/utils/ScopeAnalyzer'; 37import { 38 createSourceFile, 39 factory, 40 FunctionDeclaration, 41 ParameterDeclaration, 42 ScriptTarget, 43 SyntaxKind, 44 SymbolFlags, 45} from 'typescript'; 46import type { 47 __String, 48 Declaration, 49 ExportDeclaration, 50 Identifier, 51 JSDocTagInfo, 52 LabeledStatement, 53 NamespaceExport, 54 ObjectBindingPattern, 55 SourceFile, 56 Symbol, 57 SymbolDisplayPart, 58 TypeChecker, 59 VariableStatement 60} from 'typescript'; 61 62describe('ScopeAnalyzer ut', function () { 63 let sourceFile: SourceFile; 64 65 before('init parameters', function () { 66 const sourceFileContent = ` 67 //This is a comment 68 class Demo{ 69 constructor(public title: string, public content: string, public mark: number) { 70 this.title = title 71 this.content = content 72 this.mark = mark 73 } 74 } 75 `; 76 77 sourceFile = createSourceFile('sourceFile.ts', sourceFileContent, ScriptTarget.ES2015, true); 78 }); 79 80 describe('unit test for isGlobalScope', function () { 81 it('is Global Scope', function () { 82 let curScope = new Scope('curScope', sourceFile, ScopeKind.GLOBAL); 83 assert.isTrue(isGlobalScope(curScope)); 84 }); 85 86 it('is not Global Scope', function () { 87 let curScope = new Scope('curScope', sourceFile, ScopeKind.MODULE); 88 assert.isFalse(isGlobalScope(curScope)); 89 }); 90 }); 91 92 describe('unit test for isFunctionScope', function () { 93 it('is Function Scope', function () { 94 let curScope = new Scope('curScope', sourceFile, ScopeKind.FUNCTION); 95 assert.isTrue(isFunctionScope(curScope)); 96 }); 97 98 it('is not Function Scope', function () { 99 let curScope = new Scope('curScope', sourceFile, ScopeKind.MODULE); 100 assert.isFalse(isFunctionScope(curScope)); 101 }); 102 }); 103 104 describe('unit test for isClassScope', function () { 105 it('is Class Scope', function () { 106 let curScope = new Scope('curScope', sourceFile, ScopeKind.CLASS); 107 assert.isTrue(isClassScope(curScope)); 108 }); 109 110 it('is not Class Scope', function () { 111 let curScope = new Scope('curScope', sourceFile, ScopeKind.MODULE); 112 assert.isFalse(isClassScope(curScope)); 113 }); 114 }); 115 116 describe('unit test for isInterfaceScope', function () { 117 it('is Interface Scope', function () { 118 let curScope = new Scope('curScope', sourceFile, ScopeKind.INTERFACE); 119 assert.isTrue(isInterfaceScope(curScope)); 120 }); 121 122 it('is not Interface Scope', function () { 123 let curScope = new Scope('curScope', sourceFile, ScopeKind.MODULE); 124 assert.isFalse(isInterfaceScope(curScope)); 125 }); 126 }); 127 128 describe('unit test for isEnumScope', function () { 129 it('is Enum Scope', function () { 130 let curScope = new Scope('curScope', sourceFile, ScopeKind.ENUM); 131 assert.isTrue(isEnumScope(curScope)); 132 }); 133 134 it('is not Enum Scope', function () { 135 let curScope = new Scope('curScope', sourceFile, ScopeKind.MODULE); 136 assert.isFalse(isEnumScope(curScope)); 137 }); 138 }); 139 140 describe('unit test for isObjectLiteralScope', function () { 141 it('is ObjectLiteral Scope', function () { 142 let curScope = new Scope('curScope', sourceFile, ScopeKind.OBJECT_LITERAL); 143 assert.isTrue(isObjectLiteralScope(curScope)); 144 }); 145 146 it('is not ObjectLiteral Scope', function () { 147 let curScope = new Scope('curScope', sourceFile, ScopeKind.MODULE); 148 assert.isFalse(isObjectLiteralScope(curScope)); 149 }); 150 }); 151 152 describe('unit test for Scope', function () { 153 describe('constructor', function () { 154 it('init', function () { 155 let curScope = new Scope('curScope', sourceFile, ScopeKind.GLOBAL); 156 assert.equal(curScope.name, 'curScope'); 157 assert.equal(curScope.kind, ScopeKind.GLOBAL); 158 assert.equal(curScope.block, sourceFile); 159 assert.equal(curScope.parent, undefined); 160 assert.deepEqual(curScope.children, []); 161 assert.deepEqual(curScope.defs, new Set()); 162 assert.deepEqual(curScope.labels, []); 163 assert.deepEqual(curScope.importNames, new Set()); 164 assert.deepEqual(curScope.exportNames, new Set()); 165 assert.deepEqual(curScope.mangledNames, new Set()); 166 assert.equal(curScope.loc, 'curScope'); 167 }); 168 }); 169 170 describe('addChild', function () { 171 it('push', function () { 172 let parentScope = new Scope('parentScope', sourceFile, ScopeKind.FUNCTION); 173 let childScope = new Scope('childScope', sourceFile, ScopeKind.BLOCK, false, parentScope); 174 assert.include(parentScope.children, childScope); 175 }); 176 }); 177 178 describe('addDefinition', function () { 179 it('should add definition symbol to the current scope', function () { 180 let curScope = new Scope('curScope', sourceFile, ScopeKind.GLOBAL); 181 let symbol: Symbol = Object(Symbol('testSymbol')); 182 183 curScope.addDefinition(symbol, false); 184 assert.isTrue(curScope.defs.has(symbol)); 185 }); 186 187 it('should mark symbol as obfuscateAsProperty if required', function () { 188 let curScope = new Scope('curScope', sourceFile, ScopeKind.GLOBAL); 189 let symbol: Symbol = Object(Symbol('testSymbol')); 190 191 curScope.addDefinition(symbol, true); 192 assert.isTrue(Reflect.get(symbol, 'obfuscateAsProperty')); 193 }); 194 }); 195 196 describe('addLabel', function () { 197 it('should add a label to the current scope', function () { 198 let curScope = new Scope('curScope', sourceFile, ScopeKind.GLOBAL); 199 let label: Label = { name: 'testLabel', locInfo: 'locInfo', refs: [], parent: undefined, children: [], scope: curScope }; 200 201 curScope.addLabel(label); 202 assert.include(curScope.labels, label); 203 }); 204 }); 205 206 describe('getSymbolLocation', function () { 207 it('should return the correct location if symbol exists', function () { 208 let curScope = new Scope('curScope', sourceFile, ScopeKind.GLOBAL); 209 let symbol: Symbol = { 210 flags: 0, 211 escapedName: 'testSymbol' as __String, 212 declarations: [], 213 valueDeclaration: undefined, 214 members: undefined, 215 exports: undefined, 216 globalExports: undefined, 217 exportSymbol: undefined, 218 name: 'testSymbol', 219 getFlags: function (): SymbolFlags { 220 throw new Error('Function not implemented.'); 221 }, 222 getEscapedName: function (): __String { 223 throw new Error('Function not implemented.'); 224 }, 225 getName: function (): string { 226 throw new Error('Function not implemented.'); 227 }, 228 getDeclarations: function (): Declaration[] | undefined { 229 throw new Error('Function not implemented.'); 230 }, 231 getDocumentationComment: function (typeChecker: TypeChecker | undefined): SymbolDisplayPart[] { 232 throw new Error('Function not implemented.'); 233 }, 234 getJsDocTags: function (checker?: TypeChecker): JSDocTagInfo[] { 235 throw new Error('Function not implemented.'); 236 } 237 }; 238 curScope.addDefinition(symbol); 239 assert.equal(curScope.getSymbolLocation(symbol), 'testSymbol'); 240 }); 241 242 it('should return an empty string if symbol does not exist', function () { 243 let curScope = new Scope('curScope', sourceFile, ScopeKind.GLOBAL); 244 let symbol: Symbol = Object(Symbol('nonExistentSymbol')); 245 246 assert.equal(curScope.getSymbolLocation(symbol), ''); 247 }); 248 }); 249 250 describe('getLabelLocation', function () { 251 it('should return the correct location if label exists', function () { 252 let curScope = new Scope('curScope', sourceFile, ScopeKind.GLOBAL); 253 let label: Label = { name: 'testLabel', locInfo: 'locInfo', refs: [], parent: undefined, children: [], scope: curScope }; 254 255 curScope.addLabel(label); 256 assert.equal(curScope.getLabelLocation(label), 'testLabel'); 257 }); 258 259 it('should return an empty string if label does not exist', function () { 260 let curScope = new Scope('curScope', sourceFile, ScopeKind.GLOBAL); 261 let label: Label = { name: 'nonExistentLabel', locInfo: 'locInfo', refs: [], parent: undefined, children: [], scope: curScope }; 262 263 assert.equal(curScope.getLabelLocation(label), ''); 264 }); 265 }); 266 }); 267 268 describe('unit test for createLabel', function () { 269 const sourceFileContent = ` 270 label: console.log('This is a labeled statement'); 271 `; 272 sourceFile = createSourceFile('test.ts', sourceFileContent, ScriptTarget.ES2015, true); 273 let labeledStatement: LabeledStatement = sourceFile.statements[0] as LabeledStatement; 274 let curScope = new Scope('curScope', sourceFile, ScopeKind.GLOBAL); 275 276 it('should create a label and add it to the scope', function () { 277 const label = createLabel(labeledStatement, curScope); 278 279 assert.equal(label.name, labeledStatement.label.text); 280 assert.equal(label.locInfo, `$0_${labeledStatement.label.text}`); 281 assert.deepEqual(label.refs, [labeledStatement.label]); 282 assert.equal(label.parent, undefined); 283 assert.deepEqual(label.children, []); 284 assert.equal(label.scope, curScope); 285 assert.include(curScope.labels, label); 286 }); 287 288 it('should create a label with a parent and add it to the parent\'s children', function () { 289 const parentLabel: Label = { 290 name: 'parentLabel', 291 locInfo: 'parentLocInfo', 292 refs: [], 293 parent: undefined, 294 children: [], 295 scope: curScope 296 }; 297 const label = createLabel(labeledStatement, curScope, parentLabel); 298 299 assert.equal(label.name, labeledStatement.label.text); 300 assert.equal(label.locInfo, `$1_${labeledStatement.label.text}`); 301 assert.deepEqual(label.refs, [labeledStatement.label]); 302 assert.equal(label.parent, parentLabel); 303 assert.deepEqual(label.children, []); 304 assert.equal(label.scope, curScope); 305 assert.include(curScope.labels, label); 306 assert.include(parentLabel.children, label); 307 }); 308 }); 309 310 describe('unit test for createScopeManager', function () { 311 let scopeManager: ScopeManager; 312 let sourceFile: SourceFile; 313 let checker: TypeChecker; 314 315 function InitScopeManager(newFilePath: string): void { 316 const fileContent = fs.readFileSync(newFilePath, 'utf8'); 317 sourceFile = createSourceFile(newFilePath, fileContent, ScriptTarget.ES2015, true); 318 checker = TypeUtils.createChecker(sourceFile); 319 scopeManager = createScopeManager(); 320 scopeManager.analyze(sourceFile, checker, false); 321 } 322 323 describe('analyze', function () { 324 describe('analyzeModule', function () { 325 let filePath = 'test/ut/utils/ScopeAnalyzer/analyzeModule.ts'; 326 InitScopeManager(filePath); 327 it('getReservedNames', function () { 328 const reservedNames = scopeManager.getReservedNames(); 329 assert.strictEqual(reservedNames.size === 0, true); 330 }); 331 it('getRootScope', function () { 332 const rootScope = scopeManager.getRootScope(); 333 assert.strictEqual(rootScope.fileExportNames?.size !== 0, true); 334 }); 335 describe('getScopeOfNode', function () { 336 it('node is not identifier', function () { 337 const node: SourceFile = sourceFile; 338 const scope = scopeManager.getScopeOfNode(node); 339 assert.strictEqual(scope, undefined); 340 }); 341 342 it('node with no symbol', function () { 343 const node: Identifier = factory.createIdentifier('noSymbolIdentifier'); 344 const scope = scopeManager.getScopeOfNode(node); 345 assert.strictEqual(scope, undefined); 346 }); 347 348 it('get the scope of node', function () { 349 const classDeclaration = sourceFile.statements.find(node => node && node.kind === SyntaxKind.ClassDeclaration); 350 if (!classDeclaration) throw new Error('ClassDeclaration not found'); 351 const node: Identifier = (classDeclaration as any)?.name; 352 const scope = scopeManager.getScopeOfNode(node); 353 assert.strictEqual(scope?.name, ''); 354 }); 355 356 it('get no scope of node', function () { 357 const anotherSourceFile = createSourceFile('another.ts', 'let y = 2;', ScriptTarget.ES2015, true); 358 const node = (anotherSourceFile.statements[0] as VariableStatement).declarationList.declarations[0].name; 359 const scope = scopeManager.getScopeOfNode(node); 360 assert.strictEqual(scope, undefined); 361 }); 362 }); 363 }); 364 365 describe('analyzeFunctionLike', function () { 366 let filePath = 'test/ut/utils/ScopeAnalyzer/analyzeFunctionLike.ts'; 367 InitScopeManager(filePath); 368 it('getReservedNames', function () { 369 const reservedNames = scopeManager.getReservedNames(); 370 assert.strictEqual(reservedNames.size === 0, true); 371 }); 372 it('getRootScope', function () { 373 const rootScope = scopeManager.getRootScope(); 374 assert.strictEqual(rootScope.fileExportNames?.size !== 0, true); 375 }); 376 describe('getScopeOfNode', function () { 377 it('node is not identifier', function () { 378 const node: SourceFile = sourceFile; 379 const scope = scopeManager.getScopeOfNode(node); 380 assert.strictEqual(scope, undefined); 381 }); 382 383 it('node with no symbol', function () { 384 const node: Identifier = factory.createIdentifier('noSymbolIdentifier'); 385 const scope = scopeManager.getScopeOfNode(node); 386 assert.strictEqual(scope, undefined); 387 }); 388 389 it('get the scope of node', function () { 390 const functionDeclaration = sourceFile.statements.find(node => node && node.kind === SyntaxKind.FunctionDeclaration); 391 if (!functionDeclaration) throw new Error('FunctionDeclaration not found'); 392 const node: Identifier = (functionDeclaration as any)?.name; 393 const scope = scopeManager.getScopeOfNode(node); 394 assert.strictEqual(scope?.name, ''); 395 }); 396 397 it('get no scope of node', function () { 398 const anotherSourceFile = createSourceFile('another.ts', 'let y = 2;', ScriptTarget.ES2015, true); 399 const node = (anotherSourceFile.statements[0] as VariableStatement).declarationList.declarations[0].name; 400 const scope = scopeManager.getScopeOfNode(node); 401 assert.strictEqual(scope, undefined); 402 }); 403 }); 404 }); 405 406 describe('analyzeExportNames', function () { 407 let filePath = 'test/ut/utils/ScopeAnalyzer/analyzeExportNames.ts'; 408 InitScopeManager(filePath); 409 it('getReservedNames', function () { 410 const reservedNames = scopeManager.getReservedNames(); 411 assert.strictEqual(reservedNames.size === 0, true); 412 }); 413 it('getRootScope', function () { 414 const rootScope = scopeManager.getRootScope(); 415 assert.strictEqual(rootScope.fileExportNames?.size !== 0, true); 416 }); 417 describe('getScopeOfNode', function () { 418 it('node is not identifier', function () { 419 const node: SourceFile = sourceFile; 420 const scope = scopeManager.getScopeOfNode(node); 421 assert.strictEqual(scope, undefined); 422 }); 423 424 it('node with no symbol', function () { 425 const node: Identifier = factory.createIdentifier('noSymbolIdentifier'); 426 const scope = scopeManager.getScopeOfNode(node); 427 assert.strictEqual(scope, undefined); 428 }); 429 430 it('get the scope of node', function () { 431 const exportDeclaration = sourceFile.statements.find(node => node && node.kind === SyntaxKind.ExportDeclaration); 432 if (!exportDeclaration) throw new Error('ExportDeclaration not found'); 433 434 const exportClause = (exportDeclaration as any).exportClause; 435 if (!exportClause || !exportClause.elements) throw new Error('ExportSpecifier not found'); 436 437 const exportSpecifier = exportClause.elements[0]; 438 const node: Identifier = exportSpecifier.name; 439 440 const scope = scopeManager.getScopeOfNode(node); 441 assert.strictEqual(scope?.name, undefined); 442 }); 443 444 it('get no scope of node', function () { 445 const anotherSourceFile = createSourceFile('another.ts', 'let y = 2;', ScriptTarget.ES2015, true); 446 const node = (anotherSourceFile.statements[0] as VariableStatement).declarationList.declarations[0].name; 447 const scope = scopeManager.getScopeOfNode(node); 448 assert.strictEqual(scope, undefined); 449 }); 450 }); 451 }); 452 453 describe('getScopeOfNode', () => { 454 let filePath = 'test/ut/utils/ScopeAnalyzer/analyzeFunctionType.ts'; 455 const fileContent = fs.readFileSync(filePath, 'utf8'); 456 let sourceFile = createSourceFile(filePath, fileContent, ScriptTarget.ES2015, true); 457 let checker = TypeUtils.createChecker(sourceFile); 458 let scopeManager = createScopeManager(); 459 scopeManager.analyze(sourceFile, checker, false); 460 461 const functionDeclaration = sourceFile.statements[0] as FunctionDeclaration; 462 const parameter = functionDeclaration.parameters[0] as ParameterDeclaration; 463 const node = parameter.name as Identifier; 464 465 const scope = scopeManager.getScopeOfNode(node); 466 assert.strictEqual(scope.defs.size, 1, 'Scope should have exactly 1 definition'); 467 }); 468 469 describe('analyzeImportEqualsDeclaration', function () { 470 let filePath = 'test/ut/utils/ScopeAnalyzer/analyzeImportEqualsDeclaration.ts'; 471 const fileContent = fs.readFileSync(filePath, 'utf8'); 472 let sourceFile = createSourceFile(filePath, fileContent, ScriptTarget.ES2015, true); 473 let checker = TypeUtils.createChecker(sourceFile); 474 let scopeManager = createScopeManager(); 475 scopeManager.analyze(sourceFile, checker, false); 476 it('getReservedNames', function () { 477 const reservedNames = scopeManager.getReservedNames(); 478 assert.strictEqual(reservedNames.size === 0, true); 479 }); 480 it('getRootScope', function () { 481 const rootScope = scopeManager.getRootScope(); 482 assert.strictEqual(rootScope.fileExportNames?.size === 2, true); 483 assert.strictEqual(rootScope.fileExportNames?.has("a"), true); 484 assert.strictEqual(rootScope.fileExportNames?.has("b"), true); 485 }); 486 describe('getScopeOfNode', function () { 487 it('node is not identifier', function () { 488 const node: SourceFile = sourceFile; 489 const scope = scopeManager.getScopeOfNode(node); 490 assert.strictEqual(scope, undefined); 491 }); 492 493 it('node with no symbol', function () { 494 const node: Identifier = factory.createIdentifier('noSymbolIdentifier'); 495 const scope = scopeManager.getScopeOfNode(node); 496 assert.strictEqual(scope, undefined); 497 }); 498 499 it('get the scope of node', function () { 500 const moduleDeclaration = sourceFile.statements.find(node => node && node.kind === SyntaxKind.ModuleDeclaration); 501 if (!moduleDeclaration) throw new Error('ModuleDeclaration not found'); 502 const moduleBlock = (moduleDeclaration as any).body; 503 if (!moduleBlock) throw new Error('ModuleBlock not found'); 504 const importEqualsDeclaration = moduleBlock.statements.find(node => node && node.kind === SyntaxKind.ImportEqualsDeclaration); 505 if (!importEqualsDeclaration) throw new Error('ImportEqualsDeclaration not found'); 506 const node: Identifier = (importEqualsDeclaration as any)?.name; 507 const scope = scopeManager.getScopeOfNode(node); 508 assert.strictEqual(scope?.name, 'ns111'); 509 }); 510 511 it('get no scope of node', function () { 512 const anotherSourceFile = createSourceFile('another.ts', 'let y = 2;', ScriptTarget.ES2015, true); 513 const node = (anotherSourceFile.statements[0] as VariableStatement).declarationList.declarations[0].name; 514 const scope = scopeManager.getScopeOfNode(node); 515 assert.strictEqual(scope, undefined); 516 }); 517 }); 518 }); 519 520 describe('analyzeClassLike', function () { 521 let filePath = 'test/ut/utils/ScopeAnalyzer/analyzeClassLike.ts'; 522 it('should collect symbol of class property parameters', function () { 523 InitScopeManager(filePath); 524 const rootScope = scopeManager.getRootScope(); 525 const classScope = rootScope.children[0]; 526 const nameSet = new Set(); 527 classScope.defs.forEach((symbol) => { 528 nameSet.add(symbol.escapedName); 529 }) 530 expect(nameSet.has('prop1')).to.be.true; 531 expect(nameSet.has('__constructor')).to.be.true; 532 expect(nameSet.has('para1')).to.be.true; 533 expect(nameSet.has('para2')).to.be.true; 534 expect(nameSet.has('para3')).to.be.true; 535 expect(nameSet.has('para4')).to.be.true; 536 }); 537 }); 538 539 describe('analyzeObjectBindingPatternRequire', function () { 540 let filePath = 'test/ut/utils/ScopeAnalyzer/analyzeObjectBindingPatternRequire.ts'; 541 const fileContent = fs.readFileSync(filePath, 'utf8'); 542 let sourceFile = createSourceFile(filePath, fileContent, ScriptTarget.ES2015, true); 543 let checker = TypeUtils.createChecker(sourceFile); 544 let scopeManager = createScopeManager(); 545 scopeManager.analyze(sourceFile, checker, false); 546 547 it('getReservedNames', function () { 548 const reservedNames = scopeManager.getReservedNames(); 549 assert.strictEqual(reservedNames.size === 0, true); 550 }); 551 it('getRootScope', function () { 552 const rootScope = scopeManager.getRootScope(); 553 assert.strictEqual(rootScope.parent, undefined); 554 assert.strictEqual(rootScope.fileImportNames?.size, 2); 555 assert.deepEqual(rootScope.fileImportNames, new Set(["x", "z"])); 556 assert.strictEqual(rootScope.importNames.size, 2); 557 assert.deepEqual(rootScope.fileImportNames, new Set(["x", "z"])); 558 }); 559 describe('getScopeOfNode', function () { 560 it('node is not identifier', function () { 561 const node: SourceFile = sourceFile; 562 const scope = scopeManager.getScopeOfNode(node); 563 assert.strictEqual(scope, undefined); 564 }); 565 566 it('get the scope of node', function () { 567 const objectBindingPattern = (sourceFile.statements[1] as VariableStatement).declarationList.declarations[0].name; 568 assert.strictEqual(objectBindingPattern.kind, SyntaxKind.ObjectBindingPattern); 569 const node = (objectBindingPattern as ObjectBindingPattern).elements[0].name; 570 const scope = scopeManager.getScopeOfNode(node); 571 assert.notStrictEqual(scope, undefined); 572 }); 573 }); 574 }); 575 576 describe('analyzeNamespaceExport', function () { 577 let filePath = 'test/ut/utils/ScopeAnalyzer/analyzeNamespaceExport.ts'; 578 const fileContent = fs.readFileSync(filePath, 'utf8'); 579 let sourceFile = createSourceFile(filePath, fileContent, ScriptTarget.ES2015, true); 580 let checker = TypeUtils.createChecker(sourceFile); 581 let scopeManager = createScopeManager(); 582 583 it('exportObfuscation is false', function () { 584 scopeManager.analyze(sourceFile, checker, false); 585 const rootScope = scopeManager.getRootScope(); 586 assert.deepEqual(rootScope.defs.size, 0); 587 }); 588 589 it('exportObfuscation is true', function () { 590 scopeManager.analyze(sourceFile, checker, true); 591 const rootScope = scopeManager.getRootScope(); 592 assert.strictEqual(rootScope.defs.size, 1); 593 }); 594 595 it('getReservedNames', function () { 596 const reservedNames = scopeManager.getReservedNames(); 597 assert.strictEqual(reservedNames.size === 0, true); 598 }); 599 it('getRootScope', function () { 600 const rootScope = scopeManager.getRootScope(); 601 assert.strictEqual(rootScope.parent, undefined); 602 }); 603 describe('getScopeOfNode', function () { 604 it('node is not identifier', function () { 605 const node: SourceFile = sourceFile; 606 const scope = scopeManager.getScopeOfNode(node); 607 assert.strictEqual(scope, undefined); 608 }); 609 610 it('get the scope of node', function () { 611 const namespaceExport = (sourceFile.statements[0] as ExportDeclaration).exportClause; 612 assert.strictEqual((namespaceExport as NamespaceExport).kind, SyntaxKind.NamespaceExport); 613 const node = (namespaceExport as NamespaceExport).name; 614 const scope = scopeManager.getScopeOfNode(node); 615 assert.notStrictEqual(scope, undefined); 616 }); 617 }); 618 }); 619 620 describe('analyzeBreakOrContinue', function () { 621 let filePath = 'test/ut/utils/ScopeAnalyzer/analyzeBreakOrContinue.ts'; 622 const fileContent = fs.readFileSync(filePath, 'utf8'); 623 let sourceFile = createSourceFile(filePath, fileContent, ScriptTarget.ES2015, true); 624 let checker = TypeUtils.createChecker(sourceFile); 625 let scopeManager = createScopeManager(); 626 scopeManager.analyze(sourceFile, checker, false); 627 628 it('getReservedNames', function () { 629 const reservedNames = scopeManager.getReservedNames(); 630 assert.strictEqual(reservedNames.size === 0, true); 631 }); 632 it('getRootScope', function () { 633 const rootScope = scopeManager.getRootScope(); 634 assert.strictEqual(rootScope.parent, undefined); 635 assert.strictEqual(rootScope.defs.size, 2); 636 }); 637 describe('getScopeOfNode', function () { 638 it('node is not identifier', function () { 639 const node: SourceFile = sourceFile; 640 const scope = scopeManager.getScopeOfNode(node); 641 assert.strictEqual(scope, undefined); 642 }); 643 644 it('get the scope of node', function () { 645 const continueStatement = (sourceFile.statements[0] as any) 646 .body 647 .statements[0] 648 .statement 649 .statement 650 .statements[0] 651 .statement 652 .statement 653 .statements[0] 654 .thenStatement 655 .statements[0]; 656 assert.strictEqual(continueStatement.kind, SyntaxKind.ContinueStatement); 657 const labelStatement = (sourceFile.statements[0] as any).body.statements[0]; 658 assert.strictEqual(labelStatement.kind, SyntaxKind.LabeledStatement); 659 }); 660 }); 661 }); 662 663 describe('analyzeCatchClause', function () { 664 let filePath = 'test/ut/utils/ScopeAnalyzer/analyzeCatchClause.ts'; 665 const fileContent = fs.readFileSync(filePath, 'utf8'); 666 let sourceFile = createSourceFile(filePath, fileContent, ScriptTarget.ES2015, true); 667 let checker = TypeUtils.createChecker(sourceFile); 668 let scopeManager = createScopeManager(); 669 scopeManager.analyze(sourceFile, checker, false); 670 671 it('getReservedNames', function () { 672 const reservedNames = scopeManager.getReservedNames(); 673 assert.strictEqual(reservedNames.size === 0, true); 674 }); 675 it('getRootScope', function () { 676 const rootScope = scopeManager.getRootScope(); 677 assert.strictEqual(rootScope.parent, undefined); 678 assert.strictEqual(rootScope.defs.size, 1); 679 }); 680 describe('getScopeOfNode', function () { 681 it('node is not identifier', function () { 682 const node: SourceFile = sourceFile; 683 const scope = scopeManager.getScopeOfNode(node); 684 assert.strictEqual(scope, undefined); 685 }); 686 687 it('get the scope of node', function () { 688 const catchClause = (sourceFile.statements[0] as any).body.statements[0].catchClause; 689 assert.strictEqual(catchClause.kind, SyntaxKind.CatchClause); 690 const node = catchClause.variableDeclaration.name; 691 const scope = scopeManager.getScopeOfNode(node); 692 assert.notStrictEqual(scope, undefined); 693 assert.strictEqual(scope?.name, '$1'); 694 }); 695 }); 696 }); 697 698 describe('analyzeEnum', function () { 699 let filePath = 'test/ut/utils/ScopeAnalyzer/analyzeEnum.ts'; 700 const fileContent = fs.readFileSync(filePath, 'utf8'); 701 let sourceFile = createSourceFile(filePath, fileContent, ScriptTarget.ES2015, true); 702 let checker = TypeUtils.createChecker(sourceFile); 703 let scopeManager = createScopeManager(); 704 scopeManager.analyze(sourceFile, checker, false); 705 706 it('getReservedNames', function () { 707 const reservedNames = scopeManager.getReservedNames(); 708 assert.strictEqual(reservedNames.size === 0, true); 709 }); 710 it('getRootScope', function () { 711 const rootScope = scopeManager.getRootScope(); 712 assert.strictEqual(rootScope.parent, undefined); 713 assert.strictEqual(rootScope.defs.size, 1); 714 }); 715 describe('getScopeOfNode', function () { 716 it('node is not identifier', function () { 717 const node: SourceFile = sourceFile; 718 const scope = scopeManager.getScopeOfNode(node); 719 assert.strictEqual(scope, undefined); 720 }); 721 722 it('get the scope of node', function () { 723 const enumDeclaration = sourceFile.statements[0]; 724 assert.strictEqual(enumDeclaration.kind, SyntaxKind.EnumDeclaration); 725 const node = (enumDeclaration as any).name; 726 const scope = scopeManager.getScopeOfNode(node); 727 assert.notStrictEqual(scope, undefined); 728 assert.strictEqual(scope?.name, ''); 729 }); 730 }); 731 }); 732 }); 733 }); 734 735 describe('unit test for getNameWithScopeLoc', function () { 736 const sourceFileContent: string = ` 737 let a = 1; 738 `; 739 const sourceFile: SourceFile = createSourceFile('test.ts', sourceFileContent, ScriptTarget.ES2015, true); 740 let scope: Scope = new Scope("testScope", sourceFile, ScopeKind.GLOBAL); 741 const scopeName: string = getNameWithScopeLoc(scope, "testName"); 742 assert.equal(scopeName, "testScope#testName"); 743 }); 744}); 745