|
| 1 | +import { CompileMetadataResolver } from "@angular/compiler"; |
| 2 | +import { TestBed } from "@angular/core/testing"; |
| 3 | +import { |
| 4 | + BrowserDynamicTestingModule, |
| 5 | + platformBrowserDynamicTesting, |
| 6 | +} from "@angular/platform-browser-dynamic/testing"; |
| 7 | + |
| 8 | +/** |
| 9 | + * Use this to speed up your angular test suite. Normally when you call `TestBed.compileComponents()`, it recompiles all the configured components even if it has compiled them before in a previous test. AoT compilation would be a great fix, but is not well supported/documented for tests yet. This function fills the gap by precompiling the components as if by AoT and setting up `TestBed` to use them. |
| 10 | + * |
| 11 | + * ```ts |
| 12 | + * // let's assume `AppModule` declares or imports a `HelloWorldComponent` |
| 13 | + * precompileForTests([AppModule]); |
| 14 | + * |
| 15 | + * describe("AppComponent", () => { |
| 16 | + * it("says hello", async () => { |
| 17 | + * TestBed.configureTestingModule({ declarations: [HelloWorldComponent] }); |
| 18 | + * await TestBed.compileComponents(); // <- this line is faster |
| 19 | + * const fixture = TestBed.createComponent(HelloWorldComponent); |
| 20 | + * expect(fixture.nativeElement.textContent).toContain("Hello, world!"); |
| 21 | + * }); |
| 22 | + * }); |
| 23 | + * ``` |
| 24 | + * |
| 25 | + * Note that this uses `TestBed.initTestEnvironment` to set up the precompiled components, so you will not be able to use it yourself at the same time. |
| 26 | + */ |
| 27 | +export function precompileForTests(modules: any[], skipModules: any[] = []) { |
| 28 | + beforeAll(async () => { |
| 29 | + TestBed.configureTestingModule({ imports: modules }); |
| 30 | + await TestBed.compileComponents(); |
| 31 | + |
| 32 | + // technique modeled from https://github.com/angular/angular/blob/11325bad4ab786a07e52ff380c00622fda11c0b7/packages/core/test/linker/jit_summaries_integration_spec.ts#L83 |
| 33 | + const metadataResolver = TestBed.get( |
| 34 | + CompileMetadataResolver, |
| 35 | + ) as CompileMetadataResolver; |
| 36 | + |
| 37 | + TestBed.resetTestEnvironment(); |
| 38 | + TestBed.initTestEnvironment( |
| 39 | + BrowserDynamicTestingModule, |
| 40 | + platformBrowserDynamicTesting(), |
| 41 | + () => |
| 42 | + extractAotSummaries(metadataResolver, modules, new Set(skipModules)), |
| 43 | + ); |
| 44 | + }); |
| 45 | +} |
| 46 | + |
| 47 | +function extractAotSummaries( |
| 48 | + metadataResolver: CompileMetadataResolver, |
| 49 | + modules: any[], |
| 50 | + skipModules: Set<any>, |
| 51 | +): any[] { |
| 52 | + return modules.map((module) => () => |
| 53 | + extractAotSummaries0(metadataResolver, module, skipModules), |
| 54 | + ); |
| 55 | +} |
| 56 | + |
| 57 | +function extractAotSummaries0( |
| 58 | + metadataResolver: CompileMetadataResolver, |
| 59 | + module: any, |
| 60 | + skipModules: Set<any>, |
| 61 | +) { |
| 62 | + if (skipModules.has(module)) { |
| 63 | + return []; |
| 64 | + } |
| 65 | + skipModules.add(module); |
| 66 | + |
| 67 | + const moduleMetadata = metadataResolver.getNgModuleMetadata(module)!; |
| 68 | + return [ |
| 69 | + metadataResolver.getNgModuleSummary(module), |
| 70 | + () => |
| 71 | + moduleMetadata.declaredDirectives.map((directive) => |
| 72 | + metadataResolver.getDirectiveSummary(directive.reference), |
| 73 | + ), |
| 74 | + () => |
| 75 | + moduleMetadata.declaredPipes.map((pipe) => |
| 76 | + metadataResolver.getPipeSummary(pipe.reference), |
| 77 | + ), |
| 78 | + () => |
| 79 | + extractAotSummaries( |
| 80 | + metadataResolver, |
| 81 | + moduleMetadata.importedModules.map( |
| 82 | + (importedModule) => importedModule.type.reference, |
| 83 | + ), |
| 84 | + skipModules, |
| 85 | + ), |
| 86 | + ]; |
| 87 | +} |
0 commit comments