134 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
		
		
			
		
	
	
			134 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
|  | /** | ||
|  |  * @license | ||
|  |  * Copyright Google Inc. All Rights Reserved. | ||
|  |  * | ||
|  |  * Use of this source code is governed by an MIT-style license that can be | ||
|  |  * found in the LICENSE file at https://angular.io/license
 | ||
|  |  */ | ||
|  | 
 | ||
|  | import * as ts from 'typescript'; | ||
|  | 
 | ||
|  | import {isMetadataGlobalReferenceExpression} from '../../src/metadata/schema'; | ||
|  | import {Symbols} from '../../src/metadata/symbols'; | ||
|  | 
 | ||
|  | import {Directory, Host, expectNoDiagnostics} from './typescript.mocks'; | ||
|  | 
 | ||
|  | describe('Symbols', () => { | ||
|  |   let symbols: Symbols; | ||
|  |   const someValue = 'some-value'; | ||
|  | 
 | ||
|  |   beforeEach(() => symbols = new Symbols(null as any as ts.SourceFile)); | ||
|  | 
 | ||
|  |   it('should be able to add a symbol', () => symbols.define('someSymbol', someValue)); | ||
|  | 
 | ||
|  |   beforeEach(() => symbols.define('someSymbol', someValue)); | ||
|  | 
 | ||
|  |   it('should be able to `has` a symbol', () => expect(symbols.has('someSymbol')).toBeTruthy()); | ||
|  |   it('should be able to `get` a symbol value', | ||
|  |      () => expect(symbols.resolve('someSymbol')).toBe(someValue)); | ||
|  |   it('should be able to `get` a symbol value', | ||
|  |      () => expect(symbols.resolve('someSymbol')).toBe(someValue)); | ||
|  |   it('should be able to determine symbol is missing', | ||
|  |      () => expect(symbols.has('missingSymbol')).toBeFalsy()); | ||
|  |   it('should return undefined from `get` for a missing symbol', | ||
|  |      () => expect(symbols.resolve('missingSymbol')).toBeUndefined()); | ||
|  | 
 | ||
|  |   let host: ts.LanguageServiceHost; | ||
|  |   let service: ts.LanguageService; | ||
|  |   let program: ts.Program; | ||
|  |   let expressions: ts.SourceFile; | ||
|  |   let imports: ts.SourceFile; | ||
|  | 
 | ||
|  |   beforeEach(() => { | ||
|  |     host = new Host(FILES, ['consts.ts', 'expressions.ts', 'imports.ts']); | ||
|  |     service = ts.createLanguageService(host); | ||
|  |     program = service.getProgram(); | ||
|  |     expressions = program.getSourceFile('expressions.ts'); | ||
|  |     imports = program.getSourceFile('imports.ts'); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it('should not have syntax errors in the test sources', () => { | ||
|  |     expectNoDiagnostics(service.getCompilerOptionsDiagnostics()); | ||
|  |     for (const sourceFile of program.getSourceFiles()) { | ||
|  |       expectNoDiagnostics(service.getSyntacticDiagnostics(sourceFile.fileName)); | ||
|  |     } | ||
|  |   }); | ||
|  | 
 | ||
|  |   it('should be able to find the source files', () => { | ||
|  |     expect(expressions).toBeDefined(); | ||
|  |     expect(imports).toBeDefined(); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it('should be able to create symbols for a source file', () => { | ||
|  |     const symbols = new Symbols(expressions); | ||
|  |     expect(symbols).toBeDefined(); | ||
|  |   }); | ||
|  | 
 | ||
|  | 
 | ||
|  |   it('should be able to find symbols in expression', () => { | ||
|  |     const symbols = new Symbols(expressions); | ||
|  |     expect(symbols.has('someName')).toBeTruthy(); | ||
|  |     expect(symbols.resolve('someName')) | ||
|  |         .toEqual({__symbolic: 'reference', module: './consts', name: 'someName'}); | ||
|  |     expect(symbols.has('someBool')).toBeTruthy(); | ||
|  |     expect(symbols.resolve('someBool')) | ||
|  |         .toEqual({__symbolic: 'reference', module: './consts', name: 'someBool'}); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it('should be able to detect a * import', () => { | ||
|  |     const symbols = new Symbols(imports); | ||
|  |     expect(symbols.resolve('b')).toEqual({__symbolic: 'reference', module: 'b'}); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it('should be able to detect importing a default export', () => { | ||
|  |     const symbols = new Symbols(imports); | ||
|  |     expect(symbols.resolve('d')).toEqual({__symbolic: 'reference', module: 'd', default: true}); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it('should be able to import a renamed symbol', () => { | ||
|  |     const symbols = new Symbols(imports); | ||
|  |     expect(symbols.resolve('g')).toEqual({__symbolic: 'reference', name: 'f', module: 'f'}); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it('should be able to resolve any symbol in core global scope', () => { | ||
|  |     const core = program.getSourceFiles().find(source => source.fileName.endsWith('lib.d.ts')); | ||
|  |     expect(core).toBeDefined(); | ||
|  |     const visit = (node: ts.Node): boolean => { | ||
|  |       switch (node.kind) { | ||
|  |         case ts.SyntaxKind.VariableStatement: | ||
|  |         case ts.SyntaxKind.VariableDeclarationList: | ||
|  |           return !!ts.forEachChild(node, visit); | ||
|  |         case ts.SyntaxKind.VariableDeclaration: | ||
|  |           const variableDeclaration = <ts.VariableDeclaration>node; | ||
|  |           const nameNode = <ts.Identifier>variableDeclaration.name; | ||
|  |           const name = nameNode.text; | ||
|  |           const result = symbols.resolve(name); | ||
|  |           expect(isMetadataGlobalReferenceExpression(result) && result.name).toEqual(name); | ||
|  | 
 | ||
|  |           // Ignore everything after Float64Array as it is IE specific.
 | ||
|  |           return name === 'Float64Array'; | ||
|  |       } | ||
|  |       return false; | ||
|  |     }; | ||
|  |     ts.forEachChild(core !, visit); | ||
|  |   }); | ||
|  | }); | ||
|  | 
 | ||
|  | const FILES: Directory = { | ||
|  |   'consts.ts': `
 | ||
|  |     export var someName = 'some-name'; | ||
|  |     export var someBool = true; | ||
|  |     export var one = 1; | ||
|  |     export var two = 2; | ||
|  |   `,
 | ||
|  |   'expressions.ts': `
 | ||
|  |     import {someName, someBool, one, two} from './consts'; | ||
|  |   `,
 | ||
|  |   'imports.ts': `
 | ||
|  |     import * as b from 'b'; | ||
|  |     import 'c'; | ||
|  |     import d from 'd'; | ||
|  |     import {f as g} from 'f'; | ||
|  |   `
 | ||
|  | }; |