Skip to content

Commit ff9ce8c

Browse files
committed
fix(@angular/build): ensure @angular/compiler is loaded for Vitest TestBed initialization
Explicitly import '@angular/compiler' at the top of the Vitest TestBed initialization virtual file. This ensures the compiler facade is published before any modules or providers (such as those configured via 'providersFile') are evaluated, avoiding JIT compilation errors when injectable dependencies are resolved during test initialization.
1 parent 999d84c commit ff9ce8c

2 files changed

Lines changed: 55 additions & 0 deletions

File tree

‎packages/angular/build/src/builders/unit-test/runners/vitest/build-options.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ function createTestBedInitVirtualFile(
4747
// when running Vitest in non-isolated mode with JSDOM. It looks up the
4848
// document dynamically on every operation instead of caching it.
4949
return `
50+
import '@angular/compiler';
5051
${hasLocalize ? "import '@angular/localize/init';" : ''}
5152
// Initialize the Angular testing environment
5253
import { NgModule, provideZoneChangeDetection } from '@angular/core';

‎packages/angular/build/src/builders/unit-test/tests/options/providers-file_spec.ts‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,59 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
6060
const { result } = await harness.executeOnce();
6161
expect(result?.success).toBeTrue();
6262
});
63+
64+
it('should work with providers that inject services requiring JIT compilation', async () => {
65+
await harness.writeFiles({
66+
'src/test.service.ts': `
67+
import { Injectable, inject } from '@angular/core';
68+
import { Router } from '@angular/router';
69+
70+
@Injectable({ providedIn: 'root' })
71+
export class TestService {
72+
router = inject(Router);
73+
}
74+
`,
75+
'src/test.providers.ts': `
76+
import { TestService } from './test.service';
77+
export default [TestService];
78+
`,
79+
'src/app/app.component.spec.ts': `
80+
import { TestBed } from '@angular/core/testing';
81+
import { AppComponent } from './app.component';
82+
import { TestService } from '../test.service';
83+
import { describe, expect, it } from 'vitest';
84+
85+
describe('AppComponent', () => {
86+
it('should create the app and inject TestService', () => {
87+
TestBed.configureTestingModule({
88+
declarations: [AppComponent],
89+
});
90+
const fixture = TestBed.createComponent(AppComponent);
91+
const app = fixture.componentInstance;
92+
const testService = TestBed.inject(TestService);
93+
expect(app).toBeTruthy();
94+
expect(testService).toBeTruthy();
95+
expect(testService.router).toBeTruthy();
96+
});
97+
});
98+
`,
99+
});
100+
101+
await harness.modifyFile('src/tsconfig.spec.json', (content) => {
102+
const tsConfig = JSON.parse(content);
103+
tsConfig.files ??= [];
104+
tsConfig.files.push('test.service.ts', 'test.providers.ts');
105+
106+
return JSON.stringify(tsConfig);
107+
});
108+
109+
harness.useTarget('test', {
110+
...BASE_OPTIONS,
111+
providersFile: 'src/test.providers.ts',
112+
});
113+
114+
const { result } = await harness.executeOnce();
115+
expect(result?.success).toBeTrue();
116+
});
63117
});
64118
});

0 commit comments

Comments
 (0)