Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions modules/testing/builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
"@angular-devkit/build-angular": "workspace:*",
"@angular-devkit/core": "workspace:*",
"@angular/ssr": "workspace:*",
"@vitest/coverage-v8": "4.1.11",
"@vitest/coverage-v8": "5.0.0",
"browser-sync": "3.0.4",
"istanbul-lib-instrument": "6.0.3",
"jsdom": "30.0.1",
"ng-packagr": "22.2.0-next.5",
"rxjs": "7.8.2",
"vitest": "4.1.11"
"vitest": "5.0.0"
}
}
4 changes: 2 additions & 2 deletions packages/angular/build/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"postcss": "8.5.26",
"rollup": "4.63.1",
"rxjs": "7.8.2",
"vitest": "4.1.11"
"vitest": "5.0.0"
},
"peerDependencies": {
"@angular/compiler": "0.0.0-ANGULAR-FW-PEER-DEP",
Expand All @@ -80,7 +80,7 @@
"tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0",
"tslib": "^2.3.0",
"typescript": ">=6.0 <6.1",
"vitest": "^4.0.8"
"vitest": "^4.0.8 || ^5.0.0"
},
"peerDependenciesMeta": {
"@angular/core": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import type {
ResolvedConfig,
UserWorkspaceConfig,
Vite,
VitestPlugin,
VitestPluginContext,
} from 'vitest/node';
import { createBuildAssetsMiddleware } from '../../../../tools/vite/middlewares/assets-middleware';
Expand All @@ -32,8 +31,6 @@ interface ExistingRawSourceMap {
mappings?: string;
}

type VitestPlugins = Awaited<ReturnType<typeof VitestPlugin>>;

interface PluginOptions {
workspaceRoot: string;
projectSourceRoot: string;
Expand Down Expand Up @@ -151,7 +148,7 @@ function getBrowsersToCheck(

export async function createVitestConfigPlugin(
options: VitestConfigPluginOptions,
): Promise<VitestPlugins[0]> {
): Promise<Vite.Plugin> {
const {
include,
browser,
Expand Down Expand Up @@ -248,6 +245,7 @@ export async function createVitestConfigPlugin(
const projectResolver = createProjectResolver(projectSourceRoot);

const projectDefaults: Vite.UserConfig & UserWorkspaceConfig = {
extends: false,
test: {
setupFiles,
globals: true,
Expand All @@ -268,6 +266,7 @@ export async function createVitestConfigPlugin(

const { optimizeDeps, resolve } = config;
const projectOverrides: Vite.UserConfig & UserWorkspaceConfig = {
extends: false,
test: {
name: projectName,
include,
Expand Down Expand Up @@ -313,7 +312,7 @@ async function loadResultFile(file: ResultFile): Promise<string> {
return readFile(file.inputPath, 'utf-8');
}

export function createVitestPlugins(pluginOptions: PluginOptions): VitestPlugins {
export function createVitestPlugins(pluginOptions: PluginOptions): Vite.Plugin[] {
const { workspaceRoot, buildResultFiles, testFileToEntryPoint } = pluginOptions;
const isWindows = platform() === 'win32';
let vitestConfig: ResolvedConfig;
Expand Down
5 changes: 3 additions & 2 deletions packages/angular/build/src/builders/unit-test/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
},
"runnerConfig": {
"type": ["string", "boolean"],
"description": "Specifies the configuration file for the selected test runner. If a string is provided, it will be used as the path to the configuration file. If `true`, the builder will search for a default configuration file (e.g., `vitest-base.config.ts` or `karma.conf.js`). If `false`, no external configuration file will be used.\\nFor Vitest, this enables advanced options and the use of custom plugins. Please note that while the file is loaded, the Angular team does not provide direct support for its specific contents or any third-party plugins used within it.",
"description": "Specifies the configuration file for the selected test runner. If a string is provided, it will be used as the path to the configuration file. If `true`, the builder will search for a default configuration file (e.g., `vitest-base.config.mts` or `karma.conf.js`). If `false`, no external configuration file will be used.\\nFor Vitest, this enables advanced options and the use of custom plugins. Please note that while the file is loaded, the Angular team does not provide direct support for its specific contents or any third-party plugins used within it.",
"default": false
},
"browsers": {
Expand Down Expand Up @@ -80,7 +80,8 @@
"splitting": {
"type": "boolean",
"description": "Enables code splitting for test execution. When enabled, shared code between test files is split into separate chunks. Issues with live ESM bindings in Node.js environments (such as uninitialized exports or mocking failures) can be resolved by disabling splitting. This option is only available for the Vitest runner.",
"default": true
"default": true,
"x-deprecated": "No longer needed with Vitest 5."
},
"quiet": {
"type": "boolean",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
it('should use custom reporters defined in runnerConfig file', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
});

harness.writeFile('vitest.config.ts', VITEST_CONFIG_CONTENT);
harness.writeFile('vitest.config.mts', VITEST_CONFIG_CONTENT);

const { result } = await harness.executeOnce();
expect(result?.success).toBeTrue();
Expand All @@ -45,11 +45,11 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
it('should use custom reporters and outputFile defined as an object in runnerConfig file', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
});

harness.writeFile(
'vitest.config.ts',
'vitest.config.mts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
Expand All @@ -71,12 +71,12 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
it('should preserve custom coverage options (e.g. clean: false) from runnerConfig file', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
coverage: true,
});

harness.writeFile(
'vitest.config.ts',
'vitest.config.mts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
Expand All @@ -102,11 +102,11 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
it('should override reporters defined in runnerConfig file when CLI option is present', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
reporters: ['default'],
});

harness.writeFile('vitest.config.ts', VITEST_CONFIG_CONTENT);
harness.writeFile('vitest.config.mts', VITEST_CONFIG_CONTENT);

const { result } = await harness.executeOnce();
expect(result?.success).toBeTrue();
Expand All @@ -117,12 +117,12 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
it('should use custom reportsDirectory defined in runnerConfig file', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
coverage: true,
});

harness.writeFile(
'vitest.config.ts',
'vitest.config.mts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
Expand All @@ -144,11 +144,11 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
harness.useTarget('test', {
...BASE_OPTIONS,
coverage: true,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
});

harness.writeFile(
'vitest.config.ts',
'vitest.config.mts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
Expand All @@ -167,11 +167,11 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
it('should enable coverage when set in runnerConfig file without builder option', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
});

harness.writeFile(
'vitest.config.ts',
'vitest.config.mts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
Expand All @@ -192,11 +192,11 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
it('should exclude test files based on runnerConfig file', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
});

harness.writeFile(
'vitest.config.ts',
'vitest.config.mts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
Expand Down Expand Up @@ -240,12 +240,12 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
harness.useTarget('test', {
...BASE_OPTIONS,
coverage: true,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
coverageExclude: ['src/app/cli-excluded.ts'],
});

harness.writeFile(
'vitest.config.ts',
'vitest.config.mts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
Expand Down Expand Up @@ -292,11 +292,11 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
it('should allow overriding globals to false via runnerConfig file', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
});

harness.writeFile(
'vitest.config.ts',
'vitest.config.mts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
Expand Down Expand Up @@ -325,11 +325,11 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
it('should initialize environment even when globals are disabled in runnerConfig file', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
});

harness.writeFile(
'vitest.config.ts',
'vitest.config.mts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
Expand Down Expand Up @@ -357,11 +357,11 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
it('should fail when a DOM-dependent test is run in a node environment', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
});

harness.writeFile(
'vitest.config.ts',
'vitest.config.mts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
Expand All @@ -379,11 +379,11 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
it('should warn and ignore "test.projects" option from runnerConfig file', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
});

harness.writeFile(
'vitest.config.ts',
'vitest.config.mts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
Expand Down Expand Up @@ -411,11 +411,11 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
it('should warn and ignore "test.include" option from runnerConfig file', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
});

harness.writeFile(
'vitest.config.ts',
'vitest.config.mts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
Expand Down Expand Up @@ -444,11 +444,11 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
harness.useTarget('test', {
...BASE_OPTIONS,
watch: false,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
});

harness.writeFile(
'vitest.config.ts',
'vitest.config.mts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
Expand Down Expand Up @@ -476,11 +476,11 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
it('should warn about performance when "test.exclude" option is in runnerConfig file', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
});

harness.writeFile(
'vitest.config.ts',
'vitest.config.mts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
Expand Down Expand Up @@ -508,11 +508,11 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
it(`should append "test.setupFiles" (string) from runnerConfig to the CLI's setup`, async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
});

harness.writeFile(
'vitest.config.ts',
'vitest.config.mts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
Expand Down Expand Up @@ -542,11 +542,11 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
it(`should append "test.setupFiles" (array) from runnerConfig to the CLI's setup`, async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
});

harness.writeFile(
'vitest.config.ts',
'vitest.config.mts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
Expand Down Expand Up @@ -578,11 +578,11 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
it('should merge and apply custom Vite plugins from runnerConfig file', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
runnerConfig: 'vitest.config.mts',
});

harness.writeFile(
'vitest.config.ts',
'vitest.config.mts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,11 @@ import {

describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
describe('Behavior: "Vitest shared chunk initialization"', () => {
// Regression test for https://github.com/angular/angular-cli/issues/33728.
//
// Without `disableCodeSplitting`, esbuild hoists a module imported by more than one spec
// entry point into a shared chunk behind a lazy `__esm` initializer, and a class-field
// initializer in another chunk reads the exported value as `undefined` under the jsdom
// runner. All four trigger conditions are required and encoded below:
// 1. two spec entry points import the shared module (so it lands in a shared chunk);
// 2. a component in one entry reads the export during class-field initialization;
// 3. that component's spec file contains an `async` test callback (no `await` needed);
// 4. zone.js is in the polyfills (the `setupApplicationTarget` default), which downlevels
// async and makes esbuild emit the spec entry CommonJS-wrapped.
//
// NOTE: the failure this guards against is sensitive to inert content — adding a top-level
// side effect (even a `console.log`) to the shared or importing module below defused it
// during reduction. Mirror https://github.com/jonmarozick/ng-shared-chunk-repro when
// modifying these fixtures.
it('should provide shared-module exports to class-field initializers in async specs', async () => {
setupApplicationTarget(harness);

harness.useTarget('test', {
...BASE_OPTIONS,
splitting: false,
});

// Keep the default project's spec deterministic; a third spec entry that does not touch
Expand Down
Loading
Loading