From 093d1a734de103fee34e2c0727dc18a91e14186c Mon Sep 17 00:00:00 2001 From: Caleb Ukle Date: Tue, 18 Jul 2023 10:05:58 -0500 Subject: [PATCH] feat(angular): add playwright to e2eTestRunner option --- .../angular/generators/application.json | 2 +- .../packages/angular/generators/host.json | 2 +- .../packages/angular/generators/remote.json | 2 +- packages/angular/package.json | 1 + packages/angular/project.json | 2 +- .../application/application.spec.ts | 29 +++++++++++++++++++ .../src/generators/application/lib/add-e2e.ts | 22 ++++++++++++++ .../src/generators/application/schema.json | 2 +- .../angular/src/generators/host/schema.json | 2 +- .../angular/src/generators/remote/schema.json | 2 +- packages/angular/src/utils/test-runners.ts | 1 + packages/playwright/package.json | 4 +-- .../generators/configuration/configuration.ts | 2 ++ .../files/playwright.config.ts.template | 13 ++++++--- .../src/generators/configuration/schema.d.ts | 10 +++++++ .../src/generators/configuration/schema.json | 8 +++++ .../playwright/src/generators/init/init.ts | 25 ++++++++++++++++ packages/playwright/src/index.ts | 2 ++ 18 files changed, 118 insertions(+), 13 deletions(-) diff --git a/docs/generated/packages/angular/generators/application.json b/docs/generated/packages/angular/generators/application.json index e9fc7ebb95c75..17e7942ee7767 100644 --- a/docs/generated/packages/angular/generators/application.json +++ b/docs/generated/packages/angular/generators/application.json @@ -102,7 +102,7 @@ }, "e2eTestRunner": { "type": "string", - "enum": ["cypress", "none"], + "enum": ["cypress", "playwright", "none"], "description": "Test runner to use for end to end (E2E) tests.", "default": "cypress" }, diff --git a/docs/generated/packages/angular/generators/host.json b/docs/generated/packages/angular/generators/host.json index c22fff90516d1..12f1033219ad4 100644 --- a/docs/generated/packages/angular/generators/host.json +++ b/docs/generated/packages/angular/generators/host.json @@ -109,7 +109,7 @@ }, "e2eTestRunner": { "type": "string", - "enum": ["cypress", "none"], + "enum": ["cypress", "playwright", "none"], "description": "Test runner to use for end to end (E2E) tests.", "default": "cypress" }, diff --git a/docs/generated/packages/angular/generators/remote.json b/docs/generated/packages/angular/generators/remote.json index e94bd1b698147..aff34090397f2 100644 --- a/docs/generated/packages/angular/generators/remote.json +++ b/docs/generated/packages/angular/generators/remote.json @@ -103,7 +103,7 @@ }, "e2eTestRunner": { "type": "string", - "enum": ["cypress", "none"], + "enum": ["cypress", "playwright", "none"], "description": "Test runner to use for end to end (E2E) tests.", "default": "cypress" }, diff --git a/packages/angular/package.json b/packages/angular/package.json index 45f04bf13dff3..95f0939b28914 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -63,6 +63,7 @@ "@nx/jest": "file:../jest", "@nx/js": "file:../js", "@nx/linter": "file:../linter", + "@nx/playwright": "file:../playwright", "@nx/webpack": "file:../webpack", "@nx/workspace": "file:../workspace" }, diff --git a/packages/angular/project.json b/packages/angular/project.json index 558b5af6e273c..d9dac4070d8b7 100644 --- a/packages/angular/project.json +++ b/packages/angular/project.json @@ -74,5 +74,5 @@ }, "lint": {} }, - "implicitDependencies": ["workspace", "cypress", "jest"] + "implicitDependencies": ["workspace", "playwright", "cypress", "jest"] } diff --git a/packages/angular/src/generators/application/application.spec.ts b/packages/angular/src/generators/application/application.spec.ts index aa9de783ae8dd..c5722cfc4b6b3 100644 --- a/packages/angular/src/generators/application/application.spec.ts +++ b/packages/angular/src/generators/application/application.spec.ts @@ -128,6 +128,23 @@ describe('app', () => { expect(tsconfigE2E).toMatchSnapshot('e2e tsconfig.json'); }); + it('should setup playwright', async () => { + await generateApp(appTree, 'playwright-app', { + e2eTestRunner: E2eTestRunner.Playwright, + }); + + expect( + appTree.exists('apps/playwright-app-e2e/playwright.config.ts') + ).toBeTruthy(); + expect( + appTree.exists('apps/playwright-app-e2e/src/example.spec.ts') + ).toBeTruthy(); + expect( + readProjectConfiguration(appTree, 'playwright-app-e2e')?.targets?.e2e + ?.executor + ).toEqual('@nx/playwright:playwright'); + }); + it('should setup jest with serializers', async () => { await generateApp(appTree); @@ -869,6 +886,18 @@ describe('app', () => { const project = readProjectConfiguration(appTree, 'my-app'); expect(project.targets.build.options['outputPath']).toBe('dist/my-app'); }); + + it('should generate playwright with root project', async () => { + await generateApp(appTree, 'root-app', { + e2eTestRunner: E2eTestRunner.Playwright, + rootProject: true, + }); + expect( + readProjectConfiguration(appTree, 'e2e').targets.e2e.executor + ).toEqual('@nx/playwright:playwright'); + expect(appTree.exists('e2e/playwright.config.ts')).toBeTruthy(); + expect(appTree.exists('e2e/src/example.spec.ts')).toBeTruthy(); + }); }); it('should error correctly when Angular version does not support standalone', async () => { diff --git a/packages/angular/src/generators/application/lib/add-e2e.ts b/packages/angular/src/generators/application/lib/add-e2e.ts index 7e60957d8eb54..c1068acc21c5e 100644 --- a/packages/angular/src/generators/application/lib/add-e2e.ts +++ b/packages/angular/src/generators/application/lib/add-e2e.ts @@ -1,7 +1,11 @@ import { cypressProjectGenerator } from '@nx/cypress'; +import { configurationGenerator as playwrightConfigurationGenerator } from '@nx/playwright'; import type { Tree } from '@nx/devkit'; import { addDependenciesToPackageJson, + addProjectConfiguration, + getPackageManagerCommand, + joinPathFragments, readProjectConfiguration, updateProjectConfiguration, } from '@nx/devkit'; @@ -25,6 +29,24 @@ export async function addE2e(tree: Tree, options: NormalizedSchema) { skipPackageJson: options.skipPackageJson, skipFormat: true, }); + } else if (options.e2eTestRunner === 'playwright') { + addProjectConfiguration(tree, options.e2eProjectName, { + root: options.e2eProjectRoot, + sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'), + targets: {}, + implicitDependencies: [options.name], + }); + await playwrightConfigurationGenerator(tree, { + project: options.e2eProjectName, + skipFormat: true, + skipPackageJson: options.skipPackageJson, + directory: 'src', + js: false, + webServerCommand: `${getPackageManagerCommand().exec} nx serve ${ + options.name + }`, + webServerAddress: `http://localhost:${options.port ?? 4200}`, + }); } } diff --git a/packages/angular/src/generators/application/schema.json b/packages/angular/src/generators/application/schema.json index 0d287467aa3c5..aaa623bd9a9ad 100644 --- a/packages/angular/src/generators/application/schema.json +++ b/packages/angular/src/generators/application/schema.json @@ -105,7 +105,7 @@ }, "e2eTestRunner": { "type": "string", - "enum": ["cypress", "none"], + "enum": ["cypress", "playwright", "none"], "description": "Test runner to use for end to end (E2E) tests.", "default": "cypress" }, diff --git a/packages/angular/src/generators/host/schema.json b/packages/angular/src/generators/host/schema.json index c1d21991c0c34..35d7d6ffce118 100644 --- a/packages/angular/src/generators/host/schema.json +++ b/packages/angular/src/generators/host/schema.json @@ -112,7 +112,7 @@ }, "e2eTestRunner": { "type": "string", - "enum": ["cypress", "none"], + "enum": ["cypress", "playwright", "none"], "description": "Test runner to use for end to end (E2E) tests.", "default": "cypress" }, diff --git a/packages/angular/src/generators/remote/schema.json b/packages/angular/src/generators/remote/schema.json index 42e2532ecd112..7eb37d9d97f95 100644 --- a/packages/angular/src/generators/remote/schema.json +++ b/packages/angular/src/generators/remote/schema.json @@ -106,7 +106,7 @@ }, "e2eTestRunner": { "type": "string", - "enum": ["cypress", "none"], + "enum": ["cypress", "playwright", "none"], "description": "Test runner to use for end to end (E2E) tests.", "default": "cypress" }, diff --git a/packages/angular/src/utils/test-runners.ts b/packages/angular/src/utils/test-runners.ts index f0950951f3ef1..642a233683ef5 100644 --- a/packages/angular/src/utils/test-runners.ts +++ b/packages/angular/src/utils/test-runners.ts @@ -5,5 +5,6 @@ export enum UnitTestRunner { export enum E2eTestRunner { Cypress = 'cypress', + Playwright = 'playwright', None = 'none', } diff --git a/packages/playwright/package.json b/packages/playwright/package.json index 620cb529022ca..f85b790be75b2 100644 --- a/packages/playwright/package.json +++ b/packages/playwright/package.json @@ -16,8 +16,8 @@ "Playwright", "CLI" ], - "main": "./index", - "typings": "./index.d.ts", + "main": "./src/index", + "typings": "./src/index.d.ts", "author": "Victor Savkin", "license": "MIT", "bugs": { diff --git a/packages/playwright/src/generators/configuration/configuration.ts b/packages/playwright/src/generators/configuration/configuration.ts index a0c04a8cbd2c9..d69ba968280e0 100644 --- a/packages/playwright/src/generators/configuration/configuration.ts +++ b/packages/playwright/src/generators/configuration/configuration.ts @@ -67,8 +67,10 @@ function addE2eTarget(tree: Tree, options: ConfigurationGeneratorSchema) { throw new Error(`Project ${options.project} already has an e2e target. Rename or remove the existing e2e target.`); } + projectConfig.targets ??= {}; projectConfig.targets.e2e = { executor: '@nx/playwright:playwright', + outputs: [`dist/playwright/${projectConfig.root}`], options: {}, }; updateProjectConfiguration(tree, options.project, projectConfig); diff --git a/packages/playwright/src/generators/configuration/files/playwright.config.ts.template b/packages/playwright/src/generators/configuration/files/playwright.config.ts.template index f5067f8a64624..9a4f31fa6e56a 100644 --- a/packages/playwright/src/generators/configuration/files/playwright.config.ts.template +++ b/packages/playwright/src/generators/configuration/files/playwright.config.ts.template @@ -11,6 +11,7 @@ import { defineConfig, devices } from '@playwright/test'; */ export default defineConfig({ testDir: './<%= directory %>', + outputDir: '<%= offsetFromRoot %>dist/playwright/<%= projectRoot %>/test-output', /* Run tests in files in parallel */ fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ @@ -25,7 +26,7 @@ export default defineConfig({ 'html', { outputFolder: - '<%= offsetFromRoot %>/dist/playwright/<%= projectRoot %>/playwright-report', + '<%= offsetFromRoot %>dist/playwright/<%= projectRoot %>/playwright-report', }, ], ], @@ -76,10 +77,14 @@ export default defineConfig({ // }, ], - /* Run your local dev server before starting the tests */ - // webServer: { + /* Run your local dev server before starting the tests */<% if(webServerCommand && webServerAddress) {%> + webServer: { + command: '<%= webServerCommand %>', + url: '<%= webServerAddress %>', + reuseExistingServer: !process.env.CI, + },<% } else {%>// webServer: { // command: 'npm run start', // url: 'http://127.0.0.1:3000', // reuseExistingServer: !process.env.CI, - // }, + // },<% } %> }); diff --git a/packages/playwright/src/generators/configuration/schema.d.ts b/packages/playwright/src/generators/configuration/schema.d.ts index 99213e3ed939f..73fb2258298f3 100644 --- a/packages/playwright/src/generators/configuration/schema.d.ts +++ b/packages/playwright/src/generators/configuration/schema.d.ts @@ -4,4 +4,14 @@ export interface ConfigurationGeneratorSchema { js: boolean; // default is false skipFormat: boolean; skipPackageJson: boolean; + /** + * command to give playwright to run the web server + * @example: "npx nx serve my-fe-app" + **/ + webServerCommand?: string; + /** + * address + * @example: "http://localhost:4200" + **/ + webServerAddress?: string; } diff --git a/packages/playwright/src/generators/configuration/schema.json b/packages/playwright/src/generators/configuration/schema.json index 26fded6567b08..6d3b2ad2e6632 100644 --- a/packages/playwright/src/generators/configuration/schema.json +++ b/packages/playwright/src/generators/configuration/schema.json @@ -25,6 +25,14 @@ "description": "Generate JavaScript files rather than TypeScript files.", "default": false }, + "webServerCommand": { + "type": "string", + "description": "The command to start the web server." + }, + "webServerAddress": { + "type": "string", + "description": "The address of the web server." + }, "skipFormat": { "description": "Skip formatting files.", "type": "boolean", diff --git a/packages/playwright/src/generators/init/init.ts b/packages/playwright/src/generators/init/init.ts index f904c5d10b727..085d4b083ab03 100644 --- a/packages/playwright/src/generators/init/init.ts +++ b/packages/playwright/src/generators/init/init.ts @@ -5,6 +5,7 @@ import { GeneratorCallback, runTasksInSerial, Tree, + updateJson, } from '@nx/devkit'; import { InitGeneratorSchema } from './schema'; import { nxVersion, playwrightVersion } from '../../utils/versions'; @@ -26,6 +27,30 @@ export async function initGenerator(tree: Tree, options: InitGeneratorSchema) { if (!options.skipFormat) { await formatFiles(tree); } + + if (tree.exists('.vscode/extensions.json')) { + updateJson(tree, '.vscode/extensions.json', (json) => { + json.recommendations ??= []; + + const recs = new Set(json.recommendations); + recs.add('ms-playwright.playwright'); + + json.recommendations = Array.from(recs); + return json; + }); + } else { + tree.write( + '.vscode/extensions.json', + JSON.stringify( + { + recommendations: ['ms-playwright.playwright'], + }, + null, + 2 + ) + ); + } + return runTasksInSerial(...tasks); } diff --git a/packages/playwright/src/index.ts b/packages/playwright/src/index.ts index 8b96dbdf4ae12..d404de56a7403 100644 --- a/packages/playwright/src/index.ts +++ b/packages/playwright/src/index.ts @@ -2,3 +2,5 @@ export { playwrightExecutor, PlaywrightExecutorSchema, } from './executors/playwright/playwright'; +export { initGenerator } from './generators/init/init'; +export { configurationGenerator } from './generators/configuration/configuration';