From de8f5a7a561e3b747f6048fcc512c1e3211dcf3a Mon Sep 17 00:00:00 2001 From: Karan Mistry Date: Thu, 15 Jan 2026 14:33:03 +0530 Subject: [PATCH] fix(@angular/build): add @angular/compiler to ensure the JIT compiler is available specifically for Vitest tests that use providersFile. Fixes #31993 --- .../build/src/builders/unit-test/options.ts | 16 +++++- .../unit-test/runners/vitest/build-options.ts | 7 ++- .../tests/options/providers-file_spec.ts | 56 +++++++++++++++++++ 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/packages/angular/build/src/builders/unit-test/options.ts b/packages/angular/build/src/builders/unit-test/options.ts index 7f8f8db182fe..0c018dbbf85c 100644 --- a/packages/angular/build/src/builders/unit-test/options.ts +++ b/packages/angular/build/src/builders/unit-test/options.ts @@ -135,6 +135,18 @@ export async function normalizeOptions( }; } -export function injectTestingPolyfills(polyfills: string[] = []): string[] { - return polyfills.includes('zone.js') ? [...polyfills, 'zone.js/testing'] : polyfills; +export function injectTestingPolyfills( + polyfills: string[] = [], + runner?: Runner, + hasProvidersFile?: boolean, +): string[] { + const testPolyfills = polyfills.includes('zone.js') + ? [...polyfills, 'zone.js/testing'] + : polyfills; + + if (runner === Runner.Vitest && hasProvidersFile) { + testPolyfills.push('@angular/compiler'); + } + + return testPolyfills; } diff --git a/packages/angular/build/src/builders/unit-test/runners/vitest/build-options.ts b/packages/angular/build/src/builders/unit-test/runners/vitest/build-options.ts index 7129ea4fff54..8ee5654ad3a8 100644 --- a/packages/angular/build/src/builders/unit-test/runners/vitest/build-options.ts +++ b/packages/angular/build/src/builders/unit-test/runners/vitest/build-options.ts @@ -11,6 +11,7 @@ import { toPosixPath } from '../../../../utils/path'; import type { ApplicationBuilderInternalOptions } from '../../../application/options'; import { OutputHashing } from '../../../application/schema'; import { NormalizedUnitTestBuilderOptions, injectTestingPolyfills } from '../../options'; +import { Runner } from '../../schema'; import { findTests, getTestEntrypoints } from '../../test-discovery'; import { RunnerOptions } from '../api'; @@ -130,7 +131,11 @@ export async function getVitestBuildOptions( externalDependencies, }; - buildOptions.polyfills = injectTestingPolyfills(buildOptions.polyfills); + buildOptions.polyfills = injectTestingPolyfills( + buildOptions.polyfills, + Runner.Vitest, + !!providersFile, + ); const testBedInitContents = createTestBedInitVirtualFile( providersFile, diff --git a/packages/angular/build/src/builders/unit-test/tests/options/providers-file_spec.ts b/packages/angular/build/src/builders/unit-test/tests/options/providers-file_spec.ts index d69f6480c54d..15253858d767 100644 --- a/packages/angular/build/src/builders/unit-test/tests/options/providers-file_spec.ts +++ b/packages/angular/build/src/builders/unit-test/tests/options/providers-file_spec.ts @@ -60,5 +60,61 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => { const { result } = await harness.executeOnce(); expect(result?.success).toBeTrue(); }); + + it('should work with providers that inject services requiring JIT compilation', async () => { + // This test reproduces the issue from https://github.com/angular/angular-cli/issues/31993 + // where using providersFile with a service that injects Router causes JIT compilation errors + await harness.writeFiles({ + 'src/test.service.ts': ` + import { Injectable, inject } from '@angular/core'; + import { Router } from '@angular/router'; + + @Injectable({ providedIn: 'root' }) + export class TestService { + router = inject(Router); + } + `, + 'src/test.providers.ts': ` + import { TestService } from './test.service'; + export default [TestService]; + `, + 'src/app/app.component.spec.ts': ` + import { TestBed } from '@angular/core/testing'; + import { AppComponent } from './app.component'; + import { TestService } from '../test.service'; + import { describe, expect, it } from 'vitest'; + + describe('AppComponent', () => { + it('should create the app and inject TestService', () => { + TestBed.configureTestingModule({ + declarations: [AppComponent], + }); + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + const testService = TestBed.inject(TestService); + expect(app).toBeTruthy(); + expect(testService).toBeTruthy(); + expect(testService.router).toBeTruthy(); + }); + }); + `, + }); + await harness.modifyFile('src/tsconfig.spec.json', (content) => { + const tsConfig = JSON.parse(content); + tsConfig.files ??= []; + tsConfig.files.push('test.service.ts', 'test.providers.ts'); + + return JSON.stringify(tsConfig); + }); + + harness.useTarget('test', { + ...BASE_OPTIONS, + providersFile: 'src/test.providers.ts', + }); + + const { result } = await harness.executeOnce(); + + expect(result?.success).toBeTrue(); + }); }); });