feat(misc): prompt for unit test runner when creating a workspace using --workspaces flag (#29743)

## Current Behavior

Creating a new workspace does not prompt for the unit test runner.

## Expected Behavior

Creating a new workspace should prompt for the unit test runner.

For now, this new behavior will be behind the `--workspaces` flag.

## Related Issue(s)

Fixes #
This commit is contained in:
Leosvel Pérez Espinosa 2025-01-24 16:03:57 +01:00 committed by GitHub
parent 86798a286e
commit 8d9234b385
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 153 additions and 17 deletions

View File

@ -44,6 +44,7 @@ Install `create-nx-workspace` globally to invoke the command directly, or use `n
| `--ssr` | boolean | Enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering) for the Angular application. | | `--ssr` | boolean | Enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering) for the Angular application. |
| `--standaloneApi` | boolean | Use Standalone Components if generating an Angular app. (Default: `true`) | | `--standaloneApi` | boolean | Use Standalone Components if generating an Angular app. (Default: `true`) |
| `--style` | string | Stylesheet type to be used with certain stacks. | | `--style` | string | Stylesheet type to be used with certain stacks. |
| `--unitTestRunner` | `jest`, `vitest`, `none` | Test runner to use for unit tests. |
| `--useGitHub` | boolean | Will you be using GitHub as your git hosting provider? (Default: `false`) | | `--useGitHub` | boolean | Will you be using GitHub as your git hosting provider? (Default: `false`) |
| `--version` | boolean | Show version number. | | `--version` | boolean | Show version number. |
| `--workspaces` | boolean | Use package manager workspaces. (Default: `false`) | | `--workspaces` | boolean | Use package manager workspaces. (Default: `false`) |

View File

@ -44,6 +44,7 @@ Install `create-nx-workspace` globally to invoke the command directly, or use `n
| `--ssr` | boolean | Enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering) for the Angular application. | | `--ssr` | boolean | Enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering) for the Angular application. |
| `--standaloneApi` | boolean | Use Standalone Components if generating an Angular app. (Default: `true`) | | `--standaloneApi` | boolean | Use Standalone Components if generating an Angular app. (Default: `true`) |
| `--style` | string | Stylesheet type to be used with certain stacks. | | `--style` | string | Stylesheet type to be used with certain stacks. |
| `--unitTestRunner` | `jest`, `vitest`, `none` | Test runner to use for unit tests. |
| `--useGitHub` | boolean | Will you be using GitHub as your git hosting provider? (Default: `false`) | | `--useGitHub` | boolean | Will you be using GitHub as your git hosting provider? (Default: `false`) |
| `--version` | boolean | Show version number. | | `--version` | boolean | Show version number. |
| `--workspaces` | boolean | Use package manager workspaces. (Default: `false`) | | `--workspaces` | boolean | Use package manager workspaces. (Default: `false`) |

View File

@ -70,6 +70,11 @@
"type": "boolean", "type": "boolean",
"default": true "default": true
}, },
"unitTestRunner": {
"description": "The tool to use for running unit tests.",
"type": "string",
"enum": ["jest", "vitest", "none"]
},
"e2eTestRunner": { "e2eTestRunner": {
"description": "The tool to use for running e2e tests.", "description": "The tool to use for running e2e tests.",
"type": "string", "type": "string",

View File

@ -87,6 +87,11 @@
"type": "boolean", "type": "boolean",
"default": true "default": true
}, },
"unitTestRunner": {
"description": "The tool to use for running unit tests.",
"type": "string",
"enum": ["jest", "vitest", "none"]
},
"e2eTestRunner": { "e2eTestRunner": {
"description": "The tool to use for running e2e tests.", "description": "The tool to use for running e2e tests.",
"type": "string", "type": "string",

View File

@ -223,7 +223,8 @@ describe('create-nx-workspace', () => {
}); });
expectNoAngularDevkit(); expectNoAngularDevkit();
expectNoTsJestInJestConfig(wsName); checkFilesExist('vitest.workspace.ts');
checkFilesDoNotExist('jest.config.ts');
const packageJson = readJson('package.json'); const packageJson = readJson('package.json');
expect(packageJson.devDependencies['@nx/vite']).toBeDefined(); // vite should be default bundler expect(packageJson.devDependencies['@nx/vite']).toBeDefined(); // vite should be default bundler
expectCodeIsFormatted(); expectCodeIsFormatted();

View File

@ -39,7 +39,6 @@ export async function normalizeOptions(
inlineTemplate: false, inlineTemplate: false,
skipTests: options.unitTestRunner === UnitTestRunner.None, skipTests: options.unitTestRunner === UnitTestRunner.None,
skipFormat: false, skipFormat: false,
unitTestRunner: UnitTestRunner.Jest,
e2eTestRunner: E2eTestRunner.Playwright, e2eTestRunner: E2eTestRunner.Playwright,
linter: Linter.EsLint, linter: Linter.EsLint,
strict: true, strict: true,
@ -59,5 +58,6 @@ export async function normalizeOptions(
!options.rootProject ? appProjectRoot : appProjectName !options.rootProject ? appProjectRoot : appProjectName
), ),
ssr: options.ssr ?? false, ssr: options.ssr ?? false,
unitTestRunner: options.unitTestRunner ?? UnitTestRunner.Jest,
}; };
} }

View File

@ -53,6 +53,7 @@ interface ReactArguments extends BaseArguments {
bundler: 'webpack' | 'vite' | 'rspack'; bundler: 'webpack' | 'vite' | 'rspack';
nextAppDir: boolean; nextAppDir: boolean;
nextSrcDir: boolean; nextSrcDir: boolean;
unitTestRunner: 'none' | 'jest' | 'vitest';
e2eTestRunner: 'none' | 'cypress' | 'playwright'; e2eTestRunner: 'none' | 'cypress' | 'playwright';
} }
@ -63,6 +64,7 @@ interface AngularArguments extends BaseArguments {
style: string; style: string;
routing: boolean; routing: boolean;
standaloneApi: boolean; standaloneApi: boolean;
unitTestRunner: 'none' | 'jest' | 'vitest';
e2eTestRunner: 'none' | 'cypress' | 'playwright'; e2eTestRunner: 'none' | 'cypress' | 'playwright';
bundler: 'webpack' | 'esbuild'; bundler: 'webpack' | 'esbuild';
ssr: boolean; ssr: boolean;
@ -76,6 +78,7 @@ interface VueArguments extends BaseArguments {
appName: string; appName: string;
framework: 'none' | 'nuxt'; framework: 'none' | 'nuxt';
style: string; style: string;
unitTestRunner: 'none' | 'vitest';
e2eTestRunner: 'none' | 'cypress' | 'playwright'; e2eTestRunner: 'none' | 'cypress' | 'playwright';
} }
@ -83,8 +86,9 @@ interface NodeArguments extends BaseArguments {
stack: 'node'; stack: 'node';
workspaceType: 'standalone' | 'integrated'; workspaceType: 'standalone' | 'integrated';
appName: string; appName: string;
framework: 'express' | 'fastify' | 'koa' | 'nest'; framework: 'none' | 'express' | 'fastify' | 'koa' | 'nest';
docker: boolean; docker: boolean;
unitTestRunner: 'none' | 'jest';
} }
interface UnknownStackArguments extends BaseArguments { interface UnknownStackArguments extends BaseArguments {
@ -190,6 +194,11 @@ export const commandsObject: yargs.Argv<Arguments> = yargs
choices: ['playwright', 'cypress', 'none'], choices: ['playwright', 'cypress', 'none'],
type: 'string', type: 'string',
}) })
.option('unitTestRunner', {
describe: chalk.dim`Test runner to use for unit tests.`,
choices: ['jest', 'vitest', 'none'],
type: 'string',
})
.option('ssr', { .option('ssr', {
describe: chalk.dim`Enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering) for the Angular application.`, describe: chalk.dim`Enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering) for the Angular application.`,
type: 'boolean', type: 'boolean',
@ -568,11 +577,12 @@ async function determineNoneOptions(
async function determineReactOptions( async function determineReactOptions(
parsedArgs: yargs.Arguments<ReactArguments> parsedArgs: yargs.Arguments<ReactArguments>
): Promise<Partial<Arguments>> { ): Promise<Partial<ReactArguments>> {
let preset: Preset; let preset: Preset;
let style: undefined | string = undefined; let style: undefined | string = undefined;
let appName: string; let appName: string;
let bundler: undefined | 'webpack' | 'vite' | 'rspack' = undefined; let bundler: undefined | 'webpack' | 'vite' | 'rspack' = undefined;
let unitTestRunner: undefined | 'none' | 'jest' | 'vitest' = undefined;
let e2eTestRunner: undefined | 'none' | 'cypress' | 'playwright' = undefined; let e2eTestRunner: undefined | 'none' | 'cypress' | 'playwright' = undefined;
let nextAppDir = false; let nextAppDir = false;
let nextSrcDir = false; let nextSrcDir = false;
@ -633,17 +643,29 @@ async function determineReactOptions(
if (preset === Preset.ReactStandalone || preset === Preset.ReactMonorepo) { if (preset === Preset.ReactStandalone || preset === Preset.ReactMonorepo) {
bundler = await determineReactBundler(parsedArgs); bundler = await determineReactBundler(parsedArgs);
unitTestRunner = await determineUnitTestRunner(parsedArgs, {
preferVitest: bundler === 'vite',
});
e2eTestRunner = await determineE2eTestRunner(parsedArgs); e2eTestRunner = await determineE2eTestRunner(parsedArgs);
} else if (preset === Preset.NextJs || preset === Preset.NextJsStandalone) { } else if (preset === Preset.NextJs || preset === Preset.NextJsStandalone) {
nextAppDir = await determineNextAppDir(parsedArgs); nextAppDir = await determineNextAppDir(parsedArgs);
nextSrcDir = await determineNextSrcDir(parsedArgs); nextSrcDir = await determineNextSrcDir(parsedArgs);
unitTestRunner = await determineUnitTestRunner(parsedArgs, {
exclude: 'vitest',
});
e2eTestRunner = await determineE2eTestRunner(parsedArgs); e2eTestRunner = await determineE2eTestRunner(parsedArgs);
} else if ( } else if (
preset === Preset.RemixMonorepo || preset === Preset.RemixMonorepo ||
preset === Preset.RemixStandalone || preset === Preset.RemixStandalone
preset === Preset.ReactNative ||
preset === Preset.Expo
) { ) {
unitTestRunner = await determineUnitTestRunner(parsedArgs, {
preferVitest: true,
});
e2eTestRunner = await determineE2eTestRunner(parsedArgs);
} else if (preset === Preset.ReactNative || preset === Preset.Expo) {
unitTestRunner = await determineUnitTestRunner(parsedArgs, {
exclude: 'vitest',
});
e2eTestRunner = await determineE2eTestRunner(parsedArgs); e2eTestRunner = await determineE2eTestRunner(parsedArgs);
} }
@ -715,6 +737,7 @@ async function determineReactOptions(
bundler, bundler,
nextAppDir, nextAppDir,
nextSrcDir, nextSrcDir,
unitTestRunner,
e2eTestRunner, e2eTestRunner,
linter, linter,
formatter, formatter,
@ -724,10 +747,11 @@ async function determineReactOptions(
async function determineVueOptions( async function determineVueOptions(
parsedArgs: yargs.Arguments<VueArguments> parsedArgs: yargs.Arguments<VueArguments>
): Promise<Partial<Arguments>> { ): Promise<Partial<VueArguments>> {
let preset: Preset; let preset: Preset;
let style: undefined | string = undefined; let style: undefined | string = undefined;
let appName: string; let appName: string;
let unitTestRunner: undefined | 'none' | 'vitest' = undefined;
let e2eTestRunner: undefined | 'none' | 'cypress' | 'playwright' = undefined; let e2eTestRunner: undefined | 'none' | 'cypress' | 'playwright' = undefined;
let linter: undefined | 'none' | 'eslint'; let linter: undefined | 'none' | 'eslint';
let formatter: undefined | 'none' | 'prettier'; let formatter: undefined | 'none' | 'prettier';
@ -768,6 +792,9 @@ async function determineVueOptions(
} }
} }
unitTestRunner = await determineUnitTestRunner(parsedArgs, {
exclude: 'jest',
});
e2eTestRunner = await determineE2eTestRunner(parsedArgs); e2eTestRunner = await determineE2eTestRunner(parsedArgs);
if (parsedArgs.style) { if (parsedArgs.style) {
@ -815,6 +842,7 @@ async function determineVueOptions(
preset, preset,
style, style,
appName, appName,
unitTestRunner,
e2eTestRunner, e2eTestRunner,
linter, linter,
formatter, formatter,
@ -824,10 +852,11 @@ async function determineVueOptions(
async function determineAngularOptions( async function determineAngularOptions(
parsedArgs: yargs.Arguments<AngularArguments> parsedArgs: yargs.Arguments<AngularArguments>
): Promise<Partial<Arguments>> { ): Promise<Partial<AngularArguments>> {
let preset: Preset; let preset: Preset;
let style: string; let style: string;
let appName: string; let appName: string;
let unitTestRunner: undefined | 'none' | 'jest' | 'vitest' = undefined;
let e2eTestRunner: undefined | 'none' | 'cypress' | 'playwright' = undefined; let e2eTestRunner: undefined | 'none' | 'cypress' | 'playwright' = undefined;
let bundler: undefined | 'webpack' | 'esbuild' = undefined; let bundler: undefined | 'webpack' | 'esbuild' = undefined;
let ssr: undefined | boolean = undefined; let ssr: undefined | boolean = undefined;
@ -965,6 +994,7 @@ async function determineAngularOptions(
serverRouting = false; serverRouting = false;
} }
unitTestRunner = await determineUnitTestRunner(parsedArgs);
e2eTestRunner = await determineE2eTestRunner(parsedArgs); e2eTestRunner = await determineE2eTestRunner(parsedArgs);
return { return {
@ -973,6 +1003,7 @@ async function determineAngularOptions(
appName, appName,
standaloneApi, standaloneApi,
routing, routing,
unitTestRunner,
e2eTestRunner, e2eTestRunner,
bundler, bundler,
ssr, ssr,
@ -983,14 +1014,14 @@ async function determineAngularOptions(
async function determineNodeOptions( async function determineNodeOptions(
parsedArgs: yargs.Arguments<NodeArguments> parsedArgs: yargs.Arguments<NodeArguments>
): Promise<Partial<Arguments>> { ): Promise<Partial<NodeArguments>> {
let preset: Preset; let preset: Preset;
let appName: string; let appName: string;
let framework: 'express' | 'fastify' | 'koa' | 'nest' | 'none'; let framework: 'express' | 'fastify' | 'koa' | 'nest' | 'none';
let docker: boolean; let docker: boolean;
let linter: undefined | 'none' | 'eslint'; let linter: undefined | 'none' | 'eslint';
let formatter: undefined | 'none' | 'prettier'; let formatter: undefined | 'none' | 'prettier';
let unitTestRunner: undefined | 'none' | 'jest' = undefined;
const workspaces = parsedArgs.workspaces ?? false; const workspaces = parsedArgs.workspaces ?? false;
if (parsedArgs.preset) { if (parsedArgs.preset) {
@ -1051,6 +1082,10 @@ async function determineNodeOptions(
docker = reply.docker === 'Yes'; docker = reply.docker === 'Yes';
} }
unitTestRunner = await determineUnitTestRunner(parsedArgs, {
exclude: 'vitest',
});
if (workspaces) { if (workspaces) {
linter = await determineLinterOptions(parsedArgs); linter = await determineLinterOptions(parsedArgs);
formatter = await determineFormatterOptions(parsedArgs); formatter = await determineFormatterOptions(parsedArgs);
@ -1067,6 +1102,7 @@ async function determineNodeOptions(
linter, linter,
formatter, formatter,
workspaces, workspaces,
unitTestRunner,
}; };
} }
@ -1358,6 +1394,60 @@ async function determineNodeFramework(
return reply.framework; return reply.framework;
} }
async function determineUnitTestRunner<T extends 'none' | 'jest' | 'vitest'>(
parsedArgs: yargs.Arguments<{
bundler?: 'vite' | string;
unitTestRunner?: T;
workspaces?: boolean;
}>,
options?: {
exclude?: 'jest' | 'vitest';
preferVitest?: boolean;
}
): Promise<T | undefined> {
if (parsedArgs.unitTestRunner) {
return parsedArgs.unitTestRunner;
} else if (!parsedArgs.workspaces) {
return undefined;
}
const reply = await enquirer.prompt<{
unitTestRunner: 'none' | 'jest' | 'vitest';
}>([
{
message: 'Which unit test runner would you like to use?',
type: 'autocomplete',
name: 'unitTestRunner',
skip: !parsedArgs.interactive || isCI(),
choices: [
{
name: 'none',
message: 'None',
},
{
name: 'jest',
message: 'Jest [ https://jestjs.io/ ]',
},
{
name: 'vitest',
message: 'Vitest [ https://vitest.dev/ ]',
},
]
.filter((t) => !options?.exclude || options.exclude !== t.name)
.sort((a, b) => {
if (a.name === 'none') return -1;
if (b.name === 'none') return 1;
if (options?.preferVitest && a.name === 'vitest') return -1;
if (options?.preferVitest && b.name === 'vitest') return 1;
return 0;
}),
initial: 0,
},
]);
return reply.unitTestRunner as T;
}
async function determineE2eTestRunner( async function determineE2eTestRunner(
parsedArgs: yargs.Arguments<{ parsedArgs: yargs.Arguments<{
e2eTestRunner?: 'none' | 'cypress' | 'playwright'; e2eTestRunner?: 'none' | 'cypress' | 'playwright';

View File

@ -88,6 +88,9 @@ export function generatePreset(host: Tree, opts: NormalizedSchema) {
: null, : null,
parsedArgs.interactive ? '--interactive=true' : '--interactive=false', parsedArgs.interactive ? '--interactive=true' : '--interactive=false',
opts.routing !== undefined ? `--routing=${opts.routing}` : null, opts.routing !== undefined ? `--routing=${opts.routing}` : null,
opts.unitTestRunner !== undefined
? `--unitTestRunner=${opts.unitTestRunner}`
: null,
opts.e2eTestRunner !== undefined opts.e2eTestRunner !== undefined
? `--e2eTestRunner=${opts.e2eTestRunner}` ? `--e2eTestRunner=${opts.e2eTestRunner}`
: null, : null,

View File

@ -32,6 +32,7 @@ interface Schema {
standaloneApi?: boolean; standaloneApi?: boolean;
routing?: boolean; routing?: boolean;
packageManager?: PackageManager; packageManager?: PackageManager;
unitTestRunner?: 'jest' | 'vitest' | 'none';
e2eTestRunner?: 'cypress' | 'playwright' | 'detox' | 'jest' | 'none'; e2eTestRunner?: 'cypress' | 'playwright' | 'detox' | 'jest' | 'none';
ssr?: boolean; ssr?: boolean;
serverRouting?: boolean; serverRouting?: boolean;

View File

@ -73,6 +73,11 @@
"type": "boolean", "type": "boolean",
"default": true "default": true
}, },
"unitTestRunner": {
"description": "The tool to use for running unit tests.",
"type": "string",
"enum": ["jest", "vitest", "none"]
},
"e2eTestRunner": { "e2eTestRunner": {
"description": "The tool to use for running e2e tests.", "description": "The tool to use for running e2e tests.",
"type": "string", "type": "string",

View File

@ -34,6 +34,7 @@ async function createPreset(tree: Tree, options: Schema) {
standalone: options.standaloneApi, standalone: options.standaloneApi,
routing: options.routing, routing: options.routing,
e2eTestRunner: options.e2eTestRunner ?? 'playwright', e2eTestRunner: options.e2eTestRunner ?? 'playwright',
unitTestRunner: options.unitTestRunner,
bundler: options.bundler, bundler: options.bundler,
ssr: options.ssr, ssr: options.ssr,
serverRouting: options.serverRouting, serverRouting: options.serverRouting,
@ -54,6 +55,7 @@ async function createPreset(tree: Tree, options: Schema) {
rootProject: true, rootProject: true,
standalone: options.standaloneApi, standalone: options.standaloneApi,
e2eTestRunner: options.e2eTestRunner ?? 'playwright', e2eTestRunner: options.e2eTestRunner ?? 'playwright',
unitTestRunner: options.unitTestRunner,
bundler: options.bundler, bundler: options.bundler,
ssr: options.ssr, ssr: options.ssr,
serverRouting: options.serverRouting, serverRouting: options.serverRouting,
@ -71,6 +73,9 @@ async function createPreset(tree: Tree, options: Schema) {
linter: options.linter, linter: options.linter,
bundler: options.bundler ?? 'webpack', bundler: options.bundler ?? 'webpack',
e2eTestRunner: options.e2eTestRunner ?? 'playwright', e2eTestRunner: options.e2eTestRunner ?? 'playwright',
unitTestRunner:
options.unitTestRunner ??
(options.bundler === 'vite' ? 'vitest' : 'jest'),
addPlugin, addPlugin,
nxCloudToken: options.nxCloudToken, nxCloudToken: options.nxCloudToken,
useTsSolution: options.workspaces, useTsSolution: options.workspaces,
@ -80,15 +85,18 @@ async function createPreset(tree: Tree, options: Schema) {
const { applicationGenerator: reactApplicationGenerator } = require('@nx' + const { applicationGenerator: reactApplicationGenerator } = require('@nx' +
'/react'); '/react');
const bundler = options.bundler ?? 'vite';
return reactApplicationGenerator(tree, { return reactApplicationGenerator(tree, {
name: options.name, name: options.name,
directory: '.', directory: '.',
style: options.style, style: options.style,
linter: options.linter, linter: options.linter,
rootProject: true, rootProject: true,
bundler: options.bundler ?? 'vite', bundler,
e2eTestRunner: options.e2eTestRunner ?? 'playwright', e2eTestRunner: options.e2eTestRunner ?? 'playwright',
unitTestRunner: options.bundler === 'vite' ? 'vitest' : 'jest', unitTestRunner:
options.unitTestRunner ?? (bundler === 'vite' ? 'vitest' : 'jest'),
addPlugin, addPlugin,
nxCloudToken: options.nxCloudToken, nxCloudToken: options.nxCloudToken,
formatter: options.formatter, formatter: options.formatter,
@ -102,7 +110,7 @@ async function createPreset(tree: Tree, options: Schema) {
directory: join('apps', options.name), directory: join('apps', options.name),
linter: options.linter, linter: options.linter,
e2eTestRunner: options.e2eTestRunner ?? 'playwright', e2eTestRunner: options.e2eTestRunner ?? 'playwright',
unitTestRunner: 'vitest', unitTestRunner: options.unitTestRunner ?? 'vitest',
addPlugin, addPlugin,
nxCloudToken: options.nxCloudToken, nxCloudToken: options.nxCloudToken,
useTsSolution: options.workspaces, useTsSolution: options.workspaces,
@ -118,7 +126,7 @@ async function createPreset(tree: Tree, options: Schema) {
linter: options.linter, linter: options.linter,
e2eTestRunner: options.e2eTestRunner ?? 'playwright', e2eTestRunner: options.e2eTestRunner ?? 'playwright',
rootProject: true, rootProject: true,
unitTestRunner: 'vitest', unitTestRunner: options.unitTestRunner ?? 'vitest',
addPlugin, addPlugin,
nxCloudToken: options.nxCloudToken, nxCloudToken: options.nxCloudToken,
formatter: options.formatter, formatter: options.formatter,
@ -133,6 +141,7 @@ async function createPreset(tree: Tree, options: Schema) {
style: options.style, style: options.style,
linter: options.linter, linter: options.linter,
e2eTestRunner: options.e2eTestRunner ?? 'playwright', e2eTestRunner: options.e2eTestRunner ?? 'playwright',
unitTestRunner: options.unitTestRunner,
addPlugin, addPlugin,
nxCloudToken: options.nxCloudToken, nxCloudToken: options.nxCloudToken,
useTsSolution: options.workspaces, useTsSolution: options.workspaces,
@ -149,7 +158,7 @@ async function createPreset(tree: Tree, options: Schema) {
linter: options.linter, linter: options.linter,
rootProject: true, rootProject: true,
e2eTestRunner: options.e2eTestRunner ?? 'playwright', e2eTestRunner: options.e2eTestRunner ?? 'playwright',
unitTestRunner: 'vitest', unitTestRunner: options.unitTestRunner ?? 'vitest',
addPlugin, addPlugin,
nxCloudToken: options.nxCloudToken, nxCloudToken: options.nxCloudToken,
}); });
@ -163,6 +172,7 @@ async function createPreset(tree: Tree, options: Schema) {
style: options.style, style: options.style,
linter: options.linter, linter: options.linter,
e2eTestRunner: options.e2eTestRunner ?? 'playwright', e2eTestRunner: options.e2eTestRunner ?? 'playwright',
unitTestRunner: options.unitTestRunner,
addPlugin, addPlugin,
nxCloudToken: options.nxCloudToken, nxCloudToken: options.nxCloudToken,
useTsSolution: options.workspaces, useTsSolution: options.workspaces,
@ -179,7 +189,7 @@ async function createPreset(tree: Tree, options: Schema) {
linter: options.linter, linter: options.linter,
rootProject: true, rootProject: true,
e2eTestRunner: options.e2eTestRunner ?? 'playwright', e2eTestRunner: options.e2eTestRunner ?? 'playwright',
unitTestRunner: 'vitest', unitTestRunner: options.unitTestRunner ?? 'vitest',
addPlugin, addPlugin,
nxCloudToken: options.nxCloudToken, nxCloudToken: options.nxCloudToken,
}); });
@ -195,6 +205,7 @@ async function createPreset(tree: Tree, options: Schema) {
appDir: options.nextAppDir, appDir: options.nextAppDir,
src: options.nextSrcDir, src: options.nextSrcDir,
e2eTestRunner: options.e2eTestRunner ?? 'playwright', e2eTestRunner: options.e2eTestRunner ?? 'playwright',
unitTestRunner: options.unitTestRunner,
addPlugin, addPlugin,
useTsSolution: options.workspaces, useTsSolution: options.workspaces,
formatter: options.formatter, formatter: options.formatter,
@ -210,6 +221,7 @@ async function createPreset(tree: Tree, options: Schema) {
appDir: options.nextAppDir, appDir: options.nextAppDir,
src: options.nextSrcDir, src: options.nextSrcDir,
e2eTestRunner: options.e2eTestRunner ?? 'playwright', e2eTestRunner: options.e2eTestRunner ?? 'playwright',
unitTestRunner: options.unitTestRunner,
rootProject: true, rootProject: true,
addPlugin, addPlugin,
formatter: options.formatter, formatter: options.formatter,
@ -237,6 +249,7 @@ async function createPreset(tree: Tree, options: Schema) {
directory: join('apps', options.name), directory: join('apps', options.name),
linter: options.linter, linter: options.linter,
e2eTestRunner: options.e2eTestRunner ?? 'jest', e2eTestRunner: options.e2eTestRunner ?? 'jest',
unitTestRunner: options.unitTestRunner,
addPlugin, addPlugin,
useTsSolution: options.workspaces, useTsSolution: options.workspaces,
formatter: options.formatter, formatter: options.formatter,
@ -250,6 +263,7 @@ async function createPreset(tree: Tree, options: Schema) {
directory: join('apps', options.name), directory: join('apps', options.name),
linter: options.linter, linter: options.linter,
e2eTestRunner: options.e2eTestRunner ?? 'jest', e2eTestRunner: options.e2eTestRunner ?? 'jest',
unitTestRunner: options.unitTestRunner,
addPlugin, addPlugin,
useTsSolution: options.workspaces, useTsSolution: options.workspaces,
formatter: options.formatter, formatter: options.formatter,
@ -262,6 +276,7 @@ async function createPreset(tree: Tree, options: Schema) {
directory: join('apps', options.name), directory: join('apps', options.name),
linter: options.linter, linter: options.linter,
e2eTestRunner: options.e2eTestRunner ?? 'detox', e2eTestRunner: options.e2eTestRunner ?? 'detox',
unitTestRunner: options.unitTestRunner,
addPlugin, addPlugin,
nxCloudToken: options.nxCloudToken, nxCloudToken: options.nxCloudToken,
bundler: options.bundler ?? 'webpack', bundler: options.bundler ?? 'webpack',
@ -275,6 +290,7 @@ async function createPreset(tree: Tree, options: Schema) {
directory: join('apps', options.name), directory: join('apps', options.name),
linter: options.linter, linter: options.linter,
e2eTestRunner: options.e2eTestRunner ?? 'detox', e2eTestRunner: options.e2eTestRunner ?? 'detox',
unitTestRunner: options.unitTestRunner,
addPlugin, addPlugin,
nxCloudToken: options.nxCloudToken, nxCloudToken: options.nxCloudToken,
useTsSolution: options.workspaces, useTsSolution: options.workspaces,
@ -314,6 +330,7 @@ async function createPreset(tree: Tree, options: Schema) {
docker: options.docker, docker: options.docker,
rootProject: true, rootProject: true,
e2eTestRunner: options.e2eTestRunner ?? 'jest', e2eTestRunner: options.e2eTestRunner ?? 'jest',
unitTestRunner: options.unitTestRunner,
addPlugin, addPlugin,
}); });
} else if (options.preset === Preset.NodeMonorepo) { } else if (options.preset === Preset.NodeMonorepo) {
@ -329,6 +346,7 @@ async function createPreset(tree: Tree, options: Schema) {
docker: options.docker, docker: options.docker,
rootProject: false, rootProject: false,
e2eTestRunner: options.e2eTestRunner ?? 'jest', e2eTestRunner: options.e2eTestRunner ?? 'jest',
unitTestRunner: options.unitTestRunner,
addPlugin, addPlugin,
useTsSolution: options.workspaces, useTsSolution: options.workspaces,
formatter: options.formatter, formatter: options.formatter,

View File

@ -17,6 +17,7 @@ export interface Schema {
nextSrcDir?: boolean; nextSrcDir?: boolean;
routing?: boolean; routing?: boolean;
standaloneApi?: boolean; standaloneApi?: boolean;
unitTestRunner?: 'jest' | 'vitest' | 'none';
e2eTestRunner?: 'cypress' | 'playwright' | 'jest' | 'detox' | 'none'; e2eTestRunner?: 'cypress' | 'playwright' | 'jest' | 'detox' | 'none';
js?: boolean; js?: boolean;
ssr?: boolean; ssr?: boolean;

View File

@ -90,6 +90,11 @@
"type": "boolean", "type": "boolean",
"default": true "default": true
}, },
"unitTestRunner": {
"description": "The tool to use for running unit tests.",
"type": "string",
"enum": ["jest", "vitest", "none"]
},
"e2eTestRunner": { "e2eTestRunner": {
"description": "The tool to use for running e2e tests.", "description": "The tool to use for running e2e tests.",
"type": "string", "type": "string",