From b124b97c36b62470ef240c57dbc4d5f9f8d8c19d Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Wed, 22 Mar 2023 14:53:02 -0400 Subject: [PATCH] feat(testing): add static serve target for e2e tests in CI (#15808) --- e2e/web/src/file-server.test.ts | 8 +++- .../__snapshots__/application.spec.ts.snap | 18 ++++++++ .../src/generators/application/lib/add-e2e.ts | 24 +++++++++++ .../cypress-project/cypress-project.spec.ts | 42 +++++++++++++++++++ .../cypress-project/cypress-project.ts | 5 +++ .../generators/application/lib/add-cypress.ts | 8 +++- packages/web/index.ts | 1 + 7 files changed, 103 insertions(+), 3 deletions(-) diff --git a/e2e/web/src/file-server.test.ts b/e2e/web/src/file-server.test.ts index 29c428bc11..927a602009 100644 --- a/e2e/web/src/file-server.test.ts +++ b/e2e/web/src/file-server.test.ts @@ -44,8 +44,12 @@ describe('file-server', () => { const ngAppName = uniq('ng-app'); const reactAppName = uniq('react-app'); - runCLI(`generate @nrwl/angular:app ${ngAppName} --no-interactive`); - runCLI(`generate @nrwl/react:app ${reactAppName} --no-interactive`); + runCLI( + `generate @nrwl/angular:app ${ngAppName} --no-interactive --e2eTestRunner=none` + ); + runCLI( + `generate @nrwl/react:app ${reactAppName} --no-interactive --e2eTestRunner=none` + ); runCLI( `generate @nrwl/web:static-config --buildTarget=${ngAppName}:build --no-interactive` ); diff --git a/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap b/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap index a620a9525a..b94b0842d7 100644 --- a/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap +++ b/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap @@ -474,6 +474,12 @@ Object { "defaultConfiguration": "development", "executor": "@angular-devkit/build-angular:dev-server", }, + "serve-static": Object { + "executor": "@nrwl/angular:file-server", + "options": Object { + "buildTarget": "my-dir-my-app:build", + }, + }, "test": Object { "configurations": Object { "ci": Object { @@ -508,6 +514,9 @@ Object { "targets": Object { "e2e": Object { "configurations": Object { + "ci": Object { + "devServerTarget": "my-dir-my-app:serve-static", + }, "production": Object { "devServerTarget": "my-dir-my-app:serve:production", }, @@ -623,6 +632,12 @@ Object { "defaultConfiguration": "development", "executor": "@angular-devkit/build-angular:dev-server", }, + "serve-static": Object { + "executor": "@nrwl/angular:file-server", + "options": Object { + "buildTarget": "my-app:build", + }, + }, "test": Object { "configurations": Object { "ci": Object { @@ -657,6 +672,9 @@ Object { "targets": Object { "e2e": Object { "configurations": Object { + "ci": Object { + "devServerTarget": "my-app:serve-static", + }, "production": Object { "devServerTarget": "my-app:serve:production", }, diff --git a/packages/angular/src/generators/application/lib/add-e2e.ts b/packages/angular/src/generators/application/lib/add-e2e.ts index d5503fdfa9..bf3b1104cb 100644 --- a/packages/angular/src/generators/application/lib/add-e2e.ts +++ b/packages/angular/src/generators/application/lib/add-e2e.ts @@ -1,12 +1,20 @@ import type { Tree } from '@nrwl/devkit'; import type { NormalizedSchema } from './normalized-schema'; +import { + readProjectConfiguration, + updateProjectConfiguration, +} from '@nrwl/devkit'; import { cypressProjectGenerator } from '@nrwl/cypress'; import { removeScaffoldedE2e } from './remove-scaffolded-e2e'; export async function addE2e(tree: Tree, options: NormalizedSchema) { removeScaffoldedE2e(tree, options, options.ngCliSchematicE2ERoot); + if (options.e2eTestRunner === 'cypress') { + // TODO: This can call `@nrwl/web:static-config` generator once we merge `@nrwl/angular:file-server` into `@nrwl/web:file-server`. + addFileServerTarget(tree, options, 'serve-static'); + await cypressProjectGenerator(tree, { name: options.e2eProjectName, directory: options.directory, @@ -19,3 +27,19 @@ export async function addE2e(tree: Tree, options: NormalizedSchema) { }); } } + +function addFileServerTarget( + tree: Tree, + options: NormalizedSchema, + targetName: string +) { + const projectConfig = readProjectConfiguration(tree, options.name); + projectConfig.targets[targetName] = { + executor: '@nrwl/angular:file-server', + options: { + buildTarget: `${options.name}:build`, + port: options.port, + }, + }; + updateProjectConfiguration(tree, options.name, projectConfig); +} diff --git a/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts b/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts index 13a9952ae7..cd52cc3dba 100644 --- a/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts +++ b/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts @@ -354,6 +354,48 @@ describe('Cypress Project', () => { 'apps/one/two/other-e2e/src/e2e/app.cy.ts', ].forEach((path) => expect(tree.exists(path)).toBeTruthy()); }); + + describe('serve-static', () => { + it('should configure Cypress with ci configuration if serve-static is found', async () => { + const appConfig = readProjectConfiguration(tree, 'my-app'); + appConfig.targets['serve-static'] = { + executor: 'serve-static-executor', + options: {}, + configurations: { + production: {}, + }, + }; + updateProjectConfiguration(tree, 'my-app', appConfig); + + await cypressProjectGenerator(tree, { + ...defaultOptions, + name: 'my-app-e2e', + project: 'my-app', + }); + + const e2eConfig = readProjectConfiguration(tree, 'my-app-e2e'); + expect(e2eConfig.targets.e2e).toMatchObject({ + options: { + devServerTarget: 'my-app:serve', + }, + configurations: { + production: { devServerTarget: 'my-app:serve:production' }, + ci: { devServerTarget: 'my-app:serve-static' }, + }, + }); + }); + + it('should not configure Cypress with ci configuration if serve-static is not found', async () => { + await cypressProjectGenerator(tree, { + ...defaultOptions, + name: 'my-app-e2e', + project: 'my-app', + }); + + const e2eConfig = readProjectConfiguration(tree, 'my-app-e2e'); + expect(e2eConfig.targets.e2e.configurations.ci).toBeUndefined(); + }); + }); }); describe('v9 - v7', () => { diff --git a/packages/cypress/src/generators/cypress-project/cypress-project.ts b/packages/cypress/src/generators/cypress-project/cypress-project.ts index a317ea5a94..643ef7546e 100644 --- a/packages/cypress/src/generators/cypress-project/cypress-project.ts +++ b/packages/cypress/src/generators/cypress-project/cypress-project.ts @@ -154,6 +154,11 @@ function addProject(tree: Tree, options: CypressProjectSchema) { tags: [], implicitDependencies: options.project ? [options.project] : undefined, }; + if (project.targets?.['serve-static']) { + e2eProjectConfig.targets.e2e.configurations.ci = { + devServerTarget: `${options.project}:serve-static`, + }; + } } else { throw new Error(`Either project or baseUrl should be specified.`); } diff --git a/packages/react/src/generators/application/lib/add-cypress.ts b/packages/react/src/generators/application/lib/add-cypress.ts index 45ed092e0c..18ada80ec8 100644 --- a/packages/react/src/generators/application/lib/add-cypress.ts +++ b/packages/react/src/generators/application/lib/add-cypress.ts @@ -1,4 +1,4 @@ -import { ensurePackage, Tree } from '@nrwl/devkit'; +import { ensurePackage, joinPathFragments, Tree } from '@nrwl/devkit'; import { nxVersion } from '../../../utils/versions'; import { NormalizedSchema } from '../schema'; @@ -7,6 +7,12 @@ export async function addCypress(host: Tree, options: NormalizedSchema) { return () => {}; } + const { webStaticServeGenerator } = ensurePackage('@nrwl/web', nxVersion); + await webStaticServeGenerator(host, { + buildTarget: `${options.projectName}:build`, + targetName: 'serve-static', + }); + const { cypressProjectGenerator } = ensurePackage('@nrwl/cypress', nxVersion); return await cypressProjectGenerator(host, { diff --git a/packages/web/index.ts b/packages/web/index.ts index 5bdcea824f..6a66b3065b 100644 --- a/packages/web/index.ts +++ b/packages/web/index.ts @@ -1,2 +1,3 @@ export { webInitGenerator } from './src/generators/init/init'; export { applicationGenerator } from './src/generators/application/application'; +export { webStaticServeGenerator } from './src/generators/static-serve/static-serve-configuration';