Skip to content

Commit

Permalink
cypress: add @cypress/schematic for angular
Browse files Browse the repository at this point in the history
  • Loading branch information
mshima committed Dec 12, 2024
1 parent 9f1f453 commit 3c684f2
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 24 deletions.
8 changes: 8 additions & 0 deletions generators/app/__snapshots__/generator.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,8 @@ exports[`generator - app with default config should match snapshot 1`] = `
"communicationSpringWebsocket": false,
"cucumberTests": false,
"customizeTemplatePaths": [],
"cypressAudit": undefined,
"cypressCoverage": undefined,
"cypressTests": false,
"dasherizedBaseName": "jhipster",
"databaseMigration": undefined,
Expand Down Expand Up @@ -650,6 +652,7 @@ exports[`generator - app with default config should match snapshot 1`] = `
"@angular/cli": "ANGULAR_CLI_VERSION",
"@angular/common": "ANGULAR_COMMON_VERSION",
"@cypress/code-coverage": "CYPRESS_CODE_COVERAGE_VERSION",
"@cypress/schematic": "CYPRESS_SCHEMATIC_VERSION",
"@eslint/js": "ESLINT_JS_VERSION",
"@fortawesome/angular-fontawesome": "FORTAWESOME_ANGULAR_FONTAWESOME_VERSION",
"@fortawesome/fontawesome-svg-core": "FORTAWESOME_FONTAWESOME_SVG_CORE_VERSION",
Expand Down Expand Up @@ -952,6 +955,8 @@ exports[`generator - app with gateway should match snapshot 1`] = `
"communicationSpringWebsocket": false,
"cucumberTests": false,
"customizeTemplatePaths": [],
"cypressAudit": undefined,
"cypressCoverage": undefined,
"cypressTests": false,
"dasherizedBaseName": "jhipster",
"databaseMigration": undefined,
Expand Down Expand Up @@ -1312,6 +1317,7 @@ exports[`generator - app with gateway should match snapshot 1`] = `
"@angular/cli": "ANGULAR_CLI_VERSION",
"@angular/common": "ANGULAR_COMMON_VERSION",
"@cypress/code-coverage": "CYPRESS_CODE_COVERAGE_VERSION",
"@cypress/schematic": "CYPRESS_SCHEMATIC_VERSION",
"@eslint/js": "ESLINT_JS_VERSION",
"@fortawesome/angular-fontawesome": "FORTAWESOME_ANGULAR_FONTAWESOME_VERSION",
"@fortawesome/fontawesome-svg-core": "FORTAWESOME_FONTAWESOME_SVG_CORE_VERSION",
Expand Down Expand Up @@ -1613,6 +1619,8 @@ exports[`generator - app with microservice should match snapshot 1`] = `
"communicationSpringWebsocket": false,
"cucumberTests": false,
"customizeTemplatePaths": [],
"cypressAudit": undefined,
"cypressCoverage": undefined,
"cypressTests": false,
"dasherizedBaseName": "jhipster",
"databaseMigration": undefined,
Expand Down
1 change: 1 addition & 0 deletions generators/base-application/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export type CommonClientServerApplication<Entity> = BaseApplication &
AuthenticationProperties<Entity> &
SpringBootApplication &
ClientApplication &
ExportApplicationPropertiesFromCommand<typeof import('../cypress/command.ts').default> &
ExportApplicationPropertiesFromCommand<typeof import('../git/command.ts').default> &
ExportApplicationPropertiesFromCommand<typeof import('../project-name/command.ts').default> &
ApplicationProperties & {
Expand Down
1 change: 1 addition & 0 deletions generators/client/resources/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
},
"devDependencies": {
"@cypress/code-coverage": "3.13.9",
"@cypress/schematic": "2.5.1",
"babel-loader": "9.2.1",
"babel-plugin-istanbul": "7.0.0",
"cypress": "13.16.1",
Expand Down
9 changes: 9 additions & 0 deletions generators/cypress/__snapshots__/generator.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ exports[`generator - cypress jwt-cypressAudit(false)-angular-withAdminUi(false)-
".yo-rc.json": {
"stateCleared": "modified",
},
"clientRoot/angular.json": {
"stateCleared": "modified",
},
"clientRoot/cypress.config.ts": {
"stateCleared": "modified",
},
Expand Down Expand Up @@ -156,6 +159,9 @@ exports[`generator - cypress oauth2-cypressAudit(false)-angular-withAdminUi(fals
".yo-rc.json": {
"stateCleared": "modified",
},
"angular.json": {
"stateCleared": "modified",
},
"cypress.config.ts": {
"stateCleared": "modified",
},
Expand Down Expand Up @@ -342,6 +348,9 @@ exports[`generator - cypress session-cypressAudit(false)-angular-withAdminUi(fal
".yo-rc.json": {
"stateCleared": "modified",
},
"angular.json": {
"stateCleared": "modified",
},
"cypress.config.ts": {
"stateCleared": "modified",
},
Expand Down
14 changes: 9 additions & 5 deletions generators/cypress/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,23 @@
*/
import type { JHipsterCommandDefinition } from '../../lib/command/index.js';

const command: JHipsterCommandDefinition = {
options: {
const command = {
configs: {
cypressCoverage: {
description: 'Enable Cypress code coverage report generation',
type: Boolean,
cli: {
type: Boolean,
},
scope: 'storage',
},
cypressAudit: {
description: 'Enable cypress-audit/lighthouse report generation',
type: Boolean,
cli: {
type: Boolean,
},
scope: 'storage',
},
},
};
} as const satisfies JHipsterCommandDefinition;

export default command;
127 changes: 121 additions & 6 deletions generators/cypress/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import { cypressEntityFiles, cypressFiles } from './files.js';

const { ANGULAR } = clientFrameworkTypes;

const WAIT_TIMEOUT = 3 * 60000;

export default class CypressGenerator extends BaseApplicationGenerator {
async beforeQueue() {
if (!this.fromBlueprint) {
Expand Down Expand Up @@ -67,6 +69,21 @@ export default class CypressGenerator extends BaseApplicationGenerator {
return this.delegateTasksToBlueprint(() => this.prompting);
}

get configuring() {
return this.asConfiguringTaskGroup({
async configureCypressOptions() {
if (this.jhipsterConfigWithDefaults.cypressCoverage && this.jhipsterConfigWithDefaults.clientBundler === 'experimentalEsbuild') {
this.log.warn('Code coverage for Cypress tests is not supported with the experimental ESBuild bundler.');
this.jhipsterConfig.cypressCoverage = false;
}
},
});
}

get [BaseApplicationGenerator.CONFIGURING]() {
return this.delegateTasksToBlueprint(() => this.configuring);
}

get loading() {
return this.asLoadingTaskGroup({
prepareForTemplates({ application }) {
Expand Down Expand Up @@ -188,22 +205,99 @@ export default class CypressGenerator extends BaseApplicationGenerator {
},

configure({ application }) {
const {
clientFrameworkAngular,
cypressCoverage,
dasherizedBaseName,
devServerPort,
devServerPortProxy: devServerPortE2e = devServerPort,
} = application;

const clientPackageJson = this.createStorage(this.destinationPath(application.clientRootDir!, 'package.json'));
clientPackageJson.merge({
devDependencies: {
'eslint-plugin-cypress': application.nodeDependencies['eslint-plugin-cypress'],
},
scripts: {
e2e: 'npm run e2e:cypress:headed --',
'e2e:headless': 'npm run e2e:cypress --',
'e2e:cypress:headed': 'npm run e2e:cypress -- --headed',
'ci:e2e:run': 'concurrently -k -s first -n application,e2e -c red,blue npm:ci:e2e:server:start npm:e2e:headless',
'ci:e2e:dev': `concurrently -k -s first -n application,e2e -c red,blue npm:app:start npm:e2e:headless`,
cypress: 'cypress open --e2e',
'e2e:cypress': 'cypress run --e2e --browser chrome',
'e2e:cypress:headed': 'npm run e2e:cypress -- --headed',
'e2e:cypress:record': 'npm run e2e:cypress -- --record',
cypress: 'cypress open --e2e',
'e2e:dev': `concurrently -k -s first -n application,e2e -c red,blue npm:app:start npm:e2e`,
'pree2e:headless': 'npm run ci:server:await',
'e2e:headless': 'npm run e2e:cypress --',
...(clientFrameworkAngular
? {
e2e: 'ng e2e',
'e2e:devserver': `concurrently -k -s first -n backend,e2e -c red,blue npm:backend:start "npm run ci:server:await && ng run ${dasherizedBaseName}:cypress-headless${cypressCoverage ? ':instrumenter' : ''}"`,
}
: {
e2e: 'npm run e2e:cypress:headed --',
'e2e:devserver': `concurrently -k -s first -n backend,frontend,e2e -c red,yellow,blue npm:backend:start npm:start "wait-on -t ${WAIT_TIMEOUT} http-get://127.0.0.1:${devServerPortE2e} && npm run e2e:headless -- -c baseUrl=http://localhost:${devServerPortE2e}"`,
}),
},
});
},
cypressSchematics({ application, source }) {
const { clientFrameworkAngular, dasherizedBaseName, clientRootDir } = application;
if (!clientFrameworkAngular) return;

source.mergeClientPackageJson?.({
devDependencies: {
'@cypress/schematic': null,
},
});
this.mergeDestinationJson(`${clientRootDir}angular.json`, {
projects: {
[application.dasherizedBaseName]: {
architect: {
e2e: {
builder: '@cypress/schematic:cypress',
options: {
devServerTarget: `${dasherizedBaseName}:serve`,
watch: false,
headed: true,
browser: 'chrome',
},
configurations: {
production: {
devServerTarget: `${dasherizedBaseName}:serve:production`,
},
},
},
'cypress-headless': {
builder: '@cypress/schematic:cypress',
options: {
devServerTarget: `${dasherizedBaseName}:serve`,
watch: false,
browser: 'chrome',
},
configurations: {
production: {
devServerTarget: `${dasherizedBaseName}:serve:production`,
},
},
},
'cypress-open': {
builder: '@cypress/schematic:cypress',
options: {
devServerTarget: `${dasherizedBaseName}:serve`,
watch: true,
headless: false,
},
configurations: {
production: {
devServerTarget: `${dasherizedBaseName}:serve:production`,
},
},
},
},
},
},
});
},
configureAudits({ application }) {
if (!application.cypressAudit) return;
const clientPackageJson = this.createStorage(this.destinationPath(application.clientRootDir!, 'package.json'));
Expand All @@ -220,7 +314,7 @@ export default class CypressGenerator extends BaseApplicationGenerator {
});
},
configureCoverage({ application, source }) {
const { cypressCoverage, clientFrameworkAngular, dasherizedBaseName } = application;
const { cypressCoverage, clientFrameworkAngular, clientRootDir, dasherizedBaseName } = application;
if (!cypressCoverage) return;
const clientPackageJson = this.createStorage(this.destinationPath(application.clientRootDir!, 'package.json'));
clientPackageJson.merge({
Expand All @@ -241,7 +335,28 @@ export default class CypressGenerator extends BaseApplicationGenerator {
});
if (clientFrameworkAngular) {
// Add 'ng build --configuration instrumenter' support
this.createStorage('angular.json').setPath(`projects.${dasherizedBaseName}.architect.build.configurations.instrumenter`, {});
const e2eConfigurations = {
configurations: {
instrumenter: {
devServerTarget: `${dasherizedBaseName}:serve:instrumenter`,
},
},
};

this.mergeDestinationJson(`${clientRootDir}angular.json`, {
projects: {
[dasherizedBaseName]: {
architect: {
build: { configurations: { instrumenter: {} } },
serve: { configurations: { instrumenter: { buildTarget: `${dasherizedBaseName}:build:instrumenter` } } },
e2e: e2eConfigurations,
'cypress-headless': e2eConfigurations,
'cypress-open': e2eConfigurations,
},
},
},
});

source.addWebpackConfig?.({
config: `targetOptions.configuration === 'instrumenter'
? {
Expand Down
13 changes: 0 additions & 13 deletions generators/java/generators/server/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,7 @@ export default class ServerGenerator extends BaseApplicationGenerator {
});
},
packageJsonE2eScripts({ application }) {
const { devServerPort, devServerPortProxy: devServerPortE2e = devServerPort } = application;
const scriptsStorage = this.packageJson.createStorage('scripts');
const buildCmd = application.buildToolGradle ? 'gradlew' : 'mvnw -ntp';

let applicationWaitTimeout = WAIT_TIMEOUT * (application.applicationTypeGateway ? 2 : 1);
applicationWaitTimeout = application.authenticationTypeOauth2 ? applicationWaitTimeout * 2 : applicationWaitTimeout;
Expand All @@ -110,17 +108,6 @@ export default class ServerGenerator extends BaseApplicationGenerator {
scriptsStorage.set({
'ci:server:await': `echo "Waiting for server at port $npm_package_config_backend_port to start" && wait-on -t ${applicationWaitTimeout} ${applicationEndpoint} && echo "Server at port $npm_package_config_backend_port started"`,
});

// TODO add e2eTests property to application.
if (this.jhipsterConfig.testFrameworks?.includes('cypress')) {
scriptsStorage.set({
'pree2e:headless': 'npm run ci:server:await',
'ci:e2e:run': 'concurrently -k -s first -n application,e2e -c red,blue npm:ci:e2e:server:start npm:e2e:headless',
'ci:e2e:dev': `concurrently -k -s first -n application,e2e -c red,blue "./${buildCmd}" npm:e2e:headless`,
'e2e:dev': `concurrently -k -s first -n application,e2e -c red,blue "./${buildCmd}" npm:e2e`,
'e2e:devserver': `concurrently -k -s first -n backend,frontend,e2e -c red,yellow,blue npm:backend:start npm:start "wait-on -t ${WAIT_TIMEOUT} http-get://127.0.0.1:${devServerPortE2e} && npm run e2e:headless -- -c baseUrl=http://localhost:${devServerPortE2e}"`,
});
}
},
});
}
Expand Down
1 change: 1 addition & 0 deletions lib/types/application/yo-rc.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export type ApplicationConfiguration = Simplify<
ExportStoragePropertiesFromCommand<typeof import('../../../generators/base/command.js').default> &
ExportStoragePropertiesFromCommand<typeof import('../../../generators/bootstrap-application-base/command.js').default> &
ExportStoragePropertiesFromCommand<typeof import('../../../generators/client/command.js').default> &
ExportStoragePropertiesFromCommand<typeof import('../../../generators/cypress/command.js').default> &
ExportStoragePropertiesFromCommand<typeof import('../../../generators/git/command.js').default> &
ExportStoragePropertiesFromCommand<typeof import('../../../generators/java/generators/bootstrap/command.js').default> &
ExportStoragePropertiesFromCommand<typeof import('../../../generators/java/generators/build-tool/command.js').default> &
Expand Down

0 comments on commit 3c684f2

Please sign in to comment.