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
@@ -0,0 +1,9 @@
// Learn more about Vitest configuration options at https://vitest.dev/config/

import { defineConfig } from 'vitest/config';

export default defineConfig({
test: {
// ...
},
});
60 changes: 60 additions & 0 deletions packages/schematics/angular/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,71 @@ export default createProjectSchematic<ConfigOptions>((options, { project }) => {
return addKarmaConfig(options);
case ConfigType.Browserslist:
return addBrowserslistConfig(project.root);
case ConfigType.Vitest:
return addVitestConfig(options);
default:
throw new SchematicsException(`"${options.type}" is an unknown configuration file type.`);
}
});

function addVitestConfig(options: ConfigOptions): Rule {
return (tree, context) =>
updateWorkspace((workspace) => {
const project = workspace.projects.get(options.project);
if (!project) {
throw new SchematicsException(`Project name "${options.project}" doesn't not exist.`);
}

const testTarget = project.targets.get('test');
if (!testTarget) {
throw new SchematicsException(
`No "test" target found for project "${options.project}".` +
' A "test" target is required to generate a Vitest configuration.',
);
}

if (testTarget.builder !== AngularBuilder.BuildUnitTest) {
throw new SchematicsException(
`Cannot add a Vitest configuration as builder for "test" target in project does not` +
` use "${AngularBuilder.BuildUnitTest}".`,
);
}

testTarget.options ??= {};
testTarget.options.runnerConfig = true;

// Check runner option.
if (testTarget.options.runner === 'karma') {
context.logger.warn(
`The "test" target is configured to use the "karma" runner in the main options.` +
' The generated "vitest-base.config.ts" file may not be used.',
);
}

for (const [name, config] of Object.entries(testTarget.configurations ?? {})) {
if (
config &&
typeof config === 'object' &&
'runner' in config &&
config.runner === 'karma'
) {
context.logger.warn(
`The "test" target's "${name}" configuration is configured to use the "karma" runner.` +
' The generated "vitest-base.config.ts" file may not be used for that configuration.',
);
}
}

return mergeWith(
apply(url('./files'), [
filter((p) => p.endsWith('vitest-base.config.ts.template')),
applyTemplates({}),
move(project.root),
]),
);
});
}

async function addBrowserslistConfig(projectRoot: string): Promise<Rule> {
return mergeWith(
apply(url('./files'), [
Expand Down
74 changes: 74 additions & 0 deletions packages/schematics/angular/config/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,78 @@ describe('Config Schematic', () => {
expect(tree.exists('projects/foo/.browserslistrc')).toBeTrue();
});
});

describe(`when 'type' is 'vitest'`, () => {
beforeEach(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const angularJson = applicationTree.readJson('angular.json') as any;
angularJson.projects.foo.architect.test.builder = '@angular/build:unit-test';
applicationTree.overwrite('angular.json', JSON.stringify(angularJson));
});

it('should create a vitest-base.config.ts file', async () => {
const tree = await runConfigSchematic(ConfigType.Vitest);
expect(tree.exists('projects/foo/vitest-base.config.ts')).toBeTrue();
});

it(`should set 'runnerConfig' in test builder`, async () => {
const tree = await runConfigSchematic(ConfigType.Vitest);
const config = JSON.parse(tree.readContent('/angular.json'));
const prj = config.projects.foo;
const { runnerConfig } = prj.architect.test.options;
expect(runnerConfig).toBe(true);
});

it('should throw an error if the builder is not "unit-test"', async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const angularJson = applicationTree.readJson('angular.json') as any;
angularJson.projects.foo.architect.test.builder = '@angular/build:karma';
applicationTree.overwrite('angular.json', JSON.stringify(angularJson));

await expectAsync(runConfigSchematic(ConfigType.Vitest)).toBeRejectedWithError(
/Cannot add a Vitest configuration as builder for "test" target/,
);
});

it(`should warn when 'runner' is 'karma' in options`, async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const angularJson = applicationTree.readJson('angular.json') as any;
angularJson.projects.foo.architect.test.options ??= {};
angularJson.projects.foo.architect.test.options.runner = 'karma';
applicationTree.overwrite('angular.json', JSON.stringify(angularJson));

const logs: string[] = [];
schematicRunner.logger.subscribe(({ message }) => logs.push(message));
await runConfigSchematic(ConfigType.Vitest);
expect(
logs.some((v) =>
v.includes(
`The "test" target is configured to use the "karma" runner in the main options.`,
),
),
).toBeTrue();
});

it(`should warn when 'runner' is 'karma' in a configuration`, async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const angularJson = applicationTree.readJson('angular.json') as any;
angularJson.projects.foo.architect.test.configurations ??= {};
angularJson.projects.foo.architect.test.configurations.ci = { runner: 'karma' };
applicationTree.overwrite('angular.json', JSON.stringify(angularJson));

const logs: string[] = [];
schematicRunner.logger.subscribe(({ message }) => logs.push(message));
await runConfigSchematic(ConfigType.Vitest);
expect(
logs.some((v) => v.includes(`"ci" configuration is configured to use the "karma" runner`)),
).toBeTrue();
});

it(`should not warn when 'runner' is not set`, async () => {
const logs: string[] = [];
schematicRunner.logger.subscribe(({ message }) => logs.push(message));
await runConfigSchematic(ConfigType.Vitest);
expect(logs.length).toBe(0);
});
});
});
2 changes: 1 addition & 1 deletion packages/schematics/angular/config/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"type": {
"type": "string",
"description": "Specifies the type of configuration file to generate.",
"enum": ["karma", "browserslist"],
"enum": ["karma", "browserslist", "vitest"],
"x-prompt": "Which type of configuration file would you like to create?",
"$default": {
"$source": "argv",
Expand Down