299 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
		
		
			
		
	
	
			299 lines
		
	
	
		
			8.4 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 {runInEachFileSystem} from '../../src/ngtsc/file_system/testing'; | ||
|  | import {loadStandardTestFiles} from '../helpers/src/mock_file_loading'; | ||
|  | 
 | ||
|  | import {NgtscTestEnvironment} from './env'; | ||
|  | 
 | ||
|  | const trim = (input: string): string => input.replace(/\s+/g, ' ').trim(); | ||
|  | 
 | ||
|  | const testFiles = loadStandardTestFiles(); | ||
|  | 
 | ||
|  | runInEachFileSystem(() => { | ||
|  |   describe('ModuleWithProviders generic type transform', () => { | ||
|  |     let env !: NgtscTestEnvironment; | ||
|  | 
 | ||
|  |     beforeEach(() => { | ||
|  |       env = NgtscTestEnvironment.setup(testFiles); | ||
|  |       env.tsconfig(); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('should add a generic type for static methods on exported classes', () => { | ||
|  |       env.write('test.ts', `
 | ||
|  |         import {NgModule} from '@angular/core'; | ||
|  | 
 | ||
|  |         @NgModule() | ||
|  |         export class TestModule { | ||
|  |           static forRoot() { | ||
|  |             return { | ||
|  |               ngModule: TestModule, | ||
|  |             }; | ||
|  |           } | ||
|  |         } | ||
|  |       `);
 | ||
|  | 
 | ||
|  |       env.driveMain(); | ||
|  | 
 | ||
|  |       const dtsContents = trim(env.getContents('test.d.ts')); | ||
|  |       expect(dtsContents).toContain('import * as i0 from "@angular/core";'); | ||
|  |       expect(dtsContents).toContain('static forRoot(): i0.ModuleWithProviders<TestModule>;'); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('should not add a generic type for non-static methods', () => { | ||
|  |       env.write('test.ts', `
 | ||
|  |         import {NgModule} from '@angular/core'; | ||
|  | 
 | ||
|  |         @NgModule() | ||
|  |         export class TestModule { | ||
|  |           forRoot() { | ||
|  |             return { | ||
|  |               ngModule: TestModule, | ||
|  |             }; | ||
|  |           } | ||
|  |         } | ||
|  |       `);
 | ||
|  | 
 | ||
|  |       env.driveMain(); | ||
|  | 
 | ||
|  |       const dtsContents = trim(env.getContents('test.d.ts')); | ||
|  |       expect(dtsContents).toContain('import * as i0 from "@angular/core";'); | ||
|  |       expect(dtsContents).toContain('forRoot(): { ngModule: typeof TestModule; };'); | ||
|  |       expect(dtsContents).not.toContain('static forRoot()'); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('should add a generic type for exported functions', () => { | ||
|  |       env.write('test.ts', `
 | ||
|  |         import {NgModule} from '@angular/core'; | ||
|  | 
 | ||
|  |         export function forRoot() { | ||
|  |           return { | ||
|  |             ngModule: TestModule, | ||
|  |           }; | ||
|  |         } | ||
|  | 
 | ||
|  |         @NgModule() | ||
|  |         export class TestModule {} | ||
|  |       `);
 | ||
|  | 
 | ||
|  |       env.driveMain(); | ||
|  | 
 | ||
|  |       const dtsContents = trim(env.getContents('test.d.ts')); | ||
|  |       expect(dtsContents).toContain('import * as i0 from "@angular/core";'); | ||
|  |       expect(dtsContents) | ||
|  |           .toContain('export declare function forRoot(): i0.ModuleWithProviders<TestModule>;'); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('should not add a generic type when already present', () => { | ||
|  |       env.write('test.ts', `
 | ||
|  |         import {NgModule, ModuleWithProviders} from '@angular/core'; | ||
|  | 
 | ||
|  |         export class TestModule { | ||
|  |           forRoot(): ModuleWithProviders<InternalTestModule> { | ||
|  |             return { | ||
|  |               ngModule: TestModule, | ||
|  |             }; | ||
|  |           } | ||
|  |         } | ||
|  | 
 | ||
|  |         @NgModule() | ||
|  |         export class InternalTestModule {} | ||
|  |       `);
 | ||
|  | 
 | ||
|  |       env.driveMain(); | ||
|  | 
 | ||
|  |       const dtsContents = trim(env.getContents('test.d.ts')); | ||
|  |       expect(dtsContents).toContain('forRoot(): ModuleWithProviders<InternalTestModule>;'); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('should add a generic type when missing the generic type parameter', () => { | ||
|  |       env.write('test.ts', `
 | ||
|  |         import {NgModule, ModuleWithProviders} from '@angular/core'; | ||
|  | 
 | ||
|  |         @NgModule() | ||
|  |         export class TestModule { | ||
|  |           static forRoot(): ModuleWithProviders { | ||
|  |             return { | ||
|  |               ngModule: TestModule, | ||
|  |             }; | ||
|  |           } | ||
|  |         } | ||
|  |       `);
 | ||
|  | 
 | ||
|  |       env.driveMain(); | ||
|  | 
 | ||
|  |       const dtsContents = trim(env.getContents('test.d.ts')); | ||
|  |       expect(dtsContents).toContain('static forRoot(): i0.ModuleWithProviders<TestModule>;'); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('should add a generic type when missing the generic type parameter (qualified name)', () => { | ||
|  |       env.write('test.ts', `
 | ||
|  |         import * as ng from '@angular/core'; | ||
|  | 
 | ||
|  |         @ng.NgModule() | ||
|  |         export class TestModule { | ||
|  |           static forRoot(): ng.ModuleWithProviders { | ||
|  |             return { | ||
|  |               ngModule: TestModule, | ||
|  |             }; | ||
|  |           } | ||
|  |         } | ||
|  |       `);
 | ||
|  | 
 | ||
|  |       env.driveMain(); | ||
|  | 
 | ||
|  |       const dtsContents = trim(env.getContents('test.d.ts')); | ||
|  |       expect(dtsContents).toContain('static forRoot(): i0.ModuleWithProviders<TestModule>;'); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('should add a generic type and add an import for external references', () => { | ||
|  |       env.write('test.ts', `
 | ||
|  |         import {ModuleWithProviders} from '@angular/core'; | ||
|  |         import {InternalTestModule} from './internal'; | ||
|  | 
 | ||
|  |         export class TestModule { | ||
|  |           static forRoot(): ModuleWithProviders { | ||
|  |             return { | ||
|  |               ngModule: InternalTestModule, | ||
|  |             }; | ||
|  |           } | ||
|  |         } | ||
|  |       `);
 | ||
|  |       env.write('internal.ts', `
 | ||
|  |         import {NgModule} from '@angular/core'; | ||
|  | 
 | ||
|  |         @NgModule() | ||
|  |         export class InternalTestModule {} | ||
|  |       `);
 | ||
|  | 
 | ||
|  |       env.driveMain(); | ||
|  | 
 | ||
|  |       const dtsContents = trim(env.getContents('test.d.ts')); | ||
|  |       expect(dtsContents).toContain('import * as i1 from "./internal";'); | ||
|  |       expect(dtsContents) | ||
|  |           .toContain('static forRoot(): i0.ModuleWithProviders<i1.InternalTestModule>;'); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('should not add a generic type if the return type is not ModuleWithProviders', () => { | ||
|  |       env.write('test.ts', `
 | ||
|  |         import {NgModule} from '@angular/core'; | ||
|  | 
 | ||
|  |         @NgModule() | ||
|  |         export class TestModule { | ||
|  |           static forRoot(): { ngModule: typeof TestModule } { | ||
|  |             return { | ||
|  |               ngModule: TestModule, | ||
|  |             }; | ||
|  |           } | ||
|  |         } | ||
|  |       `);
 | ||
|  | 
 | ||
|  |       env.driveMain(); | ||
|  | 
 | ||
|  |       const dtsContents = trim(env.getContents('test.d.ts')); | ||
|  |       expect(dtsContents).toContain('static forRoot(): { ngModule: typeof TestModule; };'); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('should not add a generic type if the return type is not ModuleWithProviders from @angular/core', | ||
|  |        () => { | ||
|  |          env.write('test.ts', `
 | ||
|  |         import {NgModule} from '@angular/core'; | ||
|  |         import {ModuleWithProviders} from './mwp'; | ||
|  | 
 | ||
|  |         @NgModule() | ||
|  |         export class TestModule { | ||
|  |           static forRoot(): ModuleWithProviders { | ||
|  |             return { | ||
|  |               ngModule: TestModule, | ||
|  |             }; | ||
|  |           } | ||
|  |         } | ||
|  |       `);
 | ||
|  |          env.write('mwp.ts', `
 | ||
|  |       export type ModuleWithProviders = { ngModule: any }; | ||
|  |       `);
 | ||
|  | 
 | ||
|  |          env.driveMain(); | ||
|  | 
 | ||
|  |          const dtsContents = trim(env.getContents('test.d.ts')); | ||
|  |          expect(dtsContents).toContain('static forRoot(): ModuleWithProviders;'); | ||
|  |        }); | ||
|  | 
 | ||
|  |     it('should not add a generic type when the "ngModule" property is not a reference', () => { | ||
|  |       env.write('test.ts', `
 | ||
|  |         import {NgModule} from '@angular/core'; | ||
|  | 
 | ||
|  |         @NgModule() | ||
|  |         export class TestModule { | ||
|  |           static forRoot() { | ||
|  |             return { | ||
|  |               ngModule: 'test', | ||
|  |             }; | ||
|  |           } | ||
|  |         } | ||
|  |       `);
 | ||
|  | 
 | ||
|  |       env.driveMain(); | ||
|  | 
 | ||
|  |       const dtsContents = trim(env.getContents('test.d.ts')); | ||
|  |       expect(dtsContents).toContain('static forRoot(): { ngModule: string; };'); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('should not add a generic type when the class is not exported', () => { | ||
|  |       env.write('test.ts', `
 | ||
|  |         import {NgModule} from '@angular/core'; | ||
|  | 
 | ||
|  |         @NgModule() | ||
|  |         class TestModule { | ||
|  |           static forRoot() { | ||
|  |             return { | ||
|  |               ngModule: TestModule, | ||
|  |             }; | ||
|  |           } | ||
|  |         } | ||
|  |       `);
 | ||
|  | 
 | ||
|  |       env.driveMain(); | ||
|  | 
 | ||
|  |       // The TestModule class is not exported so doesn't even show up in the declaration file
 | ||
|  |       const dtsContents = trim(env.getContents('test.d.ts')); | ||
|  |       expect(dtsContents).not.toContain('static forRoot()'); | ||
|  |     }); | ||
|  | 
 | ||
|  |     it('should add a generic type only when ngModule/providers are present', () => { | ||
|  |       env.write('test.ts', `
 | ||
|  |         import {NgModule, ModuleWithProviders} from '@angular/core'; | ||
|  | 
 | ||
|  |         @NgModule() | ||
|  |         export class TestModule { | ||
|  |           static hasNgModuleAndProviders() { | ||
|  |             return { | ||
|  |               ngModule: TestModule, | ||
|  |               providers: [], | ||
|  |             }; | ||
|  |           } | ||
|  |           static hasNgModuleAndFoo() { | ||
|  |             return { | ||
|  |               ngModule: TestModule, | ||
|  |               foo: 'test', | ||
|  |             }; | ||
|  |           } | ||
|  |         } | ||
|  |       `);
 | ||
|  | 
 | ||
|  |       env.driveMain(); | ||
|  | 
 | ||
|  |       const dtsContents = trim(env.getContents('test.d.ts')); | ||
|  |       expect(dtsContents) | ||
|  |           .toContain('static hasNgModuleAndProviders(): i0.ModuleWithProviders<TestModule>;'); | ||
|  |       expect(dtsContents) | ||
|  |           .toContain('static hasNgModuleAndFoo(): { ngModule: typeof TestModule; foo: string; };'); | ||
|  |     }); | ||
|  |   }); | ||
|  | }); |