Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ interface VitestConfigPluginOptions {
projectSourceRoot: string;
reporters?: string[] | [string, object][];
setupFiles: string[];
projectPlugins: VitestPlugins;
projectPlugins: Exclude<UserWorkspaceConfig['plugins'], undefined>;
include: string[];
}

Expand Down Expand Up @@ -73,13 +73,56 @@ export function createVitestConfigPlugin(options: VitestConfigPluginOptions): Vi
async config(config) {
const testConfig = config.test;

if (testConfig?.projects?.length) {
this.warn(
'The "test.projects" option in the Vitest configuration file is not supported. ' +
'The Angular CLI Test system will construct its own project configuration.',
);
delete testConfig.projects;
}

if (testConfig?.include) {
this.warn(
'The "test.include" option in the Vitest configuration file is not supported. ' +
'The Angular CLI Test system will manage test file discovery.',
);
delete testConfig.include;
}

// The user's setup files should be appended to the CLI's setup files.
const combinedSetupFiles = [...setupFiles];
if (testConfig?.setupFiles) {
if (typeof testConfig.setupFiles === 'string') {
combinedSetupFiles.push(testConfig.setupFiles);
} else if (Array.isArray(testConfig.setupFiles)) {
combinedSetupFiles.push(...testConfig.setupFiles);
}
delete testConfig.setupFiles;
}

// Merge user-defined plugins from the Vitest config with the CLI's internal plugins.
if (config.plugins) {
const userPlugins = config.plugins.filter(
(plugin) =>
// Only inspect objects with a `name` property as these would be the internal injected plugins
!plugin ||
typeof plugin !== 'object' ||
!('name' in plugin) ||
(!plugin.name.startsWith('angular:') && !plugin.name.startsWith('vitest')),
);

if (userPlugins.length > 0) {
projectPlugins.push(...userPlugins);
}
}

const projectResolver = createRequire(projectSourceRoot + '/').resolve;

const projectConfig: UserWorkspaceConfig = {
test: {
...testConfig,
name: projectName,
setupFiles,
setupFiles: combinedSetupFiles,
include,
globals: testConfig?.globals ?? true,
...(browser ? { browser } : {}),
Expand All @@ -99,6 +142,7 @@ export function createVitestConfigPlugin(options: VitestConfigPluginOptions): Vi
coverage: await generateCoverageOption(options.coverage, projectName),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...(reporters ? ({ reporters } as any) : {}),
...(browser ? { browser } : {}),
projects: [projectConfig],
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
const results = JSON.parse(harness.readFile('vitest-results.json'));
expect(results.numPassedTests).toBe(1);
});

it('should allow overriding builder options via runnerConfig file', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
Expand Down Expand Up @@ -142,5 +143,179 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
const { result } = await harness.executeOnce();
expect(result?.success).toBeFalse();
});

it('should warn and ignore "test.projects" option from runnerConfig file', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
});

harness.writeFile(
'vitest.config.ts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
projects: ['./foo.config.ts'],
},
});
`,
);

const { result, logs } = await harness.executeOnce();
expect(result?.success).toBeTrue();

// TODO: Re-enable once Vite logs are remapped through build system
// expect(logs).toContain(
// jasmine.objectContaining({
// level: 'warn',
// message: jasmine.stringMatching(
// 'The "test.projects" option in the Vitest configuration file is not supported.',
// ),
// }),
// );
});

it('should warn and ignore "test.include" option from runnerConfig file', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
});

harness.writeFile(
'vitest.config.ts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
include: ['src/app/non-existent.spec.ts'],
},
});
`,
);

const { result, logs } = await harness.executeOnce();
expect(result?.success).toBeTrue();

// TODO: Re-enable once Vite logs are remapped through build system
// expect(logs).toContain(
// jasmine.objectContaining({
// level: 'warn',
// message: jasmine.stringMatching(
// 'The "test.include" option in the Vitest configuration file is not supported.',
// ),
// }),
// );
});

it(`should append "test.setupFiles" (string) from runnerConfig to the CLI's setup`, async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
});

harness.writeFile(
'vitest.config.ts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
setupFiles: './src/app/custom-setup.ts',
},
});
`,
);

harness.writeFile('src/app/custom-setup.ts', `(globalThis as any).customSetupLoaded = true;`);

harness.writeFile(
'src/app/app.component.spec.ts',
`
import { test, expect } from 'vitest';
test('should have custom setup loaded', () => {
expect((globalThis as any).customSetupLoaded).toBe(true);
});
`,
);

const { result } = await harness.executeOnce();
expect(result?.success).toBeTrue();
});

it(`should append "test.setupFiles" (array) from runnerConfig to the CLI's setup`, async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
});

harness.writeFile(
'vitest.config.ts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
setupFiles: ['./src/app/custom-setup-1.ts', './src/app/custom-setup-2.ts'],
},
});
`,
);

harness.writeFile('src/app/custom-setup-1.ts', `(globalThis as any).customSetup1 = true;`);
harness.writeFile('src/app/custom-setup-2.ts', `(globalThis as any).customSetup2 = true;`);

harness.writeFile(
'src/app/app.component.spec.ts',
`
import { test, expect } from 'vitest';
test('should have custom setups loaded', () => {
expect((globalThis as any).customSetup1).toBe(true);
expect((globalThis as any).customSetup2).toBe(true);
});
`,
);

const { result } = await harness.executeOnce();
expect(result?.success).toBeTrue();
});

it('should merge and apply custom Vite plugins from runnerConfig file', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
runnerConfig: 'vitest.config.ts',
});

harness.writeFile(
'vitest.config.ts',
`
import { defineConfig } from 'vitest/config';
export default defineConfig({
plugins: [
{
name: 'my-custom-transform-plugin',
transform(code, id) {
if (code.includes('__PLACEHOLDER__')) {
return code.replace('__PLACEHOLDER__', 'transformed by custom plugin');
}
},
},
],
});
`,
);

harness.writeFile(
'src/app/app.component.spec.ts',
`
import { test, expect } from 'vitest';
test('should have been transformed by custom plugin', () => {
const placeholder = '__PLACEHOLDER__';
expect(placeholder).toBe('transformed by custom plugin');
});
`,
);

const { result } = await harness.executeOnce();
expect(result?.success).toBeTrue();
});
});
});