Currently the `@angular/compiler-cli` compliance tests sometimes do
not throw an exception if the expected output does not match the
generated JavaScript output. This can happen for the following cases:
1. Expected code includes character that is not part of known alphabet
    (e.g. `Δ` is still used in a new compliance test after rebasing a PR)
2. Expected code asserts that a string literal matches a string with
    escaped quotes. e.g. expects `const $var$ = "\"quoted\"";`)
PR Close #30597
		
	
			
		
			
				
	
	
		
			246 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			246 lines
		
	
	
		
			7.9 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 {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 throw if the expected output contains unknown characters', () => {
 | |
|       const files = {
 | |
|         app: {
 | |
|           'test.ts': `ɵsayHello();`,
 | |
|         }
 | |
|       };
 | |
| 
 | |
|       const result = compile(files, angularFiles);
 | |
| 
 | |
|       expect(() => {
 | |
|         expectEmit(result.source, `ΔsayHello();`, 'Output does not match.');
 | |
|       }).toThrowError(/Invalid test, no token found for "Δ"/);
 | |
|     });
 | |
| 
 | |
|     it('should be able to properly handle string literals with escaped quote', () => {
 | |
|       const files = {
 | |
|         app: {
 | |
|           'test.ts': String.raw `const identifier = "\"quoted\"";`,
 | |
|         }
 | |
|       };
 | |
| 
 | |
|       const result = compile(files, angularFiles);
 | |
| 
 | |
|       expect(() => {
 | |
|         expectEmit(result.source, String.raw `const $a$ = "\"quoted\"";`, 'Output does not match.');
 | |
|       }).not.toThrow();
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   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$.ɵɵtext(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\)\//);
 | |
|   });
 | |
| });
 |