feat(testing): prompt e2eTestRunner playwright (#18203)
This commit is contained in:
parent
c0d3aa5697
commit
ed300d5f82
@ -79,6 +79,14 @@ Type: `boolean`
|
||||
|
||||
Generate a Dockerfile for the Node API
|
||||
|
||||
### e2eTestRunner
|
||||
|
||||
Type: `string`
|
||||
|
||||
Choices: [cypress, playwright, none]
|
||||
|
||||
Test runner to use for end to end (E2E) tests.
|
||||
|
||||
### framework
|
||||
|
||||
Type: `string`
|
||||
|
||||
@ -79,6 +79,14 @@ Type: `boolean`
|
||||
|
||||
Generate a Dockerfile for the Node API
|
||||
|
||||
### e2eTestRunner
|
||||
|
||||
Type: `string`
|
||||
|
||||
Choices: [cypress, playwright, none]
|
||||
|
||||
Test runner to use for end to end (E2E) tests.
|
||||
|
||||
### framework
|
||||
|
||||
Type: `string`
|
||||
|
||||
@ -73,7 +73,7 @@
|
||||
"e2eTestRunner": {
|
||||
"description": "The tool to use for running e2e tests.",
|
||||
"type": "string",
|
||||
"enum": ["cypress", "jest", "detox", "none"]
|
||||
"enum": ["cypress", "playwright", "jest", "detox", "none"]
|
||||
}
|
||||
},
|
||||
"additionalProperties": true,
|
||||
|
||||
@ -85,7 +85,7 @@
|
||||
"e2eTestRunner": {
|
||||
"description": "The tool to use for running e2e tests.",
|
||||
"type": "string",
|
||||
"enum": ["cypress", "jest", "detox", "none"]
|
||||
"enum": ["cypress", "playwright", "jest", "detox", "none"]
|
||||
}
|
||||
},
|
||||
"required": ["preset", "name"],
|
||||
|
||||
@ -24,6 +24,7 @@ describe('Storybook generators and executors for standalone workspaces - using R
|
||||
style: 'css',
|
||||
bundler: 'vite',
|
||||
packageManager: getSelectedPackageManager(),
|
||||
e2eTestRunner: 'none',
|
||||
});
|
||||
|
||||
runCLI(
|
||||
|
||||
@ -138,6 +138,7 @@ export function runCreateWorkspace(
|
||||
standaloneApi,
|
||||
docker,
|
||||
nextAppDir,
|
||||
e2eTestRunner,
|
||||
}: {
|
||||
preset: string;
|
||||
appName?: string;
|
||||
@ -153,6 +154,7 @@ export function runCreateWorkspace(
|
||||
routing?: boolean;
|
||||
docker?: boolean;
|
||||
nextAppDir?: boolean;
|
||||
e2eTestRunner?: 'cypress' | 'playwright' | 'jest' | 'detox' | 'none';
|
||||
}
|
||||
) {
|
||||
projName = name;
|
||||
@ -198,6 +200,10 @@ export function runCreateWorkspace(
|
||||
command += ` --package-manager=${packageManager}`;
|
||||
}
|
||||
|
||||
if (e2eTestRunner) {
|
||||
command += ` --e2eTestRunner=${e2eTestRunner}`;
|
||||
}
|
||||
|
||||
if (extraArgs) {
|
||||
command += ` ${extraArgs}`;
|
||||
}
|
||||
|
||||
@ -31,6 +31,7 @@ describe('create-nx-workspace', () => {
|
||||
packageManager,
|
||||
standaloneApi: false,
|
||||
routing: false,
|
||||
e2eTestRunner: 'none',
|
||||
});
|
||||
|
||||
checkFilesExist('package.json');
|
||||
@ -50,6 +51,7 @@ describe('create-nx-workspace', () => {
|
||||
packageManager,
|
||||
standaloneApi: true,
|
||||
routing: true,
|
||||
e2eTestRunner: 'none',
|
||||
});
|
||||
|
||||
checkFilesExist('package.json');
|
||||
@ -68,6 +70,7 @@ describe('create-nx-workspace', () => {
|
||||
style: 'css',
|
||||
packageManager,
|
||||
bundler: 'vite',
|
||||
e2eTestRunner: 'none',
|
||||
});
|
||||
|
||||
checkFilesExist('package.json');
|
||||
@ -77,7 +80,7 @@ describe('create-nx-workspace', () => {
|
||||
expectCodeIsFormatted();
|
||||
});
|
||||
|
||||
it('should create a workspace with a single react app with webpack at the root', () => {
|
||||
it('should create a workspace with a single react app with webpack and playwright at the root', () => {
|
||||
const wsName = uniq('react');
|
||||
|
||||
runCreateWorkspace(wsName, {
|
||||
@ -86,6 +89,7 @@ describe('create-nx-workspace', () => {
|
||||
style: 'css',
|
||||
packageManager,
|
||||
bundler: 'webpack',
|
||||
e2eTestRunner: 'playwright',
|
||||
});
|
||||
|
||||
checkFilesExist('package.json');
|
||||
@ -144,6 +148,7 @@ describe('create-nx-workspace', () => {
|
||||
packageManager,
|
||||
standaloneApi: false,
|
||||
routing: true,
|
||||
e2eTestRunner: 'none',
|
||||
});
|
||||
expectCodeIsFormatted();
|
||||
});
|
||||
@ -162,6 +167,7 @@ describe('create-nx-workspace', () => {
|
||||
packageManager,
|
||||
standaloneApi: false,
|
||||
routing: false,
|
||||
e2eTestRunner: 'none',
|
||||
})
|
||||
).toThrow();
|
||||
});
|
||||
@ -176,6 +182,7 @@ describe('create-nx-workspace', () => {
|
||||
appName,
|
||||
packageManager,
|
||||
bundler: 'webpack',
|
||||
e2eTestRunner: 'none',
|
||||
});
|
||||
|
||||
expectNoAngularDevkit();
|
||||
@ -195,6 +202,7 @@ describe('create-nx-workspace', () => {
|
||||
appName,
|
||||
packageManager,
|
||||
bundler: 'vite',
|
||||
e2eTestRunner: 'none',
|
||||
});
|
||||
|
||||
expectNoAngularDevkit();
|
||||
@ -213,6 +221,7 @@ describe('create-nx-workspace', () => {
|
||||
appName,
|
||||
nextAppDir: false,
|
||||
packageManager,
|
||||
e2eTestRunner: 'none',
|
||||
});
|
||||
|
||||
checkFilesExist(`apps/${appName}/pages/index.tsx`);
|
||||
@ -230,6 +239,7 @@ describe('create-nx-workspace', () => {
|
||||
nextAppDir: true,
|
||||
appName,
|
||||
packageManager,
|
||||
e2eTestRunner: 'none',
|
||||
});
|
||||
|
||||
checkFilesExist('app/page.tsx');
|
||||
@ -247,6 +257,7 @@ describe('create-nx-workspace', () => {
|
||||
nextAppDir: false,
|
||||
appName,
|
||||
packageManager,
|
||||
e2eTestRunner: 'none',
|
||||
});
|
||||
|
||||
checkFilesExist('pages/index.tsx');
|
||||
|
||||
@ -49,6 +49,7 @@ interface ReactArguments extends BaseArguments {
|
||||
style: string;
|
||||
bundler: 'webpack' | 'vite' | 'rspack';
|
||||
nextAppDir: boolean;
|
||||
e2eTestRunner: 'none' | 'cypress' | 'playwright';
|
||||
}
|
||||
|
||||
interface AngularArguments extends BaseArguments {
|
||||
@ -58,6 +59,7 @@ interface AngularArguments extends BaseArguments {
|
||||
style: string;
|
||||
routing: boolean;
|
||||
standaloneApi: boolean;
|
||||
e2eTestRunner: 'none' | 'cypress' | 'playwright';
|
||||
}
|
||||
|
||||
interface NodeArguments extends BaseArguments {
|
||||
@ -147,6 +149,11 @@ export const commandsObject: yargs.Argv<Arguments> = yargs
|
||||
.option('nextAppDir', {
|
||||
describe: chalk.dim`Enable the App Router for Next.js`,
|
||||
type: 'boolean',
|
||||
})
|
||||
.option('e2eTestRunner', {
|
||||
describe: chalk.dim`Test runner to use for end to end (E2E) tests.`,
|
||||
choices: ['cypress', 'playwright', 'none'],
|
||||
type: 'string',
|
||||
}),
|
||||
withNxCloud,
|
||||
withCI,
|
||||
@ -448,6 +455,7 @@ async function determineReactOptions(
|
||||
let style: undefined | string = undefined;
|
||||
let appName: string;
|
||||
let bundler: undefined | 'webpack' | 'vite' | 'rspack' = undefined;
|
||||
let e2eTestRunner: undefined | 'none' | 'cypress' | 'playwright' = undefined;
|
||||
let nextAppDir = false;
|
||||
|
||||
if (parsedArgs.preset && parsedArgs.preset !== Preset.React) {
|
||||
@ -497,8 +505,10 @@ async function determineReactOptions(
|
||||
|
||||
if (preset === Preset.ReactStandalone || preset === Preset.ReactMonorepo) {
|
||||
bundler = await determineReactBundler(parsedArgs);
|
||||
e2eTestRunner = await determineE2eTestRunner(parsedArgs);
|
||||
} else if (preset === Preset.NextJs || preset === Preset.NextJsStandalone) {
|
||||
nextAppDir = await determineNextAppDir(parsedArgs);
|
||||
e2eTestRunner = await determineE2eTestRunner(parsedArgs);
|
||||
}
|
||||
|
||||
if (parsedArgs.style) {
|
||||
@ -549,7 +559,7 @@ async function determineReactOptions(
|
||||
style = reply.style;
|
||||
}
|
||||
|
||||
return { preset, style, appName, bundler, nextAppDir };
|
||||
return { preset, style, appName, bundler, nextAppDir, e2eTestRunner };
|
||||
}
|
||||
|
||||
async function determineAngularOptions(
|
||||
@ -559,6 +569,7 @@ async function determineAngularOptions(
|
||||
let style: string;
|
||||
let appName: string;
|
||||
let standaloneApi: boolean;
|
||||
let e2eTestRunner: undefined | 'none' | 'cypress' | 'playwright' = undefined;
|
||||
let routing: boolean;
|
||||
|
||||
if (parsedArgs.preset && parsedArgs.preset !== Preset.Angular) {
|
||||
@ -609,6 +620,8 @@ async function determineAngularOptions(
|
||||
style = reply.style;
|
||||
}
|
||||
|
||||
e2eTestRunner = await determineE2eTestRunner(parsedArgs);
|
||||
|
||||
if (parsedArgs.standaloneApi !== undefined) {
|
||||
standaloneApi = parsedArgs.standaloneApi;
|
||||
} else {
|
||||
@ -655,7 +668,7 @@ async function determineAngularOptions(
|
||||
routing = reply.routing === 'Yes';
|
||||
}
|
||||
|
||||
return { preset, style, appName, standaloneApi, routing };
|
||||
return { preset, style, appName, standaloneApi, routing, e2eTestRunner };
|
||||
}
|
||||
|
||||
async function determineNodeOptions(
|
||||
@ -950,3 +963,35 @@ async function determineNodeFramework(
|
||||
]);
|
||||
return reply.framework;
|
||||
}
|
||||
|
||||
async function determineE2eTestRunner(
|
||||
parsedArgs: yargs.Arguments<{
|
||||
e2eTestRunner?: 'none' | 'cypress' | 'playwright';
|
||||
}>
|
||||
): Promise<'none' | 'cypress' | 'playwright'> {
|
||||
if (parsedArgs.e2eTestRunner) return parsedArgs.e2eTestRunner;
|
||||
const reply = await enquirer.prompt<{
|
||||
e2eTestRunner: 'none' | 'cypress' | 'playwright';
|
||||
}>([
|
||||
{
|
||||
message: 'Test runner to use for end to end (E2E) tests',
|
||||
type: 'autocomplete',
|
||||
name: 'e2eTestRunner',
|
||||
choices: [
|
||||
{
|
||||
name: 'cypress',
|
||||
message: 'Cypress [ https://www.cypress.io/ ]',
|
||||
},
|
||||
{
|
||||
name: 'playwright',
|
||||
message: 'Playwright [ https://playwright.dev/ ]',
|
||||
},
|
||||
{
|
||||
name: 'none',
|
||||
message: 'None',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
return reply.e2eTestRunner;
|
||||
}
|
||||
|
||||
@ -20,7 +20,6 @@ import { checkForUncommittedChanges } from './check-for-uncommitted-changes';
|
||||
import { cleanUpFiles } from './clean-up-files';
|
||||
import { readNameFromPackageJson } from './read-name-from-package-json';
|
||||
import { renameJsToJsx } from './rename-js-to-jsx';
|
||||
import { setupE2eProject } from './setup-e2e-project';
|
||||
import { setupTsConfig } from './tsconfig-setup';
|
||||
import { writeCracoConfig } from './write-craco-config';
|
||||
import { writeViteConfig } from './write-vite-config';
|
||||
@ -185,7 +184,7 @@ function createTempWorkspace(options: NormalizedOptions) {
|
||||
options.isVite ? 'vite' : 'webpack'
|
||||
} --packageManager=${options.packageManager} ${
|
||||
options.nxCloud ? '--nxCloud' : '--nxCloud=false'
|
||||
}`,
|
||||
} ${options.addE2e ? '--e2eTestRunner=cypress' : '--e2eTestRunner=none'}`,
|
||||
{ stdio: [0, 1, 2] }
|
||||
);
|
||||
|
||||
@ -349,14 +348,6 @@ function cleanUpUnusedFilesAndAddConfigFiles(options: NormalizedOptions) {
|
||||
|
||||
setupTsConfig(options.reactAppName, options.isStandalone);
|
||||
|
||||
if (options.addE2e && !options.isStandalone) {
|
||||
output.log({ title: '📃 Setup e2e tests' });
|
||||
setupE2eProject(options.reactAppName);
|
||||
} else {
|
||||
removeSync(join('apps', `${options.reactAppName}-e2e`));
|
||||
execSync(`${options.pmc.rm} cypress @nx/cypress eslint-plugin-cypress`);
|
||||
}
|
||||
|
||||
if (options.isStandalone) {
|
||||
removeSync('apps');
|
||||
}
|
||||
|
||||
@ -1,46 +0,0 @@
|
||||
import { writeFileSync } from 'fs';
|
||||
import {
|
||||
fileExists,
|
||||
readJsonFile,
|
||||
writeJsonFile,
|
||||
} from '../../../../utils/fileutils';
|
||||
|
||||
export function setupE2eProject(appName: string) {
|
||||
const json = readJsonFile(`apps/${appName}-e2e/project.json`);
|
||||
json.targets.e2e = {
|
||||
executor: 'nx:run-commands',
|
||||
options: {
|
||||
commands: [`nx e2e-serve ${appName}-e2e`, `nx e2e-run ${appName}-e2e`],
|
||||
},
|
||||
};
|
||||
json.targets['e2e-run'] = {
|
||||
executor: '@nx/cypress:cypress',
|
||||
options: {
|
||||
cypressConfig: `apps/${appName}-e2e/cypress.json`,
|
||||
tsConfig: `apps/${appName}-e2e/tsconfig.e2e.json`,
|
||||
baseUrl: 'http://localhost:3000',
|
||||
},
|
||||
};
|
||||
json.targets['e2e-serve'] = {
|
||||
executor: 'nx:run-commands',
|
||||
options: {
|
||||
commands: [`nx serve ${appName}`],
|
||||
readyWhen: 'can now view',
|
||||
},
|
||||
};
|
||||
writeJsonFile(`apps/${appName}-e2e/project.json`, json);
|
||||
|
||||
if (fileExists(`apps/${appName}-e2e/src/integration/app.spec.ts`)) {
|
||||
const integrationE2eTest = `
|
||||
describe('${appName}', () => {
|
||||
beforeEach(() => cy.visit('/'));
|
||||
it('should contain a body', () => {
|
||||
cy.get('body').should('exist');
|
||||
});
|
||||
});`;
|
||||
writeFileSync(
|
||||
`apps/${appName}-e2e/src/integration/app.spec.ts`,
|
||||
integrationE2eTest
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,6 @@ import {
|
||||
|
||||
import { initGenerator as jsInitGenerator } from '@nx/js';
|
||||
import {
|
||||
babelPresetReactVersion,
|
||||
nxVersion,
|
||||
reactDomVersion,
|
||||
reactVersion,
|
||||
|
||||
@ -30,7 +30,7 @@ interface Schema {
|
||||
standaloneApi?: boolean;
|
||||
routing?: boolean;
|
||||
packageManager?: PackageManager;
|
||||
e2eTestRunner?: 'cypress' | 'detox' | 'jest' | 'none';
|
||||
e2eTestRunner?: 'cypress' | 'playwright' | 'detox' | 'jest' | 'none';
|
||||
}
|
||||
|
||||
export interface NormalizedSchema extends Schema {
|
||||
|
||||
@ -76,7 +76,7 @@
|
||||
"e2eTestRunner": {
|
||||
"description": "The tool to use for running e2e tests.",
|
||||
"type": "string",
|
||||
"enum": ["cypress", "jest", "detox", "none"]
|
||||
"enum": ["cypress", "playwright", "jest", "detox", "none"]
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
|
||||
@ -14,6 +14,6 @@ export interface Schema {
|
||||
nextAppDir?: boolean;
|
||||
routing?: boolean;
|
||||
standaloneApi?: boolean;
|
||||
e2eTestRunner?: 'cypress' | 'jest' | 'detox' | 'none';
|
||||
e2eTestRunner?: 'cypress' | 'playwright' | 'jest' | 'detox' | 'none';
|
||||
js?: boolean;
|
||||
}
|
||||
|
||||
@ -88,7 +88,7 @@
|
||||
"e2eTestRunner": {
|
||||
"description": "The tool to use for running e2e tests.",
|
||||
"type": "string",
|
||||
"enum": ["cypress", "jest", "detox", "none"]
|
||||
"enum": ["cypress", "playwright", "jest", "detox", "none"]
|
||||
}
|
||||
},
|
||||
"required": ["preset", "name"]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user