fix(testing): update the cypress plugin implementation (#20314)
This commit is contained in:
parent
5d82a2aab2
commit
a916794318
@ -52,11 +52,6 @@
|
||||
"version": "17.2.0-beta.0",
|
||||
"description": "Add devServerTargets into cypress.config.ts files for @nx/cypress/plugin",
|
||||
"implementation": "./src/migrations/update-17-2-0/add-dev-server-targets-to-cypress-configs"
|
||||
},
|
||||
"add-nx-cypress-plugin": {
|
||||
"version": "17.2.0-beta.0",
|
||||
"description": "Add the @nx/cypress/plugin to nx.json plugins",
|
||||
"implementation": "./src/migrations/update-17-2-0/add-nx-cypress-plugin"
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import {
|
||||
createProjectGraphAsync,
|
||||
logger,
|
||||
parseTargetString,
|
||||
workspaceRoot,
|
||||
} from '@nx/devkit';
|
||||
@ -8,7 +7,6 @@ import { dirname, join, relative } from 'path';
|
||||
import { lstatSync } from 'fs';
|
||||
|
||||
import vitePreprocessor from '../src/plugins/preprocessor-vite';
|
||||
import { ChildProcess, fork } from 'node:child_process';
|
||||
import { createExecutorContext } from '../src/utils/ct-helpers';
|
||||
import { startDevServer } from '../src/utils/start-dev-server';
|
||||
|
||||
|
||||
@ -1,170 +0,0 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import {
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
|
||||
import update from './add-nx-cypress-plugin';
|
||||
import { defineConfig } from 'cypress';
|
||||
import { TempFs } from 'nx/src/internal-testing-utils/temp-fs';
|
||||
import { join } from 'path';
|
||||
|
||||
describe('add-nx-cypress-plugin migration', () => {
|
||||
let tree: Tree;
|
||||
let tempFs: TempFs;
|
||||
|
||||
function mockCypressConfig(cypressConfig: Cypress.ConfigOptions) {
|
||||
jest.mock(
|
||||
join(tempFs.tempDir, 'e2e/cypress.config.ts'),
|
||||
() => ({
|
||||
default: cypressConfig,
|
||||
}),
|
||||
{
|
||||
virtual: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
tempFs = new TempFs('test');
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
tree.root = tempFs.tempDir;
|
||||
await tempFs.createFiles({
|
||||
'e2e/cypress.config.ts': '',
|
||||
'e2e/project.json': '{ "name": "e2e" }',
|
||||
});
|
||||
tree.write('e2e/cypress.config.ts', `console.log('hi');`);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetModules();
|
||||
tempFs.cleanup();
|
||||
});
|
||||
|
||||
it('should remove the e2e target when there are no other options', async () => {
|
||||
mockCypressConfig(
|
||||
defineConfig({
|
||||
env: {
|
||||
devServerTargets: {
|
||||
default: 'my-app:serve',
|
||||
production: 'my-app:serve:production',
|
||||
},
|
||||
ciDevServerTarget: 'my-app:serve-static',
|
||||
},
|
||||
e2e: {
|
||||
specPattern: '**/*.cy.ts',
|
||||
},
|
||||
})
|
||||
);
|
||||
updateProjectConfiguration(tree, 'e2e', {
|
||||
name: 'e2e',
|
||||
root: 'e2e',
|
||||
targets: {
|
||||
e2e: {
|
||||
executor: '@nx/cypress:cypress',
|
||||
options: {
|
||||
devServerTarget: 'my-app:serve',
|
||||
},
|
||||
configurations: {
|
||||
production: {
|
||||
devServerTarget: 'my-app:serve:production',
|
||||
},
|
||||
ci: {
|
||||
devServerTarget: 'my-app:serve-static',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'e2e').targets.e2e).toEqual({
|
||||
configurations: {
|
||||
ci: {
|
||||
devServerTarget: 'my-app:serve-static',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should not the e2e target when it uses a different executor', async () => {
|
||||
const e2eTarget = {
|
||||
executor: '@nx/playwright:playwright',
|
||||
options: {
|
||||
devServerTarget: 'my-app:serve',
|
||||
},
|
||||
configurations: {
|
||||
production: {
|
||||
devServerTarget: 'my-app:serve:production',
|
||||
},
|
||||
ci: {
|
||||
devServerTarget: 'my-app:serve-static',
|
||||
},
|
||||
},
|
||||
};
|
||||
updateProjectConfiguration(tree, 'e2e', {
|
||||
root: 'e2e',
|
||||
targets: {
|
||||
e2e: e2eTarget,
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'e2e').targets.e2e).toEqual(
|
||||
e2eTarget
|
||||
);
|
||||
});
|
||||
|
||||
it('should leave the e2e target with other options', async () => {
|
||||
mockCypressConfig(
|
||||
defineConfig({
|
||||
env: {
|
||||
devServerTargets: {
|
||||
default: 'my-app:serve',
|
||||
production: 'my-app:serve:production',
|
||||
},
|
||||
ciDevServerTarget: 'my-app:serve-static',
|
||||
},
|
||||
e2e: {
|
||||
specPattern: '**/*.cy.ts',
|
||||
},
|
||||
})
|
||||
);
|
||||
updateProjectConfiguration(tree, 'e2e', {
|
||||
root: 'e2e',
|
||||
targets: {
|
||||
e2e: {
|
||||
executor: '@nx/cypress:cypress',
|
||||
options: {
|
||||
devServerTarget: 'my-app:serve',
|
||||
watch: false,
|
||||
},
|
||||
configurations: {
|
||||
production: {
|
||||
devServerTarget: 'my-app:serve:production',
|
||||
},
|
||||
ci: {
|
||||
devServerTarget: 'my-app:serve-static',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'e2e').targets.e2e).toEqual({
|
||||
options: {
|
||||
watch: false,
|
||||
},
|
||||
configurations: {
|
||||
ci: {
|
||||
devServerTarget: 'my-app:serve-static',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,24 +0,0 @@
|
||||
import { formatFiles, getProjects, Tree } from '@nx/devkit';
|
||||
import { createNodes } from '../../plugins/plugin';
|
||||
|
||||
import { createProjectRootMappingsFromProjectConfigurations } from 'nx/src/project-graph/utils/find-project-for-path';
|
||||
import { replaceProjectConfigurationsWithPlugin } from '@nx/devkit/src/utils/replace-project-configuration-with-plugin';
|
||||
|
||||
export default async function update(tree: Tree) {
|
||||
const proj = Object.fromEntries(getProjects(tree).entries());
|
||||
|
||||
const rootMappings = createProjectRootMappingsFromProjectConfigurations(proj);
|
||||
|
||||
await replaceProjectConfigurationsWithPlugin(
|
||||
tree,
|
||||
rootMappings,
|
||||
'@nx/cypress/plugin',
|
||||
createNodes,
|
||||
{
|
||||
targetName: 'e2e',
|
||||
componentTestingTargetName: 'component-test',
|
||||
}
|
||||
);
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -37,6 +37,12 @@ describe('@nx/cypress/plugin', () => {
|
||||
mockCypressConfig(
|
||||
defineConfig({
|
||||
e2e: {
|
||||
env: {
|
||||
devServerTargets: {
|
||||
default: 'my-app:serve',
|
||||
production: 'my-app:serve:production',
|
||||
},
|
||||
},
|
||||
videosFolder: './dist/videos',
|
||||
screenshotsFolder: './dist/screenshots',
|
||||
},
|
||||
@ -58,18 +64,25 @@ describe('@nx/cypress/plugin', () => {
|
||||
"targets": {
|
||||
"e2e": {
|
||||
"cache": true,
|
||||
"executor": "@nx/cypress:cypress",
|
||||
"command": "cypress run --config-file cypress.config.js --e2e",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"command": "cypress run --config-file cypress.config.js --e2e --env.devServerTarget my-app:serve:production",
|
||||
},
|
||||
},
|
||||
"inputs": [
|
||||
"default",
|
||||
"^production",
|
||||
{
|
||||
"externalDependencies": [
|
||||
"cypress",
|
||||
],
|
||||
},
|
||||
],
|
||||
"options": {
|
||||
"cypressConfig": "cypress.config.js",
|
||||
"testingType": "e2e",
|
||||
"cwd": ".",
|
||||
},
|
||||
"outputs": [
|
||||
"{options.videosFolder}",
|
||||
"{options.screenshotsFolder}",
|
||||
"{projectRoot}/dist/videos",
|
||||
"{projectRoot}/dist/screenshots",
|
||||
],
|
||||
@ -110,18 +123,20 @@ describe('@nx/cypress/plugin', () => {
|
||||
"targets": {
|
||||
"component-test": {
|
||||
"cache": true,
|
||||
"executor": "@nx/cypress:cypress",
|
||||
"command": "cypress open --config-file cypress.config.js --component",
|
||||
"inputs": [
|
||||
"default",
|
||||
"^production",
|
||||
{
|
||||
"externalDependencies": [
|
||||
"cypress",
|
||||
],
|
||||
},
|
||||
],
|
||||
"options": {
|
||||
"cypressConfig": "cypress.config.js",
|
||||
"testingType": "component",
|
||||
"cwd": ".",
|
||||
},
|
||||
"outputs": [
|
||||
"{options.videosFolder}",
|
||||
"{options.screenshotsFolder}",
|
||||
"{projectRoot}/dist/videos",
|
||||
"{projectRoot}/dist/screenshots",
|
||||
],
|
||||
@ -138,6 +153,8 @@ describe('@nx/cypress/plugin', () => {
|
||||
defineConfig({
|
||||
e2e: {
|
||||
specPattern: '**/*.cy.ts',
|
||||
videosFolder: './dist/videos',
|
||||
screenshotsFolder: './dist/screenshots',
|
||||
env: {
|
||||
devServerTargets: {
|
||||
default: 'my-app:serve',
|
||||
@ -164,24 +181,27 @@ describe('@nx/cypress/plugin', () => {
|
||||
"targets": {
|
||||
"e2e": {
|
||||
"cache": true,
|
||||
"command": "cypress run --config-file cypress.config.js --e2e",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "my-app:serve:production",
|
||||
"command": "cypress run --config-file cypress.config.js --e2e --env.devServerTarget my-app:serve:production",
|
||||
},
|
||||
},
|
||||
"executor": "@nx/cypress:cypress",
|
||||
"inputs": [
|
||||
"default",
|
||||
"^production",
|
||||
{
|
||||
"externalDependencies": [
|
||||
"cypress",
|
||||
],
|
||||
},
|
||||
],
|
||||
"options": {
|
||||
"cypressConfig": "cypress.config.js",
|
||||
"devServerTarget": "my-app:serve",
|
||||
"testingType": "e2e",
|
||||
"cwd": ".",
|
||||
},
|
||||
"outputs": [
|
||||
"{options.videosFolder}",
|
||||
"{options.screenshotsFolder}",
|
||||
"{projectRoot}/dist/videos",
|
||||
"{projectRoot}/dist/screenshots",
|
||||
],
|
||||
},
|
||||
"e2e-ci": {
|
||||
@ -197,29 +217,32 @@ describe('@nx/cypress/plugin', () => {
|
||||
"inputs": [
|
||||
"default",
|
||||
"^production",
|
||||
{
|
||||
"externalDependencies": [
|
||||
"cypress",
|
||||
],
|
||||
},
|
||||
],
|
||||
"outputs": [
|
||||
"{options.videosFolder}",
|
||||
"{options.screenshotsFolder}",
|
||||
"{projectRoot}/dist/videos",
|
||||
"{projectRoot}/dist/screenshots",
|
||||
],
|
||||
},
|
||||
"e2e-ci--test.cy.ts": {
|
||||
"cache": true,
|
||||
"configurations": undefined,
|
||||
"executor": "@nx/cypress:cypress",
|
||||
"command": "cypress run --config-file cypress.config.js --e2e --env.devServerTarget my-app:serve-static --spec test.cy.ts",
|
||||
"inputs": [
|
||||
"default",
|
||||
"^production",
|
||||
{
|
||||
"externalDependencies": [
|
||||
"cypress",
|
||||
],
|
||||
"options": {
|
||||
"cypressConfig": "cypress.config.js",
|
||||
"devServerTarget": "my-app:serve-static",
|
||||
"spec": "test.cy.ts",
|
||||
"testingType": "e2e",
|
||||
},
|
||||
],
|
||||
"outputs": [
|
||||
"{options.videosFolder}",
|
||||
"{options.screenshotsFolder}",
|
||||
"{projectRoot}/dist/videos",
|
||||
"{projectRoot}/dist/screenshots",
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
@ -2,11 +2,12 @@ import {
|
||||
CreateDependencies,
|
||||
CreateNodes,
|
||||
CreateNodesContext,
|
||||
NxJsonConfiguration,
|
||||
readJsonFile,
|
||||
TargetConfiguration,
|
||||
writeJsonFile,
|
||||
} from '@nx/devkit';
|
||||
import { dirname, extname, join } from 'path';
|
||||
import { dirname, extname, join, relative } from 'path';
|
||||
import { registerTsProject } from '@nx/js/src/internal';
|
||||
|
||||
import { getRootTsConfigPath } from '@nx/js';
|
||||
@ -102,7 +103,7 @@ function getOutputs(
|
||||
}
|
||||
|
||||
const { screenshotsFolder, videosFolder, e2e, component } = cypressConfig;
|
||||
const outputs = ['{options.videosFolder}', '{options.screenshotsFolder}'];
|
||||
const outputs = [];
|
||||
|
||||
if (videosFolder) {
|
||||
outputs.push(getOutput(videosFolder));
|
||||
@ -143,44 +144,39 @@ function buildCypressTargets(
|
||||
) {
|
||||
const cypressConfig = getCypressConfig(configFilePath, context);
|
||||
|
||||
const cypressEnv = {
|
||||
...cypressConfig.env,
|
||||
...cypressConfig.e2e?.env,
|
||||
};
|
||||
|
||||
const devServerTargets: Record<string, string> = cypressEnv?.devServerTargets;
|
||||
|
||||
const relativeConfigPath = relative(projectRoot, configFilePath);
|
||||
|
||||
const namedInputs = getNamedInputs(projectRoot, context);
|
||||
|
||||
const baseTargetConfig: TargetConfiguration<CypressExecutorOptions> = {
|
||||
executor: '@nx/cypress:cypress',
|
||||
const targets: Record<string, TargetConfiguration> = {};
|
||||
|
||||
if ('e2e' in cypressConfig) {
|
||||
targets[options.targetName] = {
|
||||
command: `cypress run --config-file ${relativeConfigPath} --e2e`,
|
||||
options: {
|
||||
cypressConfig: configFilePath,
|
||||
cwd: projectRoot,
|
||||
},
|
||||
};
|
||||
|
||||
const targets: Record<
|
||||
string,
|
||||
TargetConfiguration<CypressExecutorOptions>
|
||||
> = {};
|
||||
|
||||
if ('e2e' in cypressConfig) {
|
||||
const e2eTargetDefaults = readTargetDefaultsForTarget(
|
||||
options.targetName,
|
||||
context.nxJsonConfiguration.targetDefaults,
|
||||
'@nx/cypress:cypress'
|
||||
'run-commands'
|
||||
);
|
||||
|
||||
targets[options.targetName] = {
|
||||
...baseTargetConfig,
|
||||
options: {
|
||||
...baseTargetConfig.options,
|
||||
testingType: 'e2e',
|
||||
},
|
||||
};
|
||||
|
||||
if (e2eTargetDefaults?.cache === undefined) {
|
||||
targets[options.targetName].cache = true;
|
||||
}
|
||||
|
||||
if (e2eTargetDefaults?.inputs === undefined) {
|
||||
targets[options.targetName].inputs =
|
||||
'production' in namedInputs
|
||||
? ['default', '^production']
|
||||
: ['default', '^default'];
|
||||
targets[options.targetName].inputs = getInputs(namedInputs);
|
||||
}
|
||||
|
||||
if (e2eTargetDefaults?.outputs === undefined) {
|
||||
@ -191,17 +187,7 @@ function buildCypressTargets(
|
||||
);
|
||||
}
|
||||
|
||||
const cypressEnv = {
|
||||
...cypressConfig.env,
|
||||
...cypressConfig.e2e?.env,
|
||||
};
|
||||
|
||||
const devServerTargets: Record<string, string> =
|
||||
cypressEnv?.devServerTargets;
|
||||
|
||||
if (devServerTargets?.default) {
|
||||
targets[options.targetName].options.devServerTarget =
|
||||
devServerTargets.default;
|
||||
delete devServerTargets.default;
|
||||
}
|
||||
|
||||
@ -211,7 +197,7 @@ function buildCypressTargets(
|
||||
devServerTargets ?? {}
|
||||
)) {
|
||||
targets[options.targetName].configurations[configuration] = {
|
||||
devServerTarget,
|
||||
command: `cypress run --config-file ${relativeConfigPath} --e2e --env.devServerTarget ${devServerTarget}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -236,23 +222,15 @@ function buildCypressTargets(
|
||||
|
||||
const dependsOn: TargetConfiguration['dependsOn'] = [];
|
||||
const outputs = getOutputs(projectRoot, cypressConfig, 'e2e');
|
||||
const inputs =
|
||||
'production' in namedInputs
|
||||
? ['default', '^production']
|
||||
: ['default', '^default'];
|
||||
const inputs = getInputs(namedInputs);
|
||||
for (const file of specFiles) {
|
||||
const targetName = options.ciTargetName + '--' + file;
|
||||
const relativeSpecFilePath = relative(projectRoot, file);
|
||||
const targetName = options.ciTargetName + '--' + relativeSpecFilePath;
|
||||
targets[targetName] = {
|
||||
...targets[options.targetName],
|
||||
outputs,
|
||||
inputs,
|
||||
cache: true,
|
||||
configurations: undefined,
|
||||
options: {
|
||||
...targets[options.targetName].options,
|
||||
devServerTarget: ciDevServerTarget,
|
||||
spec: file,
|
||||
},
|
||||
command: `cypress run --config-file ${relativeConfigPath} --e2e --env.devServerTarget ${ciDevServerTarget} --spec ${relativeSpecFilePath}`,
|
||||
};
|
||||
dependsOn.push({
|
||||
target: targetName,
|
||||
@ -281,10 +259,9 @@ function buildCypressTargets(
|
||||
|
||||
// This will not override the e2e target if it is the same
|
||||
targets[options.componentTestingTargetName] ??= {
|
||||
...baseTargetConfig,
|
||||
command: `cypress open --config-file ${relativeConfigPath} --component`,
|
||||
options: {
|
||||
...baseTargetConfig.options,
|
||||
testingType: 'component',
|
||||
cwd: projectRoot,
|
||||
},
|
||||
};
|
||||
|
||||
@ -294,9 +271,7 @@ function buildCypressTargets(
|
||||
|
||||
if (componentTestingTargetDefaults?.inputs === undefined) {
|
||||
targets[options.componentTestingTargetName].inputs =
|
||||
'production' in namedInputs
|
||||
? ['default', '^production']
|
||||
: ['default', '^default'];
|
||||
getInputs(namedInputs);
|
||||
}
|
||||
|
||||
if (componentTestingTargetDefaults?.outputs === undefined) {
|
||||
@ -344,3 +319,16 @@ function normalizeOptions(options: CypressPluginOptions): CypressPluginOptions {
|
||||
options.ciTargetName ??= 'e2e-ci';
|
||||
return options;
|
||||
}
|
||||
function getInputs(
|
||||
namedInputs: NxJsonConfiguration['namedInputs']
|
||||
): TargetConfiguration['inputs'] {
|
||||
return [
|
||||
...('production' in namedInputs
|
||||
? ['default', '^production']
|
||||
: ['default', '^default']),
|
||||
|
||||
{
|
||||
externalDependencies: ['cypress'],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user