135 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			135 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() as ts.SourceFile[])
 | 
						|
                     .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';
 | 
						|
  `
 | 
						|
};
 |