• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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