feat(testing): prompt e2eTestRunner playwright (#18203)

This commit is contained in:
Emily Xiong 2023-08-04 11:14:59 -04:00 committed by GitHub
parent c0d3aa5697
commit ed300d5f82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 89 additions and 66 deletions

View File

@ -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`

View File

@ -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`

View File

@ -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,

View File

@ -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"],

View File

@ -24,6 +24,7 @@ describe('Storybook generators and executors for standalone workspaces - using R
style: 'css',
bundler: 'vite',
packageManager: getSelectedPackageManager(),
e2eTestRunner: 'none',
});
runCLI(

View File

@ -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}`;
}

View File

@ -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');

View File

@ -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;
}

View File

@ -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');
}

View File

@ -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
);
}
}

View File

@ -12,7 +12,6 @@ import {
import { initGenerator as jsInitGenerator } from '@nx/js';
import {
babelPresetReactVersion,
nxVersion,
reactDomVersion,
reactVersion,

View File

@ -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 {

View File

@ -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

View File

@ -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;
}

View File

@ -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"]