it('should be able to get metadata for a class with nested method calls', () => {
   const annotations = reflector.annotations(
       reflector.getStaticSymbol('/tmp/src/static-method-call.ts', 'MyFactoryComponent'));
   expect(annotations.length).toBe(1);
   expect(annotations[0].providers).toEqual({
     provide: 'c',
     useFactory:
         reflector.getStaticSymbol('/tmp/src/static-method.ts', 'AnotherModule', ['someFactory'])
   });
 });
 it('should simplify values initialized with a function call', () => {
   expect(simplify(
              reflector.getStaticSymbol('/tmp/src/function-reference.ts', ''),
              reflector.getStaticSymbol('/tmp/src/function-reference.ts', 'one')))
       .toEqual(['some-value']);
   expect(simplify(
              reflector.getStaticSymbol('/tmp/src/function-reference.ts', ''),
              reflector.getStaticSymbol('/tmp/src/function-reference.ts', 'three')))
       .toEqual(3);
 });
 function init(
     testData: {[key: string]: any} = DEFAULT_TEST_DATA,
     decorators: {name: string, filePath: string, ctor: any}[] = [],
     errorRecorder?: (error: any, fileName: string) => void, collectorOptions?: CollectorOptions) {
   const symbolCache = new StaticSymbolCache();
   host = new MockStaticSymbolResolverHost(testData, collectorOptions);
   symbolResolver =
       new StaticSymbolResolver(host, symbolCache, new MockSummaryResolver([]), errorRecorder);
   reflector = new StaticReflector(symbolResolver, decorators, [], errorRecorder);
   noContext = reflector.getStaticSymbol('', '');
 }
  it('should quote identifiers quoted in the source', () => {
    const sourceText = `
      import {Component} from '@angular/core';

      @Component({
        providers: [{ provide: 'SomeToken', useValue: {a: 1, 'b': 2, c: 3, 'd': 4}}]
      })
      export class MyComponent {}
    `;
    const source = ts.createSourceFile('test.ts', sourceText, ts.ScriptTarget.Latest);
    const collector = new MetadataCollector({quotedNames: true});
    const stubHost = new StubReflectorHost();
    const symbolCache = new StaticSymbolCache();
    const symbolResolver =
        new StaticSymbolResolver(stubHost, symbolCache, new MockSummaryResolver());
    const reflector = new StaticReflector(symbolResolver);

    // Get the metadata from the above source
    const metadata = collector.getMetadata(source);
    const componentMetadata = metadata.metadata['MyComponent'];

    // Get the first argument of the decorator call which is passed to @Component
    expect(isClassMetadata(componentMetadata)).toBeTruthy();
    if (!isClassMetadata(componentMetadata)) return;
    const decorators = componentMetadata.decorators;
    const firstDecorator = decorators[0];
    expect(isMetadataSymbolicCallExpression(firstDecorator)).toBeTruthy();
    if (!isMetadataSymbolicCallExpression(firstDecorator)) return;
    const firstArgument = firstDecorator.arguments[0];

    // Simplify this value using the StaticReflector
    const context = reflector.getStaticSymbol('none', 'none');
    const argumentValue = reflector.simplify(context, firstArgument);

    // Convert the value to an output AST
    const outputAst = convertValueToOutputAst(argumentValue);
    const statement = outputAst.toStmt();

    // Convert the value to text using the typescript emitter
    const emitter = new TypeScriptEmitter(new StubImportResolver());
    const text = emitter.emitStatements('module', [statement], []);

    // Expect the keys for 'b' and 'd' to be quoted but 'a' and 'c' not to be.
    expect(text).toContain('\'b\': 2');
    expect(text).toContain('\'d\': 4');
    expect(text).not.toContain('\'a\'');
    expect(text).not.toContain('\'c\'');
  });
Example #5
0
  it('should not throw on unknown decorators', () => {
    const data = Object.create(DEFAULT_TEST_DATA);
    const file = '/tmp/src/app.component.ts';
    data[file] = `
      import { Component } from '@angular/core';

      export const enum TypeEnum {
        type
      }

      export function MyValidationDecorator(p1: any, p2: any): any {
        return null;
      }

      export function ValidationFunction(a1: any): any {
        return null;
      }

      @Component({
        selector: 'my-app',
        template: "<h1>Hello {{name}}</h1>",
      })
      export class AppComponent  {
        name = 'Angular';

        @MyValidationDecorator( TypeEnum.type, ValidationFunction({option: 'value'}))
        myClassProp: number;
    }`;
    init(data);
    const appComponent = reflector.getStaticSymbol(file, 'AppComponent');
    expect(() => reflector.propMetadata(appComponent)).not.toThrow();
  });
Example #6
0
 it('should get annotations for HeroDetailComponent', () => {
   const HeroDetailComponent =
       reflector.findDeclaration('src/app/hero-detail.component', 'HeroDetailComponent');
   const annotations = reflector.annotations(HeroDetailComponent);
   expect(annotations.length).toEqual(1);
   const annotation = annotations[0];
   expect(annotation.selector).toEqual('my-hero-detail');
   expect(annotation.animations).toEqual([trigger('myAnimation', [
     state('state1', style({'background': 'white'})),
     transition(
         '* => *',
         sequence([group([animate(
             '1s 0.5s',
             keyframes([style({'background': 'blue'}), style({'background': 'red'})]))])]))
   ])]);
 });
  it('should continue to aggresively evaluate enum member accessors', () => {
    const data = Object.create(DEFAULT_TEST_DATA);
    const file = '/tmp/src/my_component.ts';
    data[file] = `
      import {Component} from '@angular/core';
      import {intermediate} from './index';

      @Component({
        template: '<div></div>',
        providers: [{provide: 'foo', useValue: [...intermediate]}]
      })
      export class MyComponent { }
    `;
    data['/tmp/src/intermediate.ts'] = `
      import {MyEnum} from './indirect';
      export const intermediate = [{
        data: {
          c: [MyEnum.Value]
        }
      }];`;
    data['/tmp/src/index.ts'] = `export * from './intermediate';`;
    data['/tmp/src/indirect.ts'] = `export * from './consts';`;
    data['/tmp/src/consts.ts'] = `
      export enum MyEnum {
        Value = 3
      }
    `;
    init(data);

    expect(reflector.annotations(reflector.getStaticSymbol(file, 'MyComponent'))[0]
               .providers[0]
               .useValue)
        .toEqual([{data: {c: [3]}}]);
  });
  it('should be able to inject a ctor parameter with a @Inject and a type expression', () => {
    const data = Object.create(DEFAULT_TEST_DATA);
    const file = '/tmp/src/invalid-component.ts';
    data[file] = `
        import {Injectable, Inject} from '@angular/core';

        @Injectable()
        export class SomeClass {
          constructor (@Inject('some-token') a: {a: string, b: string}) {}
        }
      `;
    init(data);

    const someClass = reflector.getStaticSymbol(file, 'SomeClass');
    const parameters = reflector.parameters(someClass);
    expect(compilerCore.createInject.isTypeOf(parameters[0][0])).toBe(true);
  });
Example #9
0
    it('should inherit property metadata', () => {
      initWithDecorator({
        '/tmp/src/main.ts': `
            import {PropDecorator} from './decorator';

            export class A {}
            export class B {}
            export class C {}

            export class Parent {
              @PropDecorator('a')
              a: A;
              @PropDecorator('b1')
              b: B;
            }

            export class Child extends Parent {
              @PropDecorator('b2')
              b: B;
              @PropDecorator('c')
              c: C;
            }

            export class ChildInvalidParent extends a.InvalidParent {}
          `
      });

      // Check that metadata for Parent was not changed!
      expect(reflector.propMetadata(reflector.getStaticSymbol('/tmp/src/main.ts', 'Parent')))
          .toEqual({
            'a': [new PropDecorator('a')],
            'b': [new PropDecorator('b1')],
          });

      expect(reflector.propMetadata(reflector.getStaticSymbol('/tmp/src/main.ts', 'Child')))
          .toEqual({
            'a': [new PropDecorator('a')],
            'b': [new PropDecorator('b1'), new PropDecorator('b2')],
            'c': [new PropDecorator('c')]
          });

      expect(reflector.propMetadata(
                 reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildInvalidParent')))
          .toEqual({});
    });
Example #10
0
 it('should get and empty annotation list for a symbol with null value', () => {
   init({
     '/tmp/test.ts': `
       export var x = null;
     `
   });
   const annotations = reflector.annotations(reflector.getStaticSymbol('/tmp/test.ts', 'x'));
   expect(annotations).toEqual([]);
 });
Example #11
0
    it('should allow inheritance from functions', () => {
      initWithDecorator({
        '/tmp/src/main.ts': `
            export let ctor: {new(): T} = function() { return null; }
            export class Child extends ctor {}
          `
      });

      expect(reflector.annotations(reflector.getStaticSymbol('/tmp/src/main.ts', 'Child')))
          .toEqual([]);
    });
Example #12
0
    it('should allow inheritance from expressions', () => {
      initWithDecorator({
        '/tmp/src/main.ts': `
            export function metaClass() { return null; };
            export class Child extends metaClass() {}
          `
      });

      expect(reflector.annotations(reflector.getStaticSymbol('/tmp/src/main.ts', 'Child')))
          .toEqual([]);
    });
  it('should reject a ctor parameter without a @Inject and a type exprssion', () => {
    const data = Object.create(DEFAULT_TEST_DATA);
    const file = '/tmp/src/invalid-component.ts';
    data[file] = `
        import {Injectable} from '@angular/core';

        @Injectable()
        export class SomeClass {
          constructor (a: {a: string, b: string}) {}
        }
      `;

    let error: any = undefined;
    init(data, [], (err: any, filePath: string) => {
      expect(error).toBeUndefined();
      error = err;
    });

    const someClass = reflector.getStaticSymbol(file, 'SomeClass');
    expect(reflector.parameters(someClass)).toEqual([[]]);
    expect(error).toBeUndefined();
  });
Example #14
0
  it('should ignore unresolved calls', () => {
    const data = Object.create(DEFAULT_TEST_DATA);
    const file = '/tmp/src/invalid-component.ts';
    data[file] = `
        import {Component} from '@angular/core';
        import {unknown} from 'unresolved';

        @Component({
          selector: 'tmp',
          template: () => {},
          providers: [triggers()]
        })
        export class BadComponent {

        }
      `;
    init(data, [], () => {}, {verboseInvalidExpression: true});

    const badComponent = reflector.getStaticSymbol(file, 'BadComponent');
    const annotations = reflector.annotations(badComponent);
    const annotation = annotations[0];
    expect(annotation.providers).toEqual([]);
  });
Example #15
0
    it('should inherit parameters', () => {
      initWithDecorator({
        '/tmp/src/main.ts': `
            import {ParamDecorator} from './decorator';

            export class A {}
            export class B {}
            export class C {}

            export class Parent {
              constructor(@ParamDecorator('a') a: A, @ParamDecorator('b') b: B) {}
            }

            export class Child extends Parent {}

            export class ChildWithCtor extends Parent {
              constructor(@ParamDecorator('c') c: C) {}
            }

            export class ChildInvalidParent extends a.InvalidParent {}
          `
      });

      // Check that metadata for Parent was not changed!
      expect(reflector.parameters(reflector.getStaticSymbol('/tmp/src/main.ts', 'Parent')))
          .toEqual([
            [reflector.getStaticSymbol('/tmp/src/main.ts', 'A'), new ParamDecorator('a')],
            [reflector.getStaticSymbol('/tmp/src/main.ts', 'B'), new ParamDecorator('b')]
          ]);

      expect(reflector.parameters(reflector.getStaticSymbol('/tmp/src/main.ts', 'Child'))).toEqual([
        [reflector.getStaticSymbol('/tmp/src/main.ts', 'A'), new ParamDecorator('a')],
        [reflector.getStaticSymbol('/tmp/src/main.ts', 'B'), new ParamDecorator('b')]
      ]);

      expect(reflector.parameters(reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildWithCtor')))
          .toEqual([[reflector.getStaticSymbol('/tmp/src/main.ts', 'C'), new ParamDecorator('c')]]);

      expect(
          reflector.parameters(reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildInvalidParent')))
          .toEqual([]);
    });
    it('should be able to accept a lambda in a reference location', () => {
      const data = Object.create(DEFAULT_TEST_DATA);
      const file = '/tmp/src/my_component.ts';
      data[file] = `
        import {Component, InjectionToken} from '@angular/core';

        export const myLambda = () => [1, 2, 3];
        export const NUMBERS = new InjectionToken<number[]>();

        @Component({
          template: '<div>{{name}}</div>',
          providers: [{provide: NUMBERS, useFactory: myLambda}]
        })
        export class MyComponent {
          name = 'Some name';
        }
      `;
      init(data);

      expect(reflector.annotations(reflector.getStaticSymbol(file, 'MyComponent'))[0]
                 .providers[0]
                 .useFactory)
          .toBe(reflector.getStaticSymbol(file, 'myLambda'));
    });
Example #17
0
  it('should not throw with an invalid extends', () => {
    const data = Object.create(DEFAULT_TEST_DATA);
    const file = '/tmp/src/invalid-component.ts';
    data[file] = `
        import {Component} from '@angular/core';

        function InvalidParent() {
          return InvalidParent;
        }

        @Component({
          selector: 'tmp',
          template: '',
        })
        export class BadComponent extends InvalidParent() {

        }
      `;
    init(data);
    const badComponent = reflector.getStaticSymbol(file, 'BadComponent');
    expect(reflector.propMetadata(badComponent)).toEqual({});
    expect(reflector.parameters(badComponent)).toEqual([]);
    expect(reflector.hasLifecycleHook(badComponent, 'onDestroy')).toEqual(false);
  });
Example #18
0
  it('should produce a annotation even if it contains errors', () => {
    const data = Object.create(DEFAULT_TEST_DATA);
    const file = '/tmp/src/invalid-component.ts';
    data[file] = `
        import {Component} from '@angular/core';

        @Component({
          selector: 'tmp',
          template: () => {},
          providers: [1, 2, (() => {}), 3, !(() => {}), 4, 5, (() => {}) + (() => {}), 6, 7]
        })
        export class BadComponent {

        }
      `;
    init(data, [], () => {}, {verboseInvalidExpression: true});

    const badComponent = reflector.getStaticSymbol(file, 'BadComponent');
    const annotations = reflector.annotations(badComponent);
    const annotation = annotations[0];
    expect(annotation.selector).toEqual('tmp');
    expect(annotation.template).toBeUndefined();
    expect(annotation.providers).toEqual([1, 2, 3, 4, 5, 6, 7]);
  });
Example #19
0
 it('should record data about the error in the exception', () => {
   let threw = false;
   try {
     const metadata = host.getMetadataFor('/tmp/src/invalid-metadata.ts');
     expect(metadata).toBeDefined();
     const moduleMetadata: any = metadata[0]['metadata'];
     expect(moduleMetadata).toBeDefined();
     const classData: any = moduleMetadata['InvalidMetadata'];
     expect(classData).toBeDefined();
     simplify(
         reflector.getStaticSymbol('/tmp/src/invalid-metadata.ts', ''), classData.decorators[0]);
   } catch (e) {
     expect(e.fileName).toBe('/tmp/src/invalid-metadata.ts');
     threw = true;
   }
   expect(threw).toBe(true);
 });
Example #20
0
  it('should get constructor for NgFor', () => {
    const NgFor = reflector.findDeclaration('@angular/common/src/directives/ng_for', 'NgFor');
    const ViewContainerRef = reflector.findDeclaration('@angular/core', 'ViewContainerRef');
    const TemplateRef = reflector.findDeclaration('@angular/core', 'TemplateRef');
    const IterableDiffers = reflector.findDeclaration('@angular/core', 'IterableDiffers');
    const ChangeDetectorRef = reflector.findDeclaration('@angular/core', 'ChangeDetectorRef');

    const parameters = reflector.parameters(NgFor);
    expect(parameters).toEqual([
      [ViewContainerRef], [TemplateRef], [IterableDiffers], [ChangeDetectorRef]
    ]);
  });
Example #21
0
    it('should support constructor parameters with @Inject and an interface type', () => {
      const data = Object.create(DEFAULT_TEST_DATA);
      const file = '/tmp/src/inject_interface.ts';
      data[file] = `
        import {Injectable, Inject} from '@angular/core';
        import {F} from './f';

        export interface InjectedInterface {

        }

        export class Token {}

        @Injectable()
        export class SomeClass {
          constructor (@Inject(Token) injected: InjectedInterface, t: Token, @Inject(Token) f: F) {}
        }
      `;

      init(data);

      expect(reflector.parameters(reflector.getStaticSymbol(file, 'SomeClass'))[0].length)
          .toEqual(1);
    });
Example #22
0
 () => {
   const annotations = reflector.annotations(
       reflector.getStaticSymbol('/tmp/src/static-method-call.ts', 'MyDefaultsComponent'));
   expect(annotations.length).toBe(1);
   expect(annotations[0].providers).toEqual([['a', true, false]]);
 });
Example #23
0
 it('should get and empty annotation list for an unknown class', () => {
   const UnknownClass = reflector.findDeclaration('src/app/app.component', 'UnknownClass');
   const annotations = reflector.annotations(UnknownClass);
   expect(annotations).toEqual([]);
 });
Example #24
0
 return names.map(name => reflector.hasLifecycleHook(symbol, name));
Example #25
0
 it('should read ctor parameters with forwardRef', () => {
   const src = '/tmp/src/forward-ref.ts';
   const dep = reflector.getStaticSymbol(src, 'Dep');
   const props = reflector.parameters(reflector.getStaticSymbol(src, 'Forward'));
   expect(props).toEqual([[dep, new Inject(dep)]]);
 });
Example #26
0
 () => reflector.annotations(
     reflector.getStaticSymbol('/tmp/src/invalid-calls.ts', 'MyComponent')))
Example #27
0
 expect(() => reflector.propMetadata(appComponent)).not.toThrow();
Example #28
0
 it('should be able to get metadata with a reference to a static method', () => {
   const annotations = reflector.annotations(
       reflector.getStaticSymbol('/tmp/src/static-method-ref.ts', 'MethodReference'));
   expect(annotations.length).toBe(1);
   expect(annotations[0].providers[0].useValue.members[0]).toEqual('staticMethod');
 });
Example #29
0
 it('should be able to get metadata for a class containing a static method call', () => {
   const annotations = reflector.annotations(
       reflector.getStaticSymbol('/tmp/src/static-method-call.ts', 'MyComponent'));
   expect(annotations.length).toBe(1);
   expect(annotations[0].providers).toEqual({provider: 'a', useValue: 100});
 });
Example #30
0
 it('should be able to get metadata for a class containing a static field reference', () => {
   const annotations = reflector.annotations(
       reflector.getStaticSymbol('/tmp/src/static-field-reference.ts', 'Foo'));
   expect(annotations.length).toBe(1);
   expect(annotations[0].providers).toEqual([{provider: 'a', useValue: 'Some string'}]);
 });