From 20633b7e554816d1defe130b5291d33caac364b1 Mon Sep 17 00:00:00 2001 From: Emily Xiong Date: Wed, 19 Jul 2023 19:05:08 -0400 Subject: [PATCH] feat(testing): prompt e2eTestRunner playwright --- docs/generated/cli/create-nx-workspace.md | 8 +++ .../nx/documents/create-nx-workspace.md | 8 +++ .../packages/workspace/generators/new.json | 2 +- .../packages/workspace/generators/preset.json | 2 +- e2e/storybook/src/storybook-nested.test.ts | 1 + e2e/utils/create-project-utils.ts | 6 +++ .../src/create-nx-workspace.test.ts | 13 ++++- .../bin/create-nx-workspace.ts | 49 ++++++++++++++++++- .../init/implementation/react/index.ts | 11 +---- .../implementation/react/setup-e2e-project.ts | 46 ----------------- packages/react/src/generators/init/init.ts | 1 - packages/workspace/src/generators/new/new.ts | 2 +- .../workspace/src/generators/new/schema.json | 2 +- .../src/generators/preset/schema.d.ts | 2 +- .../src/generators/preset/schema.json | 2 +- 15 files changed, 89 insertions(+), 66 deletions(-) delete mode 100644 packages/nx/src/command-line/init/implementation/react/setup-e2e-project.ts diff --git a/docs/generated/cli/create-nx-workspace.md b/docs/generated/cli/create-nx-workspace.md index ddb7a06eece486..74c10904808c6e 100644 --- a/docs/generated/cli/create-nx-workspace.md +++ b/docs/generated/cli/create-nx-workspace.md @@ -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` diff --git a/docs/generated/packages/nx/documents/create-nx-workspace.md b/docs/generated/packages/nx/documents/create-nx-workspace.md index ddb7a06eece486..74c10904808c6e 100644 --- a/docs/generated/packages/nx/documents/create-nx-workspace.md +++ b/docs/generated/packages/nx/documents/create-nx-workspace.md @@ -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` diff --git a/docs/generated/packages/workspace/generators/new.json b/docs/generated/packages/workspace/generators/new.json index 8a2e3b646eed3b..83420a5a10715c 100644 --- a/docs/generated/packages/workspace/generators/new.json +++ b/docs/generated/packages/workspace/generators/new.json @@ -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, diff --git a/docs/generated/packages/workspace/generators/preset.json b/docs/generated/packages/workspace/generators/preset.json index 4d7ebca3659a02..2e1376287c3b9a 100644 --- a/docs/generated/packages/workspace/generators/preset.json +++ b/docs/generated/packages/workspace/generators/preset.json @@ -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"], diff --git a/e2e/storybook/src/storybook-nested.test.ts b/e2e/storybook/src/storybook-nested.test.ts index a40db2abfcd3fe..8ce7e6443a5d56 100644 --- a/e2e/storybook/src/storybook-nested.test.ts +++ b/e2e/storybook/src/storybook-nested.test.ts @@ -24,6 +24,7 @@ describe('Storybook generators and executors for standalone workspaces - using R style: 'css', bundler: 'vite', packageManager: getSelectedPackageManager(), + e2eTestRunner: 'none', }); runCLI( diff --git a/e2e/utils/create-project-utils.ts b/e2e/utils/create-project-utils.ts index 45f6e55213ec73..f7e950df2be575 100644 --- a/e2e/utils/create-project-utils.ts +++ b/e2e/utils/create-project-utils.ts @@ -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}`; } diff --git a/e2e/workspace-create/src/create-nx-workspace.test.ts b/e2e/workspace-create/src/create-nx-workspace.test.ts index fc78558f8b8632..8dbd7f71260f77 100644 --- a/e2e/workspace-create/src/create-nx-workspace.test.ts +++ b/e2e/workspace-create/src/create-nx-workspace.test.ts @@ -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'); diff --git a/packages/create-nx-workspace/bin/create-nx-workspace.ts b/packages/create-nx-workspace/bin/create-nx-workspace.ts index 5b3143c5093a0f..9c4336a2091035 100644 --- a/packages/create-nx-workspace/bin/create-nx-workspace.ts +++ b/packages/create-nx-workspace/bin/create-nx-workspace.ts @@ -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 = 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; +} diff --git a/packages/nx/src/command-line/init/implementation/react/index.ts b/packages/nx/src/command-line/init/implementation/react/index.ts index 44f7d8167bbd48..d3f0dfb44099ed 100644 --- a/packages/nx/src/command-line/init/implementation/react/index.ts +++ b/packages/nx/src/command-line/init/implementation/react/index.ts @@ -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'); } diff --git a/packages/nx/src/command-line/init/implementation/react/setup-e2e-project.ts b/packages/nx/src/command-line/init/implementation/react/setup-e2e-project.ts deleted file mode 100644 index 9884533c34a278..00000000000000 --- a/packages/nx/src/command-line/init/implementation/react/setup-e2e-project.ts +++ /dev/null @@ -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 - ); - } -} diff --git a/packages/react/src/generators/init/init.ts b/packages/react/src/generators/init/init.ts index 39d12b9f24d510..426dbb6ebf7213 100755 --- a/packages/react/src/generators/init/init.ts +++ b/packages/react/src/generators/init/init.ts @@ -12,7 +12,6 @@ import { import { initGenerator as jsInitGenerator } from '@nx/js'; import { - babelPresetReactVersion, nxVersion, reactDomVersion, reactVersion, diff --git a/packages/workspace/src/generators/new/new.ts b/packages/workspace/src/generators/new/new.ts index f409feb6e2efb7..d4483a5a1897f9 100644 --- a/packages/workspace/src/generators/new/new.ts +++ b/packages/workspace/src/generators/new/new.ts @@ -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 { diff --git a/packages/workspace/src/generators/new/schema.json b/packages/workspace/src/generators/new/schema.json index bf0d8bd40ac084..7e57cf06147546 100644 --- a/packages/workspace/src/generators/new/schema.json +++ b/packages/workspace/src/generators/new/schema.json @@ -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 diff --git a/packages/workspace/src/generators/preset/schema.d.ts b/packages/workspace/src/generators/preset/schema.d.ts index dd75bedfb54ff0..0f72d1030131c9 100644 --- a/packages/workspace/src/generators/preset/schema.d.ts +++ b/packages/workspace/src/generators/preset/schema.d.ts @@ -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; } diff --git a/packages/workspace/src/generators/preset/schema.json b/packages/workspace/src/generators/preset/schema.json index bde4a44af32360..57a8f5a81ca463 100644 --- a/packages/workspace/src/generators/preset/schema.json +++ b/packages/workspace/src/generators/preset/schema.json @@ -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"]