"use strict";
/**
* @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
*/
Object.defineProperty(exports, "__esModule", { value: true });
var ts = require("typescript");
var collector_1 = require("../src/collector");
var schema_1 = require("../src/schema");
var typescript_mocks_1 = require("./typescript.mocks");
describe('Collector', function () {
var documentRegistry = ts.createDocumentRegistry();
var host;
var service;
var program;
var collector;
beforeEach(function () {
host = new typescript_mocks_1.Host(FILES, [
'/app/app.component.ts',
'/app/cases-data.ts',
'/app/error-cases.ts',
'/promise.ts',
'/unsupported-1.ts',
'/unsupported-2.ts',
'/unsupported-3.ts',
'class-arity.ts',
'import-star.ts',
'exported-classes.ts',
'exported-functions.ts',
'exported-enum.ts',
'exported-consts.ts',
'local-symbol-ref.ts',
'local-function-ref.ts',
'local-symbol-ref-func.ts',
'local-symbol-ref-func-dynamic.ts',
'private-enum.ts',
're-exports.ts',
're-exports-2.ts',
'export-as.d.ts',
'static-field-reference.ts',
'static-method.ts',
'static-method-call.ts',
'static-method-with-if.ts',
'static-method-with-default.ts',
'class-inheritance.ts',
'class-inheritance-parent.ts',
'class-inheritance-declarations.d.ts',
'interface-reference.ts'
]);
service = ts.createLanguageService(host, documentRegistry);
program = service.getProgram();
collector = new collector_1.MetadataCollector({ quotedNames: true });
});
it('should not have errors in test data', function () { typescript_mocks_1.expectValidSources(service, program); });
it('should return undefined for modules that have no metadata', function () {
var sourceFile = program.getSourceFile('app/empty.ts');
var metadata = collector.getMetadata(sourceFile);
expect(metadata).toBeUndefined();
});
it('should return an interface reference for interfaces', function () {
var sourceFile = program.getSourceFile('app/hero.ts');
var metadata = collector.getMetadata(sourceFile);
expect(metadata).toEqual({ __symbolic: 'module', version: 3, metadata: { Hero: { __symbolic: 'interface' } } });
});
it('should be able to collect a simple component\'s metadata', function () {
var sourceFile = program.getSourceFile('app/hero-detail.component.ts');
var metadata = collector.getMetadata(sourceFile);
expect(metadata).toEqual({
__symbolic: 'module',
version: 3,
metadata: {
HeroDetailComponent: {
__symbolic: 'class',
decorators: [{
__symbolic: 'call',
expression: { __symbolic: 'reference', module: 'angular2/core', name: 'Component' },
arguments: [{
selector: 'my-hero-detail',
template: "\n
\n
{{hero.name}} details! \n
id: {{hero.id}}
\n
\n name: \n \n
\n
\n "
}]
}],
members: {
hero: [{
__symbolic: 'property',
decorators: [{
__symbolic: 'call',
expression: { __symbolic: 'reference', module: 'angular2/core', name: 'Input' }
}]
}]
}
}
}
});
});
it('should be able to get a more complicated component\'s metadata', function () {
var sourceFile = program.getSourceFile('/app/app.component.ts');
var metadata = collector.getMetadata(sourceFile);
expect(metadata).toEqual({
__symbolic: 'module',
version: 3,
metadata: {
AppComponent: {
__symbolic: 'class',
decorators: [{
__symbolic: 'call',
expression: { __symbolic: 'reference', module: 'angular2/core', name: 'Component' },
arguments: [{
selector: 'my-app',
template: "\n My Heroes \n \n \n {{hero.id | lowercase}} {{hero.name | uppercase}}\n \n \n \n ",
directives: [
{
__symbolic: 'reference',
module: './hero-detail.component',
name: 'HeroDetailComponent',
},
{ __symbolic: 'reference', module: 'angular2/common', name: 'NgFor' }
],
providers: [{ __symbolic: 'reference', module: './hero.service', default: true }],
pipes: [
{ __symbolic: 'reference', module: 'angular2/common', name: 'LowerCasePipe' },
{ __symbolic: 'reference', module: 'angular2/common', name: 'UpperCasePipe' }
]
}]
}],
members: {
__ctor__: [{
__symbolic: 'constructor',
parameters: [{ __symbolic: 'reference', module: './hero.service', default: true }]
}],
onSelect: [{ __symbolic: 'method' }],
ngOnInit: [{ __symbolic: 'method' }],
getHeroes: [{ __symbolic: 'method' }]
}
}
}
});
});
it('should return the values of exported variables', function () {
var sourceFile = program.getSourceFile('/app/mock-heroes.ts');
var metadata = collector.getMetadata(sourceFile);
expect(metadata).toEqual({
__symbolic: 'module',
version: 3,
metadata: {
HEROES: [
{ 'id': 11, 'name': 'Mr. Nice', '$quoted$': ['id', 'name'] },
{ 'id': 12, 'name': 'Narco', '$quoted$': ['id', 'name'] },
{ 'id': 13, 'name': 'Bombasto', '$quoted$': ['id', 'name'] },
{ 'id': 14, 'name': 'Celeritas', '$quoted$': ['id', 'name'] },
{ 'id': 15, 'name': 'Magneta', '$quoted$': ['id', 'name'] },
{ 'id': 16, 'name': 'RubberMan', '$quoted$': ['id', 'name'] },
{ 'id': 17, 'name': 'Dynama', '$quoted$': ['id', 'name'] },
{ 'id': 18, 'name': 'Dr IQ', '$quoted$': ['id', 'name'] },
{ 'id': 19, 'name': 'Magma', '$quoted$': ['id', 'name'] },
{ 'id': 20, 'name': 'Tornado', '$quoted$': ['id', 'name'] }
]
}
});
});
var casesFile;
var casesMetadata;
beforeEach(function () {
casesFile = program.getSourceFile('/app/cases-data.ts');
casesMetadata = collector.getMetadata(casesFile);
});
it('should provide any reference for an any ctor parameter type', function () {
var casesAny = casesMetadata.metadata['CaseAny'];
expect(casesAny).toBeTruthy();
var ctorData = casesAny.members['__ctor__'];
expect(ctorData).toEqual([{ __symbolic: 'constructor', parameters: [{ __symbolic: 'reference', name: 'any' }] }]);
});
it('should record annotations on set and get declarations', function () {
var propertyData = {
name: [{
__symbolic: 'property',
decorators: [{
__symbolic: 'call',
expression: { __symbolic: 'reference', module: 'angular2/core', name: 'Input' },
arguments: ['firstName']
}]
}]
};
var caseGetProp = casesMetadata.metadata['GetProp'];
expect(caseGetProp.members).toEqual(propertyData);
var caseSetProp = casesMetadata.metadata['SetProp'];
expect(caseSetProp.members).toEqual(propertyData);
var caseFullProp = casesMetadata.metadata['FullProp'];
expect(caseFullProp.members).toEqual(propertyData);
});
it('should record references to parameterized types', function () {
var casesForIn = casesMetadata.metadata['NgFor'];
expect(casesForIn).toEqual({
__symbolic: 'class',
decorators: [{
__symbolic: 'call',
expression: { __symbolic: 'reference', module: 'angular2/core', name: 'Injectable' }
}],
members: {
__ctor__: [{
__symbolic: 'constructor',
parameters: [{
__symbolic: 'reference',
name: 'ClassReference',
arguments: [{ __symbolic: 'reference', name: 'NgForRow' }]
}]
}]
}
});
});
it('should report errors for destructured imports', function () {
var unsupported1 = program.getSourceFile('/unsupported-1.ts');
var metadata = collector.getMetadata(unsupported1);
expect(metadata).toEqual({
__symbolic: 'module',
version: 3,
metadata: {
a: { __symbolic: 'error', message: 'Destructuring not supported', line: 1, character: 16 },
b: { __symbolic: 'error', message: 'Destructuring not supported', line: 1, character: 19 },
c: { __symbolic: 'error', message: 'Destructuring not supported', line: 2, character: 16 },
d: { __symbolic: 'error', message: 'Destructuring not supported', line: 2, character: 19 },
e: { __symbolic: 'error', message: 'Variable not initialized', line: 3, character: 15 }
}
});
});
it('should report an error for references to unexpected types', function () {
var unsupported1 = program.getSourceFile('/unsupported-2.ts');
var metadata = collector.getMetadata(unsupported1);
var barClass = metadata.metadata['Bar'];
var ctor = barClass.members['__ctor__'][0];
var parameter = ctor.parameters[0];
expect(parameter).toEqual({
__symbolic: 'error',
message: 'Reference to non-exported class',
line: 3,
character: 4,
context: { className: 'Foo' }
});
});
it('should be able to handle import star type references', function () {
var importStar = program.getSourceFile('/import-star.ts');
var metadata = collector.getMetadata(importStar);
var someClass = metadata.metadata['SomeClass'];
var ctor = someClass.members['__ctor__'][0];
var parameters = ctor.parameters;
expect(parameters).toEqual([
{ __symbolic: 'reference', module: 'angular2/common', name: 'NgFor' }
]);
});
it('should record all exported classes', function () {
var sourceFile = program.getSourceFile('/exported-classes.ts');
var metadata = collector.getMetadata(sourceFile);
expect(metadata).toEqual({
__symbolic: 'module',
version: 3,
metadata: {
SimpleClass: { __symbolic: 'class' },
AbstractClass: { __symbolic: 'class' },
DeclaredClass: { __symbolic: 'class' }
}
});
});
it('should be able to record functions', function () {
var exportedFunctions = program.getSourceFile('/exported-functions.ts');
var metadata = collector.getMetadata(exportedFunctions);
expect(metadata).toEqual({
__symbolic: 'module',
version: 3,
metadata: {
one: {
__symbolic: 'function',
parameters: ['a', 'b', 'c'],
value: {
a: { __symbolic: 'reference', name: 'a' },
b: { __symbolic: 'reference', name: 'b' },
c: { __symbolic: 'reference', name: 'c' }
}
},
two: {
__symbolic: 'function',
parameters: ['a', 'b', 'c'],
value: {
a: { __symbolic: 'reference', name: 'a' },
b: { __symbolic: 'reference', name: 'b' },
c: { __symbolic: 'reference', name: 'c' }
}
},
three: {
__symbolic: 'function',
parameters: ['a', 'b', 'c'],
value: [
{ __symbolic: 'reference', name: 'a' }, { __symbolic: 'reference', name: 'b' },
{ __symbolic: 'reference', name: 'c' }
]
},
supportsState: {
__symbolic: 'function',
parameters: [],
value: {
__symbolic: 'pre',
operator: '!',
operand: {
__symbolic: 'pre',
operator: '!',
operand: {
__symbolic: 'select',
expression: {
__symbolic: 'select',
expression: { __symbolic: 'reference', name: 'window' },
member: 'history'
},
member: 'pushState'
}
}
}
},
complexFn: { __symbolic: 'function' },
declaredFn: { __symbolic: 'function' }
}
});
});
it('should be able to handle import star type references', function () {
var importStar = program.getSourceFile('/import-star.ts');
var metadata = collector.getMetadata(importStar);
var someClass = metadata.metadata['SomeClass'];
var ctor = someClass.members['__ctor__'][0];
var parameters = ctor.parameters;
expect(parameters).toEqual([
{ __symbolic: 'reference', module: 'angular2/common', name: 'NgFor' }
]);
});
it('should be able to collect the value of an enum', function () {
var enumSource = program.getSourceFile('/exported-enum.ts');
var metadata = collector.getMetadata(enumSource);
var someEnum = metadata.metadata['SomeEnum'];
expect(someEnum).toEqual({ A: 0, B: 1, C: 100, D: 101 });
});
it('should ignore a non-export enum', function () {
var enumSource = program.getSourceFile('/private-enum.ts');
var metadata = collector.getMetadata(enumSource);
var publicEnum = metadata.metadata['PublicEnum'];
var privateEnum = metadata.metadata['PrivateEnum'];
expect(publicEnum).toEqual({ a: 0, b: 1, c: 2 });
expect(privateEnum).toBeUndefined();
});
it('should be able to collect enums initialized from consts', function () {
var enumSource = program.getSourceFile('/exported-enum.ts');
var metadata = collector.getMetadata(enumSource);
var complexEnum = metadata.metadata['ComplexEnum'];
expect(complexEnum).toEqual({
A: 0,
B: 1,
C: 30,
D: 40,
E: { __symbolic: 'reference', module: './exported-consts', name: 'constValue' }
});
});
it('should be able to collect a simple static method', function () {
var staticSource = program.getSourceFile('/static-method.ts');
var metadata = collector.getMetadata(staticSource);
expect(metadata).toBeDefined();
var classData = metadata.metadata['MyModule'];
expect(classData).toBeDefined();
expect(classData.statics).toEqual({
with: {
__symbolic: 'function',
parameters: ['comp'],
value: [
{ __symbolic: 'reference', name: 'MyModule' },
{ provider: 'a', useValue: { __symbolic: 'reference', name: 'comp' } }
]
}
});
});
it('should be able to collect a call to a static method', function () {
var staticSource = program.getSourceFile('/static-method-call.ts');
var metadata = collector.getMetadata(staticSource);
expect(metadata).toBeDefined();
var classData = metadata.metadata['Foo'];
expect(classData).toBeDefined();
expect(classData.decorators).toEqual([{
__symbolic: 'call',
expression: { __symbolic: 'reference', module: 'angular2/core', name: 'Component' },
arguments: [{
providers: {
__symbolic: 'call',
expression: {
__symbolic: 'select',
expression: { __symbolic: 'reference', module: './static-method', name: 'MyModule' },
member: 'with'
},
arguments: ['a']
}
}]
}]);
});
it('should be able to collect a static field', function () {
var staticSource = program.getSourceFile('/static-field.ts');
var metadata = collector.getMetadata(staticSource);
expect(metadata).toBeDefined();
var classData = metadata.metadata['MyModule'];
expect(classData).toBeDefined();
expect(classData.statics).toEqual({ VALUE: 'Some string' });
});
it('should be able to collect a reference to a static field', function () {
var staticSource = program.getSourceFile('/static-field-reference.ts');
var metadata = collector.getMetadata(staticSource);
expect(metadata).toBeDefined();
var classData = metadata.metadata['Foo'];
expect(classData).toBeDefined();
expect(classData.decorators).toEqual([{
__symbolic: 'call',
expression: { __symbolic: 'reference', module: 'angular2/core', name: 'Component' },
arguments: [{
providers: [{
provide: 'a',
useValue: {
__symbolic: 'select',
expression: { __symbolic: 'reference', module: './static-field', name: 'MyModule' },
member: 'VALUE'
}
}]
}]
}]);
});
it('should be able to collect a method with a conditional expression', function () {
var source = program.getSourceFile('/static-method-with-if.ts');
var metadata = collector.getMetadata(source);
expect(metadata).toBeDefined();
var classData = metadata.metadata['MyModule'];
expect(classData).toBeDefined();
expect(classData.statics).toEqual({
with: {
__symbolic: 'function',
parameters: ['cond'],
value: [
{ __symbolic: 'reference', name: 'MyModule' }, {
provider: 'a',
useValue: {
__symbolic: 'if',
condition: { __symbolic: 'reference', name: 'cond' },
thenExpression: '1',
elseExpression: '2'
}
}
]
}
});
});
it('should be able to collect a method with a default parameter', function () {
var source = program.getSourceFile('/static-method-with-default.ts');
var metadata = collector.getMetadata(source);
expect(metadata).toBeDefined();
var classData = metadata.metadata['MyModule'];
expect(classData).toBeDefined();
expect(classData.statics).toEqual({
with: {
__symbolic: 'function',
parameters: ['comp', 'foo', 'bar'],
defaults: [undefined, true, false],
value: [
{ __symbolic: 'reference', name: 'MyModule' }, {
__symbolic: 'if',
condition: { __symbolic: 'reference', name: 'foo' },
thenExpression: { provider: 'a', useValue: { __symbolic: 'reference', name: 'comp' } },
elseExpression: { provider: 'b', useValue: { __symbolic: 'reference', name: 'comp' } }
},
{
__symbolic: 'if',
condition: { __symbolic: 'reference', name: 'bar' },
thenExpression: { provider: 'c', useValue: { __symbolic: 'reference', name: 'comp' } },
elseExpression: { provider: 'd', useValue: { __symbolic: 'reference', name: 'comp' } }
}
]
}
});
});
it('should be able to collect re-exported symbols', function () {
var source = program.getSourceFile('/re-exports.ts');
var metadata = collector.getMetadata(source);
expect(metadata.exports).toEqual([
{ from: './static-field', export: ['MyModule'] },
{ from: './static-field-reference', export: [{ name: 'Foo', as: 'OtherModule' }] },
{ from: 'angular2/core' }
]);
});
it('should be able to collect a export as symbol', function () {
var source = program.getSourceFile('export-as.d.ts');
var metadata = collector.getMetadata(source);
expect(metadata.metadata).toEqual({ SomeFunction: { __symbolic: 'function' } });
});
it('should be able to collect exports with no module specifier', function () {
var source = program.getSourceFile('/re-exports-2.ts');
var metadata = collector.getMetadata(source);
expect(metadata.metadata).toEqual({
MyClass: Object({ __symbolic: 'class' }),
OtherModule: { __symbolic: 'reference', module: './static-field-reference', name: 'Foo' },
MyOtherModule: { __symbolic: 'reference', module: './static-field', name: 'MyModule' }
});
});
it('should collect an error symbol if collecting a reference to a non-exported symbol', function () {
var source = program.getSourceFile('/local-symbol-ref.ts');
var metadata = collector.getMetadata(source);
expect(metadata.metadata).toEqual({
REQUIRED_VALIDATOR: {
__symbolic: 'error',
message: 'Reference to a local symbol',
line: 3,
character: 8,
context: { name: 'REQUIRED' }
},
SomeComponent: {
__symbolic: 'class',
decorators: [{
__symbolic: 'call',
expression: { __symbolic: 'reference', module: 'angular2/core', name: 'Component' },
arguments: [{ providers: [{ __symbolic: 'reference', name: 'REQUIRED_VALIDATOR' }] }]
}]
}
});
});
it('should collect an error symbol if collecting a reference to a non-exported function', function () {
var source = program.getSourceFile('/local-function-ref.ts');
var metadata = collector.getMetadata(source);
expect(metadata.metadata).toEqual({
REQUIRED_VALIDATOR: {
__symbolic: 'error',
message: 'Reference to a non-exported function',
line: 3,
character: 13,
context: { name: 'required' }
},
SomeComponent: {
__symbolic: 'class',
decorators: [{
__symbolic: 'call',
expression: { __symbolic: 'reference', module: 'angular2/core', name: 'Component' },
arguments: [{ providers: [{ __symbolic: 'reference', name: 'REQUIRED_VALIDATOR' }] }]
}]
}
});
});
it('should collect an error for a simple function that references a local variable', function () {
var source = program.getSourceFile('/local-symbol-ref-func.ts');
var metadata = collector.getMetadata(source);
expect(metadata.metadata).toEqual({
foo: {
__symbolic: 'function',
parameters: ['index'],
value: {
__symbolic: 'error',
message: 'Reference to a local symbol',
line: 1,
character: 8,
context: { name: 'localSymbol' }
}
}
});
});
it('should collect any for interface parameter reference', function () {
var source = program.getSourceFile('/interface-reference.ts');
var metadata = collector.getMetadata(source);
expect(metadata.metadata['SomeClass'].members).toEqual({
__ctor__: [{
__symbolic: 'constructor',
parameterDecorators: [[{
__symbolic: 'call',
expression: { __symbolic: 'reference', module: 'angular2/core', name: 'Inject' },
arguments: ['a']
}]],
parameters: [{ __symbolic: 'reference', name: 'any' }]
}]
});
});
describe('with interpolations', function () {
function createSource(text) {
return ts.createSourceFile('', text, ts.ScriptTarget.Latest, true);
}
function e(expr, prefix) {
var source = createSource((prefix || '') + " export let value = " + expr + ";");
var metadata = collector.getMetadata(source);
return expect(metadata.metadata['value']);
}
it('should be able to collect a raw interpolated string', function () { e('`simple value`').toBe('simple value'); });
it('should be able to interpolate a single value', function () { e('`${foo}`', 'const foo = "foo value"').toBe('foo value'); });
it('should be able to interpolate multiple values', function () {
e('`foo:${foo}, bar:${bar}, end`', 'const foo = "foo"; const bar = "bar";')
.toBe('foo:foo, bar:bar, end');
});
it('should be able to interpolate with an imported reference', function () {
e('`external:${external}`', 'import {external} from "./external";').toEqual({
__symbolic: 'binop',
operator: '+',
left: 'external:',
right: {
__symbolic: 'reference',
module: './external',
name: 'external',
}
});
});
it('should simplify a redundant template', function () {
e('`${external}`', 'import {external} from "./external";')
.toEqual({ __symbolic: 'reference', module: './external', name: 'external' });
});
it('should be able to collect complex template with imported references', function () {
e('`foo:${foo}, bar:${bar}, end`', 'import {foo, bar} from "./external";').toEqual({
__symbolic: 'binop',
operator: '+',
left: {
__symbolic: 'binop',
operator: '+',
left: {
__symbolic: 'binop',
operator: '+',
left: {
__symbolic: 'binop',
operator: '+',
left: 'foo:',
right: { __symbolic: 'reference', module: './external', name: 'foo' }
},
right: ', bar:'
},
right: { __symbolic: 'reference', module: './external', name: 'bar' }
},
right: ', end'
});
});
it('should reject a tagged literal', function () {
e('tag`some value`').toEqual({
__symbolic: 'error',
message: 'Tagged template expressions are not supported in metadata',
line: 0,
character: 20
});
});
});
it('should ignore |null or |undefined in type expressions', function () {
var source = ts.createSourceFile('somefile.ts', "\n import {Foo} from './foo';\n export class SomeClass {\n constructor (a: Foo, b: Foo | null, c: Foo | undefined, d: Foo | undefined | null, e: Foo | undefined | null | Foo) {}\n }\n ", ts.ScriptTarget.Latest, true);
var metadata = collector.getMetadata(source);
expect(metadata.metadata['SomeClass'].members).toEqual({
__ctor__: [{
__symbolic: 'constructor',
parameters: [
{ __symbolic: 'reference', module: './foo', name: 'Foo' },
{ __symbolic: 'reference', module: './foo', name: 'Foo' },
{ __symbolic: 'reference', module: './foo', name: 'Foo' },
{ __symbolic: 'reference', module: './foo', name: 'Foo' },
{ __symbolic: 'reference', module: './foo', name: 'Foo' }
]
}]
});
});
describe('in strict mode', function () {
it('should throw if an error symbol is collecting a reference to a non-exported symbol', function () {
var source = program.getSourceFile('/local-symbol-ref.ts');
expect(function () { return collector.getMetadata(source, true); }).toThrowError(/Reference to a local symbol/);
});
it('should throw if an error if collecting a reference to a non-exported function', function () {
var source = program.getSourceFile('/local-function-ref.ts');
expect(function () { return collector.getMetadata(source, true); })
.toThrowError(/Reference to a non-exported function/);
});
it('should throw for references to unexpected types', function () {
var unsupported2 = program.getSourceFile('/unsupported-2.ts');
expect(function () { return collector.getMetadata(unsupported2, true); })
.toThrowError(/Reference to non-exported class/);
});
it('should throw for errors in a static method', function () {
var unsupported3 = program.getSourceFile('/unsupported-3.ts');
expect(function () { return collector.getMetadata(unsupported3, true); })
.toThrowError(/Reference to a non-exported class/);
});
});
describe('with invalid input', function () {
it('should not throw with a class with no name', function () {
var fileName = '/invalid-class.ts';
override(fileName, 'export class');
var invalidClass = program.getSourceFile(fileName);
expect(function () { return collector.getMetadata(invalidClass); }).not.toThrow();
});
it('should not throw with a function with no name', function () {
var fileName = '/invalid-function.ts';
override(fileName, 'export function');
var invalidFunction = program.getSourceFile(fileName);
expect(function () { return collector.getMetadata(invalidFunction); }).not.toThrow();
});
});
describe('inheritance', function () {
it('should record `extends` clauses for declared classes', function () {
var metadata = collector.getMetadata(program.getSourceFile('/class-inheritance.ts'));
expect(metadata.metadata['DeclaredChildClass'])
.toEqual({ __symbolic: 'class', extends: { __symbolic: 'reference', name: 'ParentClass' } });
});
it('should record `extends` clauses for classes in the same file', function () {
var metadata = collector.getMetadata(program.getSourceFile('/class-inheritance.ts'));
expect(metadata.metadata['ChildClassSameFile'])
.toEqual({ __symbolic: 'class', extends: { __symbolic: 'reference', name: 'ParentClass' } });
});
it('should record `extends` clauses for classes in a different file', function () {
var metadata = collector.getMetadata(program.getSourceFile('/class-inheritance.ts'));
expect(metadata.metadata['ChildClassOtherFile']).toEqual({
__symbolic: 'class',
extends: {
__symbolic: 'reference',
module: './class-inheritance-parent',
name: 'ParentClassFromOtherFile'
}
});
});
function expectClass(entry) {
var result = schema_1.isClassMetadata(entry);
expect(result).toBeTruthy();
return result;
}
it('should collect the correct arity for a class', function () {
var metadata = collector.getMetadata(program.getSourceFile('/class-arity.ts'));
var zero = metadata.metadata['Zero'];
if (expectClass(zero))
expect(zero.arity).toBeUndefined();
var one = metadata.metadata['One'];
if (expectClass(one))
expect(one.arity).toBe(1);
var two = metadata.metadata['Two'];
if (expectClass(two))
expect(two.arity).toBe(2);
var three = metadata.metadata['Three'];
if (expectClass(three))
expect(three.arity).toBe(3);
var nine = metadata.metadata['Nine'];
if (expectClass(nine))
expect(nine.arity).toBe(9);
});
});
describe('regerssion', function () {
it('should be able to collect a short-hand property value', function () {
var source = ts.createSourceFile('', "\n const children = { f1: 1 };\n export const r = [\n {path: ':locale', children}\n ];\n ", ts.ScriptTarget.Latest, true);
var metadata = collector.getMetadata(source);
expect(metadata.metadata).toEqual({ r: [{ path: ':locale', children: { f1: 1 } }] });
});
});
function override(fileName, content) {
host.overrideFile(fileName, content);
host.addFile(fileName);
program = service.getProgram();
}
});
// TODO: Do not use \` in a template literal as it confuses clang-format
var FILES = {
'app': {
'app.component.ts': "\n import {Component as MyComponent, OnInit} from 'angular2/core';\n import * as common from 'angular2/common';\n import {Hero} from './hero';\n import {HeroDetailComponent} from './hero-detail.component';\n import HeroService from './hero.service';\n // thrown away\n import 'angular2/core';\n\n @MyComponent({\n selector: 'my-app',\n template:" +
'`' +
"\n My Heroes \n \n \n {{hero.id | lowercase}} {{hero.name | uppercase}}\n \n \n \n " +
'`' +
",\n directives: [HeroDetailComponent, common.NgFor],\n providers: [HeroService],\n pipes: [common.LowerCasePipe, common.UpperCasePipe]\n })\n export class AppComponent implements OnInit {\n public title = 'Tour of Heroes';\n public heroes: Hero[];\n public selectedHero: Hero;\n\n constructor(private _heroService: HeroService) { }\n\n onSelect(hero: Hero) { this.selectedHero = hero; }\n\n ngOnInit() {\n this.getHeroes()\n }\n\n getHeroes() {\n this._heroService.getHeroesSlowly().then(heros => this.heroes = heros);\n }\n }",
'hero.ts': "\n export interface Hero {\n id: number;\n name: string;\n }",
'empty.ts': "",
'hero-detail.component.ts': "\n import {Component, Input} from 'angular2/core';\n import {Hero} from './hero';\n\n @Component({\n selector: 'my-hero-detail',\n template: " +
'`' +
"\n \n
{{hero.name}} details! \n
id: {{hero.id}}
\n
\n name: \n \n
\n
\n " +
'`' +
",\n })\n export class HeroDetailComponent {\n @Input() public hero: Hero;\n }",
'mock-heroes.ts': "\n import {Hero as Hero} from './hero';\n\n export const HEROES: Hero[] = [\n {\"id\": 11, \"name\": \"Mr. Nice\"},\n {\"id\": 12, \"name\": \"Narco\"},\n {\"id\": 13, \"name\": \"Bombasto\"},\n {\"id\": 14, \"name\": \"Celeritas\"},\n {\"id\": 15, \"name\": \"Magneta\"},\n {\"id\": 16, \"name\": \"RubberMan\"},\n {\"id\": 17, \"name\": \"Dynama\"},\n {\"id\": 18, \"name\": \"Dr IQ\"},\n {\"id\": 19, \"name\": \"Magma\"},\n {\"id\": 20, \"name\": \"Tornado\"}\n ];",
'default-exporter.ts': "\n let a: string;\n export default a;\n ",
'hero.service.ts': "\n import {Injectable} from 'angular2/core';\n import {HEROES} from './mock-heroes';\n import {Hero} from './hero';\n\n @Injectable()\n class HeroService {\n getHeros() {\n return Promise.resolve(HEROES);\n }\n\n getHeroesSlowly() {\n return new Promise(resolve =>\n setTimeout(()=>resolve(HEROES), 2000)); // 2 seconds\n }\n }\n export default HeroService;",
'cases-data.ts': "\n import {Injectable, Input} from 'angular2/core';\n\n @Injectable()\n export class CaseAny {\n constructor(param: any) {}\n }\n\n @Injectable()\n export class GetProp {\n private _name: string;\n @Input('firstName') get name(): string {\n return this._name;\n }\n }\n\n @Injectable()\n export class SetProp {\n private _name: string;\n @Input('firstName') set name(value: string) {\n this._name = value;\n }\n }\n\n @Injectable()\n export class FullProp {\n private _name: string;\n @Input('firstName') get name(): string {\n return this._name;\n }\n set name(value: string) {\n this._name = value;\n }\n }\n\n export class ClassReference { }\n export class NgForRow {\n\n }\n\n @Injectable()\n export class NgFor {\n constructor (public ref: ClassReference) {}\n }\n ",
'error-cases.ts': "\n import HeroService from './hero.service';\n\n export class CaseCtor {\n constructor(private _heroService: HeroService) { }\n }\n "
},
'promise.ts': "\n interface PromiseLike {\n then(onfulfilled?: (value: T) => TResult | PromiseLike, onrejected?: (reason: any) => TResult | PromiseLike): PromiseLike;\n then(onfulfilled?: (value: T) => TResult | PromiseLike, onrejected?: (reason: any) => void): PromiseLike;\n }\n\n interface Promise {\n then(onfulfilled?: (value: T) => TResult | PromiseLike, onrejected?: (reason: any) => TResult | PromiseLike): Promise;\n then(onfulfilled?: (value: T) => TResult | PromiseLike, onrejected?: (reason: any) => void): Promise;\n catch(onrejected?: (reason: any) => T | PromiseLike): Promise;\n catch(onrejected?: (reason: any) => void): Promise;\n }\n\n interface PromiseConstructor {\n prototype: Promise;\n new (executor: (resolve: (value?: T | PromiseLike) => void, reject: (reason?: any) => void) => void): Promise;\n reject(reason: any): Promise;\n reject(reason: any): Promise;\n resolve(value: T | PromiseLike): Promise;\n resolve(): Promise;\n }\n\n declare var Promise: PromiseConstructor;\n ",
'class-arity.ts': "\n export class Zero {}\n export class One {}\n export class Two {}\n export class Three {}\n export class Nine {}\n ",
'unsupported-1.ts': "\n export let {a, b} = {a: 1, b: 2};\n export let [c, d] = [1, 2];\n export let e;\n ",
'unsupported-2.ts': "\n import {Injectable} from 'angular2/core';\n\n class Foo {}\n\n @Injectable()\n export class Bar {\n constructor(private f: Foo) {}\n }\n ",
'unsupported-3.ts': "\n class Foo {}\n\n export class SomeClass {\n static someStatic() {\n return Foo;\n }\n }\n ",
'interface-reference.ts': "\n import {Injectable, Inject} from 'angular2/core';\n export interface Test {}\n\n @Injectable()\n export class SomeClass {\n constructor(@Inject(\"a\") test: Test) {}\n }\n ",
'import-star.ts': "\n import {Injectable} from 'angular2/core';\n import * as common from 'angular2/common';\n\n @Injectable()\n export class SomeClass {\n constructor(private f: common.NgFor) {}\n }\n ",
'exported-classes.ts': "\n export class SimpleClass {}\n export abstract class AbstractClass {}\n export declare class DeclaredClass {}\n ",
'class-inheritance-parent.ts': "\n export class ParentClassFromOtherFile {}\n ",
'class-inheritance.ts': "\n import {ParentClassFromOtherFile} from './class-inheritance-parent';\n\n export class ParentClass {}\n\n export declare class DeclaredChildClass extends ParentClass {}\n\n export class ChildClassSameFile extends ParentClass {}\n\n export class ChildClassOtherFile extends ParentClassFromOtherFile {}\n ",
'exported-functions.ts': "\n export function one(a: string, b: string, c: string) {\n return {a: a, b: b, c: c};\n }\n export function two(a: string, b: string, c: string) {\n return {a, b, c};\n }\n export function three({a, b, c}: {a: string, b: string, c: string}) {\n return [a, b, c];\n }\n export function supportsState(): boolean {\n return !!window.history.pushState;\n }\n export function complexFn(x: any): boolean {\n if (x) {\n return true;\n } else {\n return false;\n }\n }\n export declare function declaredFn();\n ",
'exported-enum.ts': "\n import {constValue} from './exported-consts';\n\n export const someValue = 30;\n export enum SomeEnum { A, B, C = 100, D };\n export enum ComplexEnum { A, B, C = someValue, D = someValue + 10, E = constValue };\n ",
'exported-consts.ts': "\n export const constValue = 100;\n ",
'static-method.ts': "\n export class MyModule {\n static with(comp: any): any[] {\n return [\n MyModule,\n { provider: 'a', useValue: comp }\n ];\n }\n }\n ",
'static-method-with-default.ts': "\n export class MyModule {\n static with(comp: any, foo: boolean = true, bar: boolean = false): any[] {\n return [\n MyModule,\n foo ? { provider: 'a', useValue: comp } : {provider: 'b', useValue: comp},\n bar ? { provider: 'c', useValue: comp } : {provider: 'd', useValue: comp}\n ];\n }\n }\n ",
'static-method-call.ts': "\n import {Component} from 'angular2/core';\n import {MyModule} from './static-method';\n\n @Component({\n providers: MyModule.with('a')\n })\n export class Foo { }\n ",
'static-field.ts': "\n export class MyModule {\n static VALUE = 'Some string';\n }\n ",
'static-field-reference.ts': "\n import {Component} from 'angular2/core';\n import {MyModule} from './static-field';\n\n @Component({\n providers: [ { provide: 'a', useValue: MyModule.VALUE } ]\n })\n export class Foo { }\n ",
'static-method-with-if.ts': "\n export class MyModule {\n static with(cond: boolean): any[] {\n return [\n MyModule,\n { provider: 'a', useValue: cond ? '1' : '2' }\n ];\n }\n }\n ",
're-exports.ts': "\n export {MyModule} from './static-field';\n export {Foo as OtherModule} from './static-field-reference';\n export * from 'angular2/core';\n ",
're-exports-2.ts': "\n import {MyModule} from './static-field';\n import {Foo as OtherModule} from './static-field-reference';\n class MyClass {}\n export {OtherModule, MyModule as MyOtherModule, MyClass};\n ",
'export-as.d.ts': "\n declare function someFunction(): void;\n export { someFunction as SomeFunction };\n ",
'local-symbol-ref.ts': "\n import {Component, Validators} from 'angular2/core';\n\n var REQUIRED;\n\n export const REQUIRED_VALIDATOR: any = {\n provide: 'SomeToken',\n useValue: REQUIRED,\n multi: true\n };\n\n @Component({\n providers: [REQUIRED_VALIDATOR]\n })\n export class SomeComponent {}\n ",
'private-enum.ts': "\n export enum PublicEnum { a, b, c }\n enum PrivateEnum { e, f, g }\n ",
'local-function-ref.ts': "\n import {Component, Validators} from 'angular2/core';\n\n function required() {}\n\n export const REQUIRED_VALIDATOR: any = {\n provide: 'SomeToken',\n useValue: required,\n multi: true\n };\n\n @Component({\n providers: [REQUIRED_VALIDATOR]\n })\n export class SomeComponent {}\n ",
'local-symbol-ref-func.ts': "\n var localSymbol: any[];\n\n export function foo(index: number): string {\n return localSymbol[index];\n }\n ",
'node_modules': {
'angular2': {
'core.d.ts': "\n export interface Type extends Function { }\n export interface TypeDecorator {\n (type: T): T;\n (target: Object, propertyKey?: string | symbol, parameterIndex?: number): void;\n annotations: any[];\n }\n export interface ComponentDecorator extends TypeDecorator { }\n export interface ComponentFactory {\n (obj: {\n selector?: string;\n inputs?: string[];\n outputs?: string[];\n properties?: string[];\n events?: string[];\n host?: {\n [key: string]: string;\n };\n bindings?: any[];\n providers?: any[];\n exportAs?: string;\n moduleId?: string;\n queries?: {\n [key: string]: any;\n };\n viewBindings?: any[];\n viewProviders?: any[];\n templateUrl?: string;\n template?: string;\n styleUrls?: string[];\n styles?: string[];\n directives?: Array;\n pipes?: Array;\n }): ComponentDecorator;\n }\n export declare var Component: ComponentFactory;\n export interface InputFactory {\n (bindingPropertyName?: string): any;\n new (bindingPropertyName?: string): any;\n }\n export declare var Input: InputFactory;\n export interface InjectableFactory {\n (): any;\n }\n export declare var Injectable: InjectableFactory;\n export interface InjectFactory {\n (binding?: any): any;\n new (binding?: any): any;\n }\n export declare var Inject: InjectFactory;\n export interface OnInit {\n ngOnInit(): any;\n }\n export class Validators {\n static required(): void;\n }\n ",
'common.d.ts': "\n export declare class NgFor {\n ngForOf: any;\n ngForTemplate: any;\n ngDoCheck(): void;\n }\n export declare class LowerCasePipe {\n transform(value: string, args?: any[]): string;\n }\n export declare class UpperCasePipe {\n transform(value: string, args?: any[]): string;\n }\n "
}
}
};
//# sourceMappingURL=collector.spec.js.map