angular/packages/compiler-cli/test/compliance/mock_compiler_spec.ts
Alex Rickabaugh eb999300d9 test(ivy): run compiler compliance tests without rebuilding core,common (#25248)
Previously the compiler compliance tests ran and built test code with
real dependencies on @angular/core and @angular/common. This meant that
any changes to the compiler would result in long rebuild processes
for tests to rerun.

This change removes those dependencies and causes test code to be built
against the fake_core stub of @angular/core that the ngtsc tests use.
This change also removes the dependency on @angular/common entirely, as
locality means it's possible to reference *ngIf without needing to link
to an implementation.

PR Close #25248
2018-08-03 13:08:51 -07:00

217 lines
7.1 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 {MockDirectory, setup} from '@angular/compiler/test/aot/test_util';
import {compile, expectEmit} from './mock_compile';
describe('mock_compiler', () => {
// This produces a MockDirectory of the file needed to compile an Angular application.
// This setup is performed in a beforeAll which populates the map returned.
const angularFiles = setup({
compileAngular: false,
compileFakeCore: true,
compileAnimations: false,
});
describe('compiling', () => {
// To use compile you need to supply the files in a MockDirectory that can be merged
// with a set of "environment" files such as the angular files.
it('should be able to compile a simple application', () => {
const files = {
app: {
'hello.component.ts': `
import {Component, Input} from '@angular/core';
@Component({template: 'Hello {{name}}!'})
export class HelloComponent {
@Input() name: string = 'world';
}
`,
'hello.module.ts': `
import {NgModule} from '@angular/core';
import {HelloComponent} from './hello.component';
@NgModule({declarations: [HelloComponent]})
export class HelloModule {}
`
}
};
const result = compile(files, angularFiles);
// result.source contains just the emitted factory declarations regardless of the original
// module.
expect(result.source).toContain('Hello');
});
});
describe('expecting emitted output', () => {
it('should be able to find a simple expression in the output', () => {
const files = {
app: {
'hello.component.ts': `
import {Component, Input} from '@angular/core';
@Component({template: 'Hello {{name}}! Your name as {{name.length}} characters'})
export class HelloComponent {
@Input() name: string = 'world';
}
`,
'hello.module.ts': `
import {NgModule} from '@angular/core';
import {HelloComponent} from './hello.component';
@NgModule({declarations: [HelloComponent]})
export class HelloModule {}
`
}
};
const result = compile(files, angularFiles);
// The expression can expected directly.
expectEmit(result.source, 'name.length', 'name length expression not found');
// Whitespace is not significant
expectEmit(
result.source, 'name \n\n . \n length',
'name length expression not found (whitespace)');
});
});
it('should be able to skip untested regions (… and // ...)', () => {
const files = {
app: {
'hello.component.ts': `
import {Component, Input} from '@angular/core';
@Component({template: 'Hello {{name}}! Your name as {{name.length}} characters'})
export class HelloComponent {
@Input() name: string = 'world';
}
`,
'hello.module.ts': `
import {NgModule} from '@angular/core';
import {HelloComponent} from './hello.component';
@NgModule({declarations: [HelloComponent]})
export class HelloModule {}
`
}
};
const result = compile(files, angularFiles);
// The special character … means anything can be generated between the two sections allowing
// skipping sections of the output that are not under test. The ellipsis unicode char (…) is
// used instead of '...' because '...' is legal JavaScript (the spread operator) and might
// need to be tested. `// ...` could also be used in place of `…`.
expectEmit(result.source, 'ctx.name … ctx.name.length', 'could not find correct length access');
expectEmit(
result.source, 'ctx.name // ... ctx.name.length', 'could not find correct length access');
});
it('should be able to skip TODO comments (// TODO)', () => {
const files = {
app: {
'hello.component.ts': `
import {Component, Input} from '@angular/core';
@Component({template: 'Hello!'})
export class HelloComponent { }
`,
'hello.module.ts': `
import {NgModule} from '@angular/core';
import {HelloComponent} from './hello.component';
@NgModule({declarations: [HelloComponent]})
export class HelloModule {}
`
}
};
const result = compile(files, angularFiles);
expectEmit(
result.source, `
// TODO: this comment should not be taken into account
$r3$.ɵT(0, "Hello!");
// TODO: this comment should not be taken into account
`,
'todo comments should be ignored');
});
it('should be able to enforce consistent identifiers', () => {
const files = {
app: {
'hello.component.ts': `
import {Component, Input} from '@angular/core';
@Component({template: 'Hello {{name}}! Your name as {{name.length}} characters'})
export class HelloComponent {
@Input() name: string = 'world';
}
`,
'hello.module.ts': `
import {NgModule} from '@angular/core';
import {HelloComponent} from './hello.component';
@NgModule({declarations: [HelloComponent]})
export class HelloModule {}
`
}
};
const result = compile(files, angularFiles);
// IDENT can be used a wild card for any identifier
expectEmit(result.source, 'IDENT.name', 'could not find context access');
// $<ident>$ can be used as a wild-card but all the content matched by the identifiers must
// match each other.
// This is useful if the code generator is free to invent a name but should use the name
// consistently.
expectEmit(
result.source, '$ctx$.$name$ … $ctx$.$name$.length',
'could not find correct length access');
});
it('should be able to enforce that identifiers match a regexp', () => {
const files = {
app: {
'hello.component.ts': `
import {Component, Input} from '@angular/core';
@Component({template: 'Hello {{name}}! Your name as {{name.length}} characters'})
export class HelloComponent {
@Input() name: string = 'world';
}
`,
'hello.module.ts': `
import {NgModule} from '@angular/core';
import {HelloComponent} from './hello.component';
@NgModule({declarations: [HelloComponent]})
export class HelloModule {}
`
}
};
const result = compile(files, angularFiles);
// Pass: `$n$` ends with `ME` in the generated code
expectEmit(result.source, '$ctx$.$n$ … $ctx$.$n$.length', 'Match names', {'$n$': /ME$/i});
// Fail: `$n$` does not match `/(not)_(\1)/` in the generated code
expect(() => {
expectEmit(
result.source, '$ctx$.$n$ … $ctx$.$n$.length', 'Match names', {'$n$': /(not)_(\1)/});
}).toThrowError(/"\$n\$" is "name" which doesn't match \/\(not\)_\(\\1\)\//);
});
});