330 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			330 lines
		
	
	
		
			12 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 {beforeEach, ddescribe, describe, expect, iit, it} from '@angular/core/testing/testing_internal';
 | 
						|
import * as ts from 'typescript';
 | 
						|
 | 
						|
import {ReflectorHost} from '../src/reflector_host';
 | 
						|
 | 
						|
import {Directory, Entry, MockCompilerHost, MockContext} from './mocks';
 | 
						|
 | 
						|
describe('reflector_host', () => {
 | 
						|
  var context: MockContext;
 | 
						|
  var host: ts.CompilerHost;
 | 
						|
  var program: ts.Program;
 | 
						|
  var reflectorNestedGenDir: ReflectorHost;
 | 
						|
  var reflectorSiblingGenDir: ReflectorHost;
 | 
						|
 | 
						|
  beforeEach(() => {
 | 
						|
    context = new MockContext('/tmp/src', clone(FILES));
 | 
						|
    host = new MockCompilerHost(context);
 | 
						|
    program = ts.createProgram(
 | 
						|
        ['main.ts'], {
 | 
						|
          module: ts.ModuleKind.CommonJS,
 | 
						|
        },
 | 
						|
        host);
 | 
						|
    // Force a typecheck
 | 
						|
    let errors = program.getSemanticDiagnostics();
 | 
						|
    if (errors && errors.length) {
 | 
						|
      throw new Error('Expected no errors');
 | 
						|
    }
 | 
						|
    reflectorNestedGenDir = new ReflectorHost(
 | 
						|
        program, host, {
 | 
						|
          genDir: '/tmp/project/src/gen/',
 | 
						|
          basePath: '/tmp/project/src',
 | 
						|
          skipMetadataEmit: false,
 | 
						|
          strictMetadataEmit: false,
 | 
						|
          skipTemplateCodegen: false,
 | 
						|
          trace: false
 | 
						|
        },
 | 
						|
        context);
 | 
						|
    reflectorSiblingGenDir = new ReflectorHost(
 | 
						|
        program, host, {
 | 
						|
          genDir: '/tmp/project/gen',
 | 
						|
          basePath: '/tmp/project/src/',
 | 
						|
          skipMetadataEmit: false,
 | 
						|
          strictMetadataEmit: false,
 | 
						|
          skipTemplateCodegen: false,
 | 
						|
          trace: false
 | 
						|
        },
 | 
						|
        context);
 | 
						|
  });
 | 
						|
 | 
						|
  describe('nestedGenDir', () => {
 | 
						|
    it('should import node_module from factory', () => {
 | 
						|
      expect(reflectorNestedGenDir.getImportPath(
 | 
						|
                 '/tmp/project/src/gen/my.ngfactory.ts',
 | 
						|
                 '/tmp/project/node_modules/@angular/core.d.ts'))
 | 
						|
          .toEqual('@angular/core');
 | 
						|
    });
 | 
						|
 | 
						|
    it('should import factory from factory', () => {
 | 
						|
      expect(reflectorNestedGenDir.getImportPath(
 | 
						|
                 '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ngfactory.ts'))
 | 
						|
          .toEqual('./my.other.ngfactory');
 | 
						|
      expect(reflectorNestedGenDir.getImportPath(
 | 
						|
                 '/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.css.ts'))
 | 
						|
          .toEqual('../my.other.css');
 | 
						|
      expect(reflectorNestedGenDir.getImportPath(
 | 
						|
                 '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.css.shim.ts'))
 | 
						|
          .toEqual('./a/my.other.css.shim');
 | 
						|
    });
 | 
						|
 | 
						|
    it('should import application from factory', () => {
 | 
						|
      expect(reflectorNestedGenDir.getImportPath(
 | 
						|
                 '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ts'))
 | 
						|
          .toEqual('../my.other');
 | 
						|
      expect(reflectorNestedGenDir.getImportPath(
 | 
						|
                 '/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.ts'))
 | 
						|
          .toEqual('../../my.other');
 | 
						|
      expect(reflectorNestedGenDir.getImportPath(
 | 
						|
                 '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.ts'))
 | 
						|
          .toEqual('../a/my.other');
 | 
						|
    });
 | 
						|
  });
 | 
						|
 | 
						|
  describe('nestedGenDir', () => {
 | 
						|
    it('should import node_module from factory', () => {
 | 
						|
      expect(reflectorSiblingGenDir.getImportPath(
 | 
						|
                 '/tmp/project/src/gen/my.ngfactory.ts',
 | 
						|
                 '/tmp/project/node_modules/@angular/core.d.ts'))
 | 
						|
          .toEqual('@angular/core');
 | 
						|
    });
 | 
						|
 | 
						|
    it('should import factory from factory', () => {
 | 
						|
      expect(reflectorSiblingGenDir.getImportPath(
 | 
						|
                 '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ngfactory.ts'))
 | 
						|
          .toEqual('./my.other.ngfactory');
 | 
						|
      expect(reflectorSiblingGenDir.getImportPath(
 | 
						|
                 '/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.css.ts'))
 | 
						|
          .toEqual('../my.other.css');
 | 
						|
      expect(reflectorSiblingGenDir.getImportPath(
 | 
						|
                 '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.css.shim.ts'))
 | 
						|
          .toEqual('./a/my.other.css.shim');
 | 
						|
    });
 | 
						|
 | 
						|
    it('should import application from factory', () => {
 | 
						|
      expect(reflectorSiblingGenDir.getImportPath(
 | 
						|
                 '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ts'))
 | 
						|
          .toEqual('./my.other');
 | 
						|
      expect(reflectorSiblingGenDir.getImportPath(
 | 
						|
                 '/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.ts'))
 | 
						|
          .toEqual('../my.other');
 | 
						|
      expect(reflectorSiblingGenDir.getImportPath(
 | 
						|
                 '/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.ts'))
 | 
						|
          .toEqual('./a/my.other');
 | 
						|
    });
 | 
						|
  });
 | 
						|
 | 
						|
  it('should provide the import locations for angular', () => {
 | 
						|
    let {coreDecorators, diDecorators, diMetadata, animationMetadata, provider} =
 | 
						|
        reflectorNestedGenDir.angularImportLocations();
 | 
						|
    expect(coreDecorators).toEqual('@angular/core/src/metadata');
 | 
						|
    expect(diDecorators).toEqual('@angular/core/src/di/metadata');
 | 
						|
    expect(diMetadata).toEqual('@angular/core/src/di/metadata');
 | 
						|
    expect(animationMetadata).toEqual('@angular/core/src/animation/metadata');
 | 
						|
    expect(provider).toEqual('@angular/core/src/di/provider');
 | 
						|
  });
 | 
						|
 | 
						|
  it('should be able to produce an import from main @angular/core', () => {
 | 
						|
    expect(reflectorNestedGenDir.getImportPath(
 | 
						|
               '/tmp/project/src/main.ts', '/tmp/project/node_modules/@angular/core.d.ts'))
 | 
						|
        .toEqual('@angular/core');
 | 
						|
  });
 | 
						|
 | 
						|
  it('should be able to produce an import from main to a sub-directory', () => {
 | 
						|
    expect(reflectorNestedGenDir.getImportPath('main.ts', 'lib/utils.ts')).toEqual('./lib/utils');
 | 
						|
  });
 | 
						|
 | 
						|
  it('should be able to produce an import from to a peer file', () => {
 | 
						|
    expect(reflectorNestedGenDir.getImportPath('lib/utils.ts', 'lib/collections.ts'))
 | 
						|
        .toEqual('./collections');
 | 
						|
  });
 | 
						|
 | 
						|
  it('should be able to produce an import from to a sibling directory', () => {
 | 
						|
    expect(reflectorNestedGenDir.getImportPath('lib2/utils2.ts', 'lib/utils.ts'))
 | 
						|
        .toEqual('../lib/utils');
 | 
						|
  });
 | 
						|
 | 
						|
  it('should be able to produce a symbol for an exported symbol', () => {
 | 
						|
    expect(reflectorNestedGenDir.findDeclaration('@angular/router', 'foo', 'main.ts'))
 | 
						|
        .toBeDefined();
 | 
						|
  });
 | 
						|
 | 
						|
  it('should be able to produce a symbol for values space only reference', () => {
 | 
						|
    expect(reflectorNestedGenDir.findDeclaration('@angular/router/src/providers', 'foo', 'main.ts'))
 | 
						|
        .toBeDefined();
 | 
						|
  });
 | 
						|
 | 
						|
 | 
						|
  it('should be produce the same symbol if asked twice', () => {
 | 
						|
    let foo1 = reflectorNestedGenDir.getStaticSymbol('main.ts', 'foo');
 | 
						|
    let foo2 = reflectorNestedGenDir.getStaticSymbol('main.ts', 'foo');
 | 
						|
    expect(foo1).toBe(foo2);
 | 
						|
  });
 | 
						|
 | 
						|
  it('should be able to produce a symbol for a module with no file', () => {
 | 
						|
    expect(reflectorNestedGenDir.getStaticSymbol('angularjs', 'SomeAngularSymbol')).toBeDefined();
 | 
						|
  });
 | 
						|
 | 
						|
  it('should be able to read a metadata file', () => {
 | 
						|
    expect(reflectorNestedGenDir.getMetadataFor('node_modules/@angular/core.d.ts'))
 | 
						|
        .toEqual({__symbolic: 'module', version: 1, metadata: {foo: {__symbolic: 'class'}}});
 | 
						|
  });
 | 
						|
 | 
						|
  it('should be able to read metadata from an otherwise unused .d.ts file ', () => {
 | 
						|
    expect(reflectorNestedGenDir.getMetadataFor('node_modules/@angular/unused.d.ts'))
 | 
						|
        .toBeUndefined();
 | 
						|
  });
 | 
						|
 | 
						|
  it('should be able to read empty metadata ', () => {
 | 
						|
    expect(reflectorNestedGenDir.getMetadataFor('node_modules/@angular/empty.d.ts'))
 | 
						|
        .toBeUndefined();
 | 
						|
  });
 | 
						|
 | 
						|
  it('should return undefined for missing modules', () => {
 | 
						|
    expect(reflectorNestedGenDir.getMetadataFor('node_modules/@angular/missing.d.ts'))
 | 
						|
        .toBeUndefined();
 | 
						|
  });
 | 
						|
 | 
						|
  it('should be able to trace a named export', () => {
 | 
						|
    const symbol = reflectorNestedGenDir.findDeclaration(
 | 
						|
        './reexport/reexport.d.ts', 'One', '/tmp/src/main.ts');
 | 
						|
    expect(symbol.name).toEqual('One');
 | 
						|
    expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
 | 
						|
  });
 | 
						|
 | 
						|
  it('should be able to trace a renamed export', () => {
 | 
						|
    const symbol = reflectorNestedGenDir.findDeclaration(
 | 
						|
        './reexport/reexport.d.ts', 'Four', '/tmp/src/main.ts');
 | 
						|
    expect(symbol.name).toEqual('Three');
 | 
						|
    expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
 | 
						|
  });
 | 
						|
 | 
						|
  it('should be able to trace an export * export', () => {
 | 
						|
    const symbol = reflectorNestedGenDir.findDeclaration(
 | 
						|
        './reexport/reexport.d.ts', 'Five', '/tmp/src/main.ts');
 | 
						|
    expect(symbol.name).toEqual('Five');
 | 
						|
    expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin5.d.ts');
 | 
						|
  });
 | 
						|
 | 
						|
  it('should be able to trace a multi-level re-export', () => {
 | 
						|
    const symbol = reflectorNestedGenDir.findDeclaration(
 | 
						|
        './reexport/reexport.d.ts', 'Thirty', '/tmp/src/main.ts');
 | 
						|
    expect(symbol.name).toEqual('Thirty');
 | 
						|
    expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin30.d.ts');
 | 
						|
  });
 | 
						|
});
 | 
						|
 | 
						|
const dummyModule = 'export let foo: any[];';
 | 
						|
 | 
						|
const FILES: Entry = {
 | 
						|
  'tmp': {
 | 
						|
    'src': {
 | 
						|
      'main.ts': `
 | 
						|
        import * as c from '@angular/core';
 | 
						|
        import * as r from '@angular/router';
 | 
						|
        import * as u from './lib/utils';
 | 
						|
        import * as cs from './lib/collections';
 | 
						|
        import * as u2 from './lib2/utils2';
 | 
						|
      `,
 | 
						|
      'lib': {
 | 
						|
        'utils.ts': dummyModule,
 | 
						|
        'collections.ts': dummyModule,
 | 
						|
      },
 | 
						|
      'lib2': {'utils2.ts': dummyModule},
 | 
						|
      'reexport': {
 | 
						|
        'reexport.d.ts': `
 | 
						|
          import * as c from '@angular/core';
 | 
						|
        `,
 | 
						|
        'reexport.metadata.json': JSON.stringify({
 | 
						|
          __symbolic: 'module',
 | 
						|
          version: 1,
 | 
						|
          metadata: {},
 | 
						|
          exports: [
 | 
						|
            {from: './src/origin1', export: ['One', 'Two', {name: 'Three', as: 'Four'}]},
 | 
						|
            {from: './src/origin5'}, {from: './src/reexport2'}
 | 
						|
          ]
 | 
						|
        }),
 | 
						|
        'src': {
 | 
						|
          'origin1.d.ts': `
 | 
						|
            export class One {}
 | 
						|
            export class Two {}
 | 
						|
            export class Three {}
 | 
						|
          `,
 | 
						|
          'origin1.metadata.json': JSON.stringify({
 | 
						|
            __symbolic: 'module',
 | 
						|
            version: 1,
 | 
						|
            metadata: {
 | 
						|
              One: {__symbolic: 'class'},
 | 
						|
              Two: {__symbolic: 'class'},
 | 
						|
              Three: {__symbolic: 'class'},
 | 
						|
            },
 | 
						|
          }),
 | 
						|
          'origin5.d.ts': `
 | 
						|
            export class Five {}
 | 
						|
          `,
 | 
						|
          'origin5.metadata.json': JSON.stringify({
 | 
						|
            __symbolic: 'module',
 | 
						|
            version: 1,
 | 
						|
            metadata: {
 | 
						|
              Five: {__symbolic: 'class'},
 | 
						|
            },
 | 
						|
          }),
 | 
						|
          'origin30.d.ts': `
 | 
						|
            export class Thirty {}
 | 
						|
          `,
 | 
						|
          'origin30.metadata.json': JSON.stringify({
 | 
						|
            __symbolic: 'module',
 | 
						|
            version: 1,
 | 
						|
            metadata: {
 | 
						|
              Thirty: {__symbolic: 'class'},
 | 
						|
            },
 | 
						|
          }),
 | 
						|
          'originNone.d.ts': dummyModule,
 | 
						|
          'originNone.metadata.json': JSON.stringify({
 | 
						|
            __symbolic: 'module',
 | 
						|
            version: 1,
 | 
						|
            metadata: {},
 | 
						|
          }),
 | 
						|
          'reexport2.d.ts': dummyModule,
 | 
						|
          'reexport2.metadata.json': JSON.stringify({
 | 
						|
            __symbolic: 'module',
 | 
						|
            version: 1,
 | 
						|
            metadata: {},
 | 
						|
            exports: [{from: './originNone'}, {from: './origin30'}]
 | 
						|
          })
 | 
						|
        }
 | 
						|
      },
 | 
						|
      'node_modules': {
 | 
						|
        '@angular': {
 | 
						|
          'core.d.ts': dummyModule,
 | 
						|
          'core.metadata.json':
 | 
						|
              `{"__symbolic":"module", "version": 1, "metadata": {"foo": {"__symbolic": "class"}}}`,
 | 
						|
          'router': {'index.d.ts': dummyModule, 'src': {'providers.d.ts': dummyModule}},
 | 
						|
          'unused.d.ts': dummyModule,
 | 
						|
          'empty.d.ts': 'export declare var a: string;',
 | 
						|
          'empty.metadata.json': '[]',
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
function clone(entry: Entry): Entry {
 | 
						|
  if (typeof entry === 'string') {
 | 
						|
    return entry;
 | 
						|
  } else {
 | 
						|
    let result: Directory = {};
 | 
						|
    for (let name in entry) {
 | 
						|
      result[name] = clone(entry[name]);
 | 
						|
    }
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
}
 |