fix(testing): playwright plugin clean up (#18447)

This commit is contained in:
Caleb Ukle 2023-08-02 15:14:17 -05:00 committed by GitHub
parent 1dcb80d447
commit 5e51cd6eb5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 112 additions and 53 deletions

View File

@ -132,7 +132,7 @@ jobs:
GIT_COMMITTER_NAME: Test GIT_COMMITTER_NAME: Test
NX_E2E_CI_CACHE_KEY: e2e-circleci-<< parameters.os >> NX_E2E_CI_CACHE_KEY: e2e-circleci-<< parameters.os >>
SELECTED_PM: << parameters.pm >> SELECTED_PM: << parameters.pm >>
NX_E2E_RUN_CYPRESS: 'true' NX_E2E_RUN_E2E: 'true'
NX_VERBOSE_LOGGING: 'false' NX_VERBOSE_LOGGING: 'false'
NX_NATIVE_LOGGING: 'false' NX_NATIVE_LOGGING: 'false'
NX_PERF_LOGGING: 'false' NX_PERF_LOGGING: 'false'

View File

@ -415,7 +415,7 @@ jobs:
YARN_REGISTRY: http://localhost:4872 YARN_REGISTRY: http://localhost:4872
NX_CACHE_DIRECTORY: 'tmp' NX_CACHE_DIRECTORY: 'tmp'
NX_E2E_SKIP_BUILD_CLEANUP: 'true' NX_E2E_SKIP_BUILD_CLEANUP: 'true'
NX_E2E_RUN_CYPRESS: 'true' NX_E2E_RUN_E2E: 'true'
NX_E2E_VERBOSE_LOGGING: 'true' NX_E2E_VERBOSE_LOGGING: 'true'
NX_PERF_LOGGING: 'false' NX_PERF_LOGGING: 'false'
NX_DAEMON: 'true' NX_DAEMON: 'true'

View File

@ -303,7 +303,7 @@ jobs:
npm_config_registry: http://localhost:4872 npm_config_registry: http://localhost:4872
NX_CACHE_DIRECTORY: 'tmp' NX_CACHE_DIRECTORY: 'tmp'
NX_E2E_SKIP_BUILD_CLEANUP: 'true' NX_E2E_SKIP_BUILD_CLEANUP: 'true'
NX_E2E_RUN_CYPRESS: 'true' NX_E2E_RUN_E2E: 'true'
NX_E2E_VERBOSE_LOGGING: 'true' NX_E2E_VERBOSE_LOGGING: 'true'
NX_PERF_LOGGING: 'false' NX_PERF_LOGGING: 'false'
NX_DAEMON: 'true' NX_DAEMON: 'true'

View File

@ -150,6 +150,11 @@
"uiPort": { "uiPort": {
"type": "string", "type": "string",
"description": "Port to serve UI on, 0 for any free port; specifying this option opens UI in a browser tab" "description": "Port to serve UI on, 0 for any free port; specifying this option opens UI in a browser tab"
},
"skipInstall": {
"type": "boolean",
"description": "Skip running playwright install before running playwright tests. This is to ensure that playwright browsers are installed before running tests.",
"default": false
} }
}, },
"required": [], "required": [],

View File

@ -19,6 +19,11 @@
"default": false, "default": false,
"description": "Do not add dependencies to `package.json`.", "description": "Do not add dependencies to `package.json`.",
"x-priority": "internal" "x-priority": "internal"
},
"skipInstall": {
"type": "boolean",
"description": "Skip running `playwright install`. This is to ensure that playwright browsers are installed.",
"default": false
} }
}, },
"required": [], "required": [],

View File

@ -2,7 +2,6 @@ import { names } from '@nx/devkit';
import { import {
checkFilesExist, checkFilesExist,
cleanupProject, cleanupProject,
ensurePlaywrightBrowsersInstallation,
getSize, getSize,
killPorts, killPorts,
killProcessAndPorts, killProcessAndPorts,
@ -11,7 +10,7 @@ import {
removeFile, removeFile,
runCLI, runCLI,
runCommandUntil, runCommandUntil,
runCypressTests, runE2ETests,
tmpProjPath, tmpProjPath,
uniq, uniq,
updateFile, updateFile,
@ -98,7 +97,7 @@ describe('Angular Projects', () => {
); );
// check e2e tests // check e2e tests
if (runCypressTests()) { if (runE2ETests()) {
const e2eResults = runCLI(`e2e ${app1}-e2e --no-watch`); const e2eResults = runCLI(`e2e ${app1}-e2e --no-watch`);
expect(e2eResults).toContain('All specs passed!'); expect(e2eResults).toContain('All specs passed!');
expect(await killPorts()).toBeTruthy(); expect(await killPorts()).toBeTruthy();
@ -131,8 +130,7 @@ describe('Angular Projects', () => {
`generate @nx/angular:app ${app} --e2eTestRunner=playwright --no-interactive` `generate @nx/angular:app ${app} --e2eTestRunner=playwright --no-interactive`
); );
if (runCypressTests()) { if (runE2ETests()) {
ensurePlaywrightBrowsersInstallation();
const e2eResults = runCLI(`e2e ${app}-e2e`); const e2eResults = runCLI(`e2e ${app}-e2e`);
expect(e2eResults).toContain( expect(e2eResults).toContain(
`Successfully ran target e2e for project ${app}-e2e` `Successfully ran target e2e for project ${app}-e2e`

View File

@ -4,7 +4,7 @@ import {
createFile, createFile,
newProject, newProject,
runCLI, runCLI,
runCypressTests, runE2ETests,
uniq, uniq,
updateFile, updateFile,
updateProjectConfig, updateProjectConfig,
@ -39,7 +39,7 @@ describe('Angular Cypress Component Tests', () => {
runCLI( runCLI(
`generate @nx/angular:cypress-component-configuration --project=${appName} --generate-tests --no-interactive` `generate @nx/angular:cypress-component-configuration --project=${appName} --generate-tests --no-interactive`
); );
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`component-test ${appName} --no-watch`)).toContain( expect(runCLI(`component-test ${appName} --no-watch`)).toContain(
'All specs passed!' 'All specs passed!'
); );
@ -50,7 +50,7 @@ describe('Angular Cypress Component Tests', () => {
runCLI( runCLI(
`generate @nx/angular:cypress-component-configuration --project=${usedInAppLibName} --generate-tests --no-interactive` `generate @nx/angular:cypress-component-configuration --project=${usedInAppLibName} --generate-tests --no-interactive`
); );
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`component-test ${usedInAppLibName} --no-watch`)).toContain( expect(runCLI(`component-test ${usedInAppLibName} --no-watch`)).toContain(
'All specs passed!' 'All specs passed!'
); );
@ -70,7 +70,7 @@ describe('Angular Cypress Component Tests', () => {
runCLI( runCLI(
`generate @nx/angular:cypress-component-configuration --project=${buildableLibName} --generate-tests --build-target=${appName}:build --no-interactive` `generate @nx/angular:cypress-component-configuration --project=${buildableLibName} --generate-tests --build-target=${appName}:build --no-interactive`
); );
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain( expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
'All specs passed!' 'All specs passed!'
); );
@ -92,7 +92,7 @@ describe('Angular Cypress Component Tests', () => {
} }
); );
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain( expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
'All specs passed!' 'All specs passed!'
); );
@ -108,7 +108,7 @@ describe('Angular Cypress Component Tests', () => {
updateBuilableLibTestsToAssertAppStyles(appName, buildableLibName); updateBuilableLibTestsToAssertAppStyles(appName, buildableLibName);
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain( expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
'All specs passed!' 'All specs passed!'
); );
@ -122,7 +122,7 @@ describe('Angular Cypress Component Tests', () => {
checkFilesExist('tailwind.config.js'); checkFilesExist('tailwind.config.js');
checkFilesDoNotExist(`libs/${buildableLibName}/tailwind.config.js`); checkFilesDoNotExist(`libs/${buildableLibName}/tailwind.config.js`);
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain( expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
'All specs passed!' 'All specs passed!'
); );

View File

@ -8,7 +8,7 @@ import {
newProject, newProject,
readJson, readJson,
runCLI, runCLI,
runCypressTests, runE2ETests,
uniq, uniq,
updateFile, updateFile,
updateJson, updateJson,
@ -200,7 +200,7 @@ async function testCtAndE2eInProject(
`generate @nx/${projectType}:cypress-component-configuration --project=${appName} --generate-tests --no-interactive` `generate @nx/${projectType}:cypress-component-configuration --project=${appName} --generate-tests --no-interactive`
); );
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`run ${appName}:component-test --no-watch`)).toContain( expect(runCLI(`run ${appName}:component-test --no-watch`)).toContain(
'All specs passed!' 'All specs passed!'
); );
@ -208,7 +208,7 @@ async function testCtAndE2eInProject(
runCLI(`generate @nx/cypress:e2e --project=${appName} --no-interactive`); runCLI(`generate @nx/cypress:e2e --project=${appName} --no-interactive`);
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`run ${appName}:e2e --no-watch`)).toContain( expect(runCLI(`run ${appName}:e2e --no-watch`)).toContain(
'All specs passed!' 'All specs passed!'
); );

View File

@ -2,7 +2,7 @@ import {
createFile, createFile,
newProject, newProject,
runCLI, runCLI,
runCypressTests, runE2ETests,
uniq, uniq,
updateFile, updateFile,
} from '@nx/e2e/utils'; } from '@nx/e2e/utils';
@ -17,13 +17,13 @@ describe('NextJs Component Testing', () => {
it('should test a NextJs app', () => { it('should test a NextJs app', () => {
const appName = uniq('next-app'); const appName = uniq('next-app');
createAppWithCt(appName); createAppWithCt(appName);
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`component-test ${appName} --no-watch`)).toContain( expect(runCLI(`component-test ${appName} --no-watch`)).toContain(
'All specs passed!' 'All specs passed!'
); );
} }
addTailwindToApp(appName); addTailwindToApp(appName);
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`component-test ${appName} --no-watch`)).toContain( expect(runCLI(`component-test ${appName} --no-watch`)).toContain(
'All specs passed!' 'All specs passed!'
); );
@ -33,13 +33,13 @@ describe('NextJs Component Testing', () => {
it('should test a NextJs lib', async () => { it('should test a NextJs lib', async () => {
const libName = uniq('next-lib'); const libName = uniq('next-lib');
createLibWithCt(libName, false); createLibWithCt(libName, false);
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`component-test ${libName} --no-watch`)).toContain( expect(runCLI(`component-test ${libName} --no-watch`)).toContain(
'All specs passed!' 'All specs passed!'
); );
} }
addTailwindToLib(libName); addTailwindToLib(libName);
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`component-test ${libName} --no-watch`)).toContain( expect(runCLI(`component-test ${libName} --no-watch`)).toContain(
'All specs passed!' 'All specs passed!'
); );
@ -49,14 +49,14 @@ describe('NextJs Component Testing', () => {
it('should test a NextJs buildable lib', async () => { it('should test a NextJs buildable lib', async () => {
const buildableLibName = uniq('next-buildable-lib'); const buildableLibName = uniq('next-buildable-lib');
createLibWithCt(buildableLibName, true); createLibWithCt(buildableLibName, true);
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain( expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
'All specs passed!' 'All specs passed!'
); );
} }
addTailwindToLib(buildableLibName); addTailwindToLib(buildableLibName);
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain( expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
'All specs passed!' 'All specs passed!'
); );

View File

@ -4,7 +4,7 @@ import {
readJson, readJson,
runCLI, runCLI,
runCLIAsync, runCLIAsync,
runCypressTests, runE2ETests,
} from '../../utils'; } from '../../utils';
export async function checkApp( export async function checkApp(
@ -40,7 +40,7 @@ export async function checkApp(
expect(packageJson.dependencies['react-dom']).toBeDefined(); expect(packageJson.dependencies['react-dom']).toBeDefined();
expect(packageJson.dependencies.next).toBeDefined(); expect(packageJson.dependencies.next).toBeDefined();
if (opts.checkE2E && runCypressTests()) { if (opts.checkE2E && runE2ETests()) {
const e2eResults = runCLI( const e2eResults = runCLI(
`e2e ${appName}-e2e --no-watch --configuration=production` `e2e ${appName}-e2e --no-watch --configuration=production`
); );

View File

@ -7,7 +7,6 @@ import {
readProjectConfig, readProjectConfig,
runCLI, runCLI,
runCLIAsync, runCLIAsync,
runCypressTests,
uniq, uniq,
updateFile, updateFile,
} from '@nx/e2e/utils'; } from '@nx/e2e/utils';

View File

@ -9,7 +9,7 @@ import {
readFile, readFile,
runCLI, runCLI,
runCLIAsync, runCLIAsync,
runCypressTests, runE2ETests,
uniq, uniq,
updateFile, updateFile,
updateJson, updateJson,
@ -363,7 +363,7 @@ async function testGeneratedApp(
'Test Suites: 1 passed, 1 total' 'Test Suites: 1 passed, 1 total'
); );
if (opts.checkE2E && runCypressTests()) { if (opts.checkE2E && runE2ETests()) {
const e2eResults = runCLI(`e2e ${appName}-e2e --no-watch`); const e2eResults = runCLI(`e2e ${appName}-e2e --no-watch`);
expect(e2eResults).toContain('All specs passed!'); expect(e2eResults).toContain('All specs passed!');
expect(await killPorts()).toBeTruthy(); expect(await killPorts()).toBeTruthy();

View File

@ -4,7 +4,7 @@ import {
ensureCypressInstallation, ensureCypressInstallation,
newProject, newProject,
runCLI, runCLI,
runCypressTests, runE2ETests,
uniq, uniq,
updateFile, updateFile,
updateJson, updateJson,
@ -147,7 +147,7 @@ export default Input;
runCLI( runCLI(
`generate @nx/react:cypress-component-configuration --project=${appName} --generate-tests` `generate @nx/react:cypress-component-configuration --project=${appName} --generate-tests`
); );
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`component-test ${appName} --no-watch`)).toContain( expect(runCLI(`component-test ${appName} --no-watch`)).toContain(
'All specs passed!' 'All specs passed!'
); );
@ -158,7 +158,7 @@ export default Input;
runCLI( runCLI(
`generate @nx/react:cypress-component-configuration --project=${usedInAppLibName} --generate-tests` `generate @nx/react:cypress-component-configuration --project=${usedInAppLibName} --generate-tests`
); );
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`component-test ${usedInAppLibName} --no-watch`)).toContain( expect(runCLI(`component-test ${usedInAppLibName} --no-watch`)).toContain(
'All specs passed!' 'All specs passed!'
); );
@ -190,7 +190,7 @@ describe(Input.name, () => {
`generate @nx/react:cypress-component-configuration --project=${buildableLibName} --generate-tests --build-target=${appName}:build` `generate @nx/react:cypress-component-configuration --project=${buildableLibName} --generate-tests --build-target=${appName}:build`
); );
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain( expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
'All specs passed!' 'All specs passed!'
); );
@ -221,7 +221,7 @@ ${content}`;
} }
); );
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain( expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
'All specs passed!' 'All specs passed!'
); );
@ -260,7 +260,7 @@ ${content}`;
return config; return config;
}); });
if (runCypressTests()) { if (runE2ETests()) {
const results = runCLI(`component-test ${appName}`); const results = runCLI(`component-test ${appName}`);
expect(results).toContain('I am from the custom async Webpack config'); expect(results).toContain('I am from the custom async Webpack config');
expect(results).toContain('All specs passed!'); expect(results).toContain('All specs passed!');
@ -291,7 +291,7 @@ export default MyComponent;`;
runCLI( runCLI(
`generate @nrwl/react:cypress-component-configuration --project=${viteLibName} --generate-tests --bundler=vite --build-target=${appName}:build` `generate @nrwl/react:cypress-component-configuration --project=${viteLibName} --generate-tests --bundler=vite --build-target=${appName}:build`
); );
if (runCypressTests()) { if (runE2ETests()) {
expect(runCLI(`component-test ${viteLibName}`)).toContain( expect(runCLI(`component-test ${viteLibName}`)).toContain(
'All specs passed!' 'All specs passed!'
); );

View File

@ -3,6 +3,7 @@ import { packageInstall, tmpProjPath } from './create-project-utils';
import { import {
detectPackageManager, detectPackageManager,
ensureCypressInstallation, ensureCypressInstallation,
ensurePlaywrightBrowsersInstallation,
getNpmMajorVersion, getNpmMajorVersion,
getPublishedVersion, getPublishedVersion,
getStrippedEnvironmentVariables, getStrippedEnvironmentVariables,
@ -175,15 +176,23 @@ export function getPackageManagerCommand({
}[packageManager.trim() as PackageManager]; }[packageManager.trim() as PackageManager];
} }
export function runCypressTests() { export function runE2ETests() {
if (process.env.NX_E2E_RUN_CYPRESS === 'true') { if (process.env.NX_E2E_RUN_E2E === 'true') {
ensureCypressInstallation(); ensureCypressInstallation();
ensurePlaywrightBrowsersInstallation();
return true; return true;
} else { }
console.warn(
'Not running E2E tests because NX_E2E_RUN_E2E is not set to true.'
);
if (process.env.NX_E2E_RUN_CYPRESS) {
console.warn( console.warn(
'Not running Cypress because NX_E2E_RUN_CYPRESS is not set to true.' 'NX_E2E_RUN_CYPRESS is deprecated, use NX_E2E_RUN_E2E instead.'
); );
} }
return false; return false;
} }

View File

@ -9,7 +9,7 @@ import {
removeFile, removeFile,
runCLI, runCLI,
runCLIAsync, runCLIAsync,
runCypressTests, runE2ETests,
uniq, uniq,
} from '@nx/e2e/utils'; } from '@nx/e2e/utils';
@ -35,7 +35,7 @@ describe('Web Components Applications with bundler set as vite', () => {
expect(lintE2eResults).toContain('All files pass linting.'); expect(lintE2eResults).toContain('All files pass linting.');
if (isNotWindows() && runCypressTests()) { if (isNotWindows() && runE2ETests()) {
const e2eResults = runCLI(`e2e ${appName}-e2e --no-watch`); const e2eResults = runCLI(`e2e ${appName}-e2e --no-watch`);
expect(e2eResults).toContain('All specs passed!'); expect(e2eResults).toContain('All specs passed!');
expect(await killPorts()).toBeTruthy(); expect(await killPorts()).toBeTruthy();

View File

@ -12,7 +12,7 @@ import {
rmDist, rmDist,
runCLI, runCLI,
runCLIAsync, runCLIAsync,
runCypressTests, runE2ETests,
tmpProjPath, tmpProjPath,
uniq, uniq,
updateFile, updateFile,
@ -43,7 +43,7 @@ describe('Web Components Applications', () => {
expect(lintE2eResults).toContain('All files pass linting.'); expect(lintE2eResults).toContain('All files pass linting.');
if (isNotWindows() && runCypressTests()) { if (isNotWindows() && runE2ETests()) {
const e2eResults = runCLI(`e2e ${appName}-e2e --no-watch`); const e2eResults = runCLI(`e2e ${appName}-e2e --no-watch`);
expect(e2eResults).toContain('All specs passed!'); expect(e2eResults).toContain('All specs passed!');
expect(await killPorts()).toBeTruthy(); expect(await killPorts()).toBeTruthy();
@ -109,7 +109,7 @@ describe('Web Components Applications', () => {
expect(lintE2eResults).toContain('All files pass linting.'); expect(lintE2eResults).toContain('All files pass linting.');
if (isNotWindows() && runCypressTests()) { if (isNotWindows() && runE2ETests()) {
ensurePlaywrightBrowsersInstallation(); ensurePlaywrightBrowsersInstallation();
const e2eResults = runCLI(`e2e ${appName}-e2e`); const e2eResults = runCLI(`e2e ${appName}-e2e`);
expect(e2eResults).toContain( expect(e2eResults).toContain(

View File

@ -74,5 +74,5 @@
}, },
"lint": {} "lint": {}
}, },
"implicitDependencies": ["workspace", "playwright", "cypress", "jest"] "implicitDependencies": []
} }

View File

@ -11,14 +11,12 @@ import {
import { nxVersion } from '../../../utils/versions'; import { nxVersion } from '../../../utils/versions';
import type { NormalizedSchema } from './normalized-schema'; import type { NormalizedSchema } from './normalized-schema';
import { removeScaffoldedE2e } from './remove-scaffolded-e2e'; import { removeScaffoldedE2e } from './remove-scaffolded-e2e';
import { cypressProjectGenerator } from '@nx/cypress';
export async function addE2e(tree: Tree, options: NormalizedSchema) { export async function addE2e(tree: Tree, options: NormalizedSchema) {
removeScaffoldedE2e(tree, options, options.ngCliSchematicE2ERoot); removeScaffoldedE2e(tree, options, options.ngCliSchematicE2ERoot);
if (options.e2eTestRunner === 'cypress') { if (options.e2eTestRunner === 'cypress') {
const { cypressProjectGenerator } = ensurePackage<
typeof import('@nx/cypress')
>('@nx/cypress', nxVersion);
// TODO: This can call `@nx/web:static-config` generator when ready // TODO: This can call `@nx/web:static-config` generator when ready
addFileServerTarget(tree, options, 'serve-static'); addFileServerTarget(tree, options, 'serve-static');

View File

@ -4,6 +4,9 @@
"type": "commonjs", "type": "commonjs",
"homepage": "https://nx.dev", "homepage": "https://nx.dev",
"private": false, "private": false,
"publishConfig": {
"access": "public"
},
"description": "The Nx Plugin for Playwright contains executors and generators allowing your workspace to use the powerful Playwright integration testing capabilities.", "description": "The Nx Plugin for Playwright contains executors and generators allowing your workspace to use the powerful Playwright integration testing capabilities.",
"keywords": [ "keywords": [
"Monorepo", "Monorepo",

View File

@ -1,6 +1,11 @@
import { fork } from 'child_process'; import { execSync, fork } from 'child_process';
import { ExecutorContext, names } from '@nx/devkit'; import {
import { join } from 'path'; ExecutorContext,
getPackageManagerCommand,
names,
output,
workspaceRoot,
} from '@nx/devkit';
export interface PlaywrightExecutorSchema { export interface PlaywrightExecutorSchema {
/* /*
@ -49,6 +54,7 @@ export interface PlaywrightExecutorSchema {
ui?: boolean; ui?: boolean;
uiHost?: string; uiHost?: string;
uiPort?: string; uiPort?: string;
skipInstall?: boolean;
} }
export async function playwrightExecutor( export async function playwrightExecutor(
@ -63,6 +69,16 @@ export async function playwrightExecutor(
`Unable to find the Project Root for ${context.projectName}. Is it set in the project.json?` `Unable to find the Project Root for ${context.projectName}. Is it set in the project.json?`
); );
} }
if (!options.skipInstall) {
output.log({
title: 'Ensuring Playwright is installed.',
bodyLines: ['use --skipInstall to skip installation.'],
});
const pmc = getPackageManagerCommand();
execSync(`${pmc.exec} playwright install`, { cwd: workspaceRoot });
}
const args = createArgs(options); const args = createArgs(options);
const p = runPlaywright(args, context.root); const p = runPlaywright(args, context.root);

View File

@ -149,6 +149,11 @@
"uiPort": { "uiPort": {
"type": "string", "type": "string",
"description": "Port to serve UI on, 0 for any free port; specifying this option opens UI in a browser tab" "description": "Port to serve UI on, 0 for any free port; specifying this option opens UI in a browser tab"
},
"skipInstall": {
"type": "boolean",
"description": "Skip running playwright install before running playwright tests. This is to ensure that playwright browsers are installed before running tests.",
"default": false
} }
}, },
"required": [] "required": []

View File

@ -3,12 +3,16 @@ import {
convertNxGenerator, convertNxGenerator,
formatFiles, formatFiles,
GeneratorCallback, GeneratorCallback,
getPackageManagerCommand,
output,
runTasksInSerial, runTasksInSerial,
Tree, Tree,
updateJson, updateJson,
workspaceRoot,
} from '@nx/devkit'; } from '@nx/devkit';
import { InitGeneratorSchema } from './schema'; import { InitGeneratorSchema } from './schema';
import { nxVersion, playwrightVersion } from '../../utils/versions'; import { nxVersion, playwrightVersion } from '../../utils/versions';
import { execSync } from 'child_process';
export async function initGenerator(tree: Tree, options: InitGeneratorSchema) { export async function initGenerator(tree: Tree, options: InitGeneratorSchema) {
const tasks: GeneratorCallback[] = []; const tasks: GeneratorCallback[] = [];
@ -51,6 +55,17 @@ export async function initGenerator(tree: Tree, options: InitGeneratorSchema) {
); );
} }
if (!options.skipInstall) {
tasks.push(() => {
output.log({
title: 'Ensuring Playwright is installed.',
bodyLines: ['use --skipInstall to skip installation.'],
});
const pmc = getPackageManagerCommand();
execSync(`${pmc.exec} playwright install`, { cwd: workspaceRoot });
});
}
return runTasksInSerial(...tasks); return runTasksInSerial(...tasks);
} }

View File

@ -1,4 +1,5 @@
export interface InitGeneratorSchema { export interface InitGeneratorSchema {
skipFormat: boolean; skipFormat: boolean;
skipPackageJson: boolean; skipPackageJson: boolean;
skipInstall?: boolean;
} }

View File

@ -16,6 +16,11 @@
"default": false, "default": false,
"description": "Do not add dependencies to `package.json`.", "description": "Do not add dependencies to `package.json`.",
"x-priority": "internal" "x-priority": "internal"
},
"skipInstall": {
"type": "boolean",
"description": "Skip running `playwright install`. This is to ensure that playwright browsers are installed.",
"default": false
} }
}, },
"required": [] "required": []