@nx/cypress 24.0.0-beta.2 → 24.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/internal.d.ts +1 -1
- package/dist/src/executors/cypress/{cypress.impl.d.ts → schema.d.ts} +0 -8
- package/dist/src/executors/cypress/schema.js +2 -0
- package/dist/src/generators/component-configuration/component-configuration.js +7 -57
- package/dist/src/generators/component-configuration/schema.d.ts +0 -2
- package/dist/src/generators/configuration/configuration.d.ts +0 -1
- package/dist/src/generators/configuration/configuration.js +13 -64
- package/dist/src/generators/init/init.js +2 -45
- package/dist/src/generators/init/schema.d.ts +0 -1
- package/dist/src/migrations/update-23-3-0/adapt-query-command-overwrites.md +1 -1
- package/dist/src/migrations/update-23-3-0/ai-instructions-for-cypress-16.md +2 -2
- package/dist/src/migrations/update-24-0-0/finish-inferred-conversion.md +12 -0
- package/dist/src/migrations/update-24-0-0/remove-deprecated-executors.d.ts +6 -0
- package/dist/src/migrations/update-24-0-0/remove-deprecated-executors.js +10 -0
- package/dist/src/migrations/update-24-0-0/remove-deprecated-executors.md +61 -0
- package/dist/src/plugins/plugin.js +1 -1
- package/dist/src/utils/add-linter.d.ts +0 -1
- package/dist/src/utils/add-linter.js +0 -1
- package/executors.json +1 -7
- package/migrations.json +7 -0
- package/package.json +6 -7
- package/dist/src/executors/cypress/cypress.impl.js +0 -136
- package/dist/src/executors/cypress/schema.json +0 -186
- package/dist/src/utils/deprecation.d.ts +0 -3
- package/dist/src/utils/deprecation.js +0 -20
- package/dist/src/utils/start-dev-server.d.ts +0 -9
- package/dist/src/utils/start-dev-server.js +0 -165
package/dist/internal.d.ts
CHANGED
|
@@ -2,4 +2,4 @@ export * from './src/utils/versions';
|
|
|
2
2
|
export * from './src/utils/ct-helpers';
|
|
3
3
|
export * from './src/utils/find-target-options';
|
|
4
4
|
export * from './src/utils/config';
|
|
5
|
-
export type { CypressExecutorOptions } from './src/executors/cypress/
|
|
5
|
+
export type { CypressExecutorOptions } from './src/executors/cypress/schema';
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ExecutorContext } from '@nx/devkit';
|
|
2
1
|
export type Json = {
|
|
3
2
|
[k: string]: any;
|
|
4
3
|
};
|
|
@@ -7,10 +6,6 @@ export interface CypressExecutorOptions extends Json {
|
|
|
7
6
|
watch?: boolean;
|
|
8
7
|
devServerTarget?: string;
|
|
9
8
|
headed?: boolean;
|
|
10
|
-
/**
|
|
11
|
-
* @deprecated Cypress runs headless by default. Use the --watch flag to
|
|
12
|
-
* control head/headless behavior instead. It will be removed in Nx v23.
|
|
13
|
-
**/
|
|
14
9
|
headless?: boolean;
|
|
15
10
|
exit?: boolean;
|
|
16
11
|
key?: string;
|
|
@@ -33,6 +28,3 @@ export interface CypressExecutorOptions extends Json {
|
|
|
33
28
|
runnerUi?: boolean;
|
|
34
29
|
autoCancelAfterFailures?: boolean | number;
|
|
35
30
|
}
|
|
36
|
-
export default function cypressExecutor(options: CypressExecutorOptions, context: ExecutorContext): Promise<{
|
|
37
|
-
success: any;
|
|
38
|
-
}>;
|
|
@@ -8,21 +8,17 @@ const devkit_1 = require("@nx/devkit");
|
|
|
8
8
|
const internal_1 = require("@nx/devkit/internal");
|
|
9
9
|
const internal_2 = require("@nx/js/internal");
|
|
10
10
|
const assert_supported_cypress_version_1 = require("../../utils/assert-supported-cypress-version");
|
|
11
|
-
const deprecation_1 = require("../../utils/deprecation");
|
|
12
11
|
const versions_1 = require("../../utils/versions");
|
|
13
12
|
const base_setup_1 = require("../base-setup/base-setup");
|
|
14
|
-
const init_1 = tslib_1.
|
|
13
|
+
const init_1 = tslib_1.__importStar(require("../init/init"));
|
|
15
14
|
function componentConfigurationGenerator(tree, options) {
|
|
16
|
-
return componentConfigurationGeneratorInternal(tree,
|
|
17
|
-
addPlugin: false,
|
|
18
|
-
...options,
|
|
19
|
-
});
|
|
15
|
+
return componentConfigurationGeneratorInternal(tree, options);
|
|
20
16
|
}
|
|
21
17
|
async function componentConfigurationGeneratorInternal(tree, options) {
|
|
22
18
|
(0, assert_supported_cypress_version_1.assertSupportedCypressVersion)(tree);
|
|
23
19
|
(0, internal_2.assertNotUsingTsSolutionSetup)(tree, 'cypress', 'component-configuration');
|
|
24
20
|
const tasks = [];
|
|
25
|
-
const opts = normalizeOptions(
|
|
21
|
+
const opts = normalizeOptions(options);
|
|
26
22
|
if (opts.bundler === 'vite') {
|
|
27
23
|
(0, versions_1.assertViteSupportsInstalledCypress)(tree);
|
|
28
24
|
}
|
|
@@ -38,29 +34,18 @@ async function componentConfigurationGeneratorInternal(tree, options) {
|
|
|
38
34
|
skipFormat: true,
|
|
39
35
|
}));
|
|
40
36
|
}
|
|
41
|
-
|
|
42
|
-
const hasPlugin = nxJson.plugins?.some((p) => typeof p === 'string'
|
|
43
|
-
? p === '@nx/cypress/plugin'
|
|
44
|
-
: p.plugin === '@nx/cypress/plugin');
|
|
37
|
+
await (0, init_1.addPlugin)(tree, await (0, devkit_1.createProjectGraphAsync)(), false);
|
|
45
38
|
const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, opts.project);
|
|
46
39
|
addProjectFiles(tree, projectConfig, opts);
|
|
47
|
-
|
|
48
|
-
(0, deprecation_1.warnCypressExecutorGenerating)();
|
|
49
|
-
addTargetToProject(tree, projectConfig, opts);
|
|
50
|
-
}
|
|
51
|
-
updateNxJsonConfiguration(tree, hasPlugin);
|
|
40
|
+
updateNxJsonConfiguration(tree);
|
|
52
41
|
updateTsConfigForComponentTesting(tree, projectConfig);
|
|
53
42
|
if (!opts.skipFormat) {
|
|
54
43
|
await (0, devkit_1.formatFiles)(tree);
|
|
55
44
|
}
|
|
56
45
|
return (0, devkit_1.runTasksInSerial)(...tasks);
|
|
57
46
|
}
|
|
58
|
-
function normalizeOptions(
|
|
59
|
-
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
60
|
-
const addPlugin = process.env.NX_ADD_PLUGINS !== 'false' &&
|
|
61
|
-
nxJson.useInferencePlugins !== false;
|
|
47
|
+
function normalizeOptions(options) {
|
|
62
48
|
return {
|
|
63
|
-
addPlugin,
|
|
64
49
|
...options,
|
|
65
50
|
framework: options.framework ?? null,
|
|
66
51
|
directory: options.directory ?? 'cypress',
|
|
@@ -107,17 +92,7 @@ function addProjectFiles(tree, projectConfig, opts) {
|
|
|
107
92
|
ext: '',
|
|
108
93
|
});
|
|
109
94
|
}
|
|
110
|
-
function
|
|
111
|
-
projectConfig.targets['component-test'] = {
|
|
112
|
-
executor: '@nx/cypress:cypress',
|
|
113
|
-
options: {
|
|
114
|
-
cypressConfig: (0, devkit_1.joinPathFragments)(projectConfig.root, 'cypress.config.ts'),
|
|
115
|
-
testingType: 'component',
|
|
116
|
-
},
|
|
117
|
-
};
|
|
118
|
-
(0, devkit_1.updateProjectConfiguration)(tree, opts.project, projectConfig);
|
|
119
|
-
}
|
|
120
|
-
function updateNxJsonConfiguration(tree, hasPlugin) {
|
|
95
|
+
function updateNxJsonConfiguration(tree) {
|
|
121
96
|
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
122
97
|
const productionFileSet = nxJson.namedInputs?.production;
|
|
123
98
|
if (productionFileSet) {
|
|
@@ -128,31 +103,6 @@ function updateNxJsonConfiguration(tree, hasPlugin) {
|
|
|
128
103
|
'!{projectRoot}/cypress.config.[jt]s',
|
|
129
104
|
]));
|
|
130
105
|
}
|
|
131
|
-
if (!hasPlugin) {
|
|
132
|
-
const cacheableOperations = nxJson.tasksRunnerOptions?.default?.options?.cacheableOperations;
|
|
133
|
-
if (cacheableOperations &&
|
|
134
|
-
!cacheableOperations.includes('component-test')) {
|
|
135
|
-
cacheableOperations.push('component-test');
|
|
136
|
-
}
|
|
137
|
-
// Either a `target: 'component-test'` default or a default keyed on
|
|
138
|
-
// the executor we're scaffolding will apply to the new target — pick
|
|
139
|
-
// the more specific (target-keyed) when both are present.
|
|
140
|
-
const existingForTarget = (0, internal_1.findTargetDefault)(nxJson.targetDefaults, {
|
|
141
|
-
target: 'component-test',
|
|
142
|
-
});
|
|
143
|
-
const existingForExecutor = (0, internal_1.findTargetDefault)(nxJson.targetDefaults, {
|
|
144
|
-
executor: '@nx/cypress:cypress',
|
|
145
|
-
});
|
|
146
|
-
const existing = existingForTarget ?? existingForExecutor;
|
|
147
|
-
(0, internal_1.upsertTargetDefault)(tree, nxJson, {
|
|
148
|
-
target: 'component-test',
|
|
149
|
-
cache: existing?.cache ?? true,
|
|
150
|
-
inputs: existing?.inputs ?? [
|
|
151
|
-
'default',
|
|
152
|
-
productionFileSet ? '^production' : '^default',
|
|
153
|
-
],
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
106
|
(0, devkit_1.updateNxJson)(tree, nxJson);
|
|
157
107
|
}
|
|
158
108
|
function updateTsConfigForComponentTesting(tree, projectConfig) {
|
|
@@ -4,12 +4,10 @@ export interface CypressComponentConfigurationSchema {
|
|
|
4
4
|
directory?: string;
|
|
5
5
|
bundler?: 'webpack' | 'vite';
|
|
6
6
|
jsx?: boolean;
|
|
7
|
-
addPlugin?: boolean;
|
|
8
7
|
skipPackageJson?: boolean;
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
10
|
* @internal
|
|
12
11
|
*/
|
|
13
|
-
addExplicitTargets?: boolean;
|
|
14
12
|
framework?: 'next';
|
|
15
13
|
}
|
|
@@ -21,7 +21,6 @@ export interface CypressE2EConfigSchema {
|
|
|
21
21
|
webServerCommands?: Record<string, string>;
|
|
22
22
|
ciWebServerCommand?: string;
|
|
23
23
|
ciBaseUrl?: string;
|
|
24
|
-
addPlugin?: boolean;
|
|
25
24
|
}
|
|
26
25
|
export declare function configurationGenerator(tree: Tree, options: CypressE2EConfigSchema): Promise<GeneratorCallback>;
|
|
27
26
|
export declare function configurationGeneratorInternal(tree: Tree, options: CypressE2EConfigSchema): Promise<GeneratorCallback>;
|
|
@@ -11,15 +11,11 @@ const path_1 = require("path");
|
|
|
11
11
|
const add_linter_1 = require("../../utils/add-linter");
|
|
12
12
|
const assert_supported_cypress_version_1 = require("../../utils/assert-supported-cypress-version");
|
|
13
13
|
const config_1 = require("../../utils/config");
|
|
14
|
-
const deprecation_1 = require("../../utils/deprecation");
|
|
15
14
|
const versions_1 = require("../../utils/versions");
|
|
16
15
|
const base_setup_1 = require("../base-setup/base-setup");
|
|
17
16
|
const init_1 = tslib_1.__importStar(require("../init/init"));
|
|
18
17
|
function configurationGenerator(tree, options) {
|
|
19
|
-
return configurationGeneratorInternal(tree,
|
|
20
|
-
addPlugin: false,
|
|
21
|
-
...options,
|
|
22
|
-
});
|
|
18
|
+
return configurationGeneratorInternal(tree, options);
|
|
23
19
|
}
|
|
24
20
|
async function configurationGeneratorInternal(tree, options) {
|
|
25
21
|
(0, assert_supported_cypress_version_1.assertSupportedCypressVersion)(tree);
|
|
@@ -33,18 +29,10 @@ async function configurationGeneratorInternal(tree, options) {
|
|
|
33
29
|
skipFormat: true,
|
|
34
30
|
}));
|
|
35
31
|
}
|
|
36
|
-
else
|
|
32
|
+
else {
|
|
37
33
|
await (0, init_1.addPlugin)(tree, projectGraph, false);
|
|
38
34
|
}
|
|
39
|
-
|
|
40
|
-
const hasPlugin = nxJson.plugins?.some((p) => typeof p === 'string'
|
|
41
|
-
? p === '@nx/cypress/plugin'
|
|
42
|
-
: p.plugin === '@nx/cypress/plugin');
|
|
43
|
-
await addFiles(tree, opts, projectGraph, hasPlugin);
|
|
44
|
-
if (!hasPlugin) {
|
|
45
|
-
(0, deprecation_1.warnCypressExecutorGenerating)();
|
|
46
|
-
addTarget(tree, opts, projectGraph);
|
|
47
|
-
}
|
|
35
|
+
await addFiles(tree, opts, projectGraph);
|
|
48
36
|
const projectTsConfigPath = (0, devkit_1.joinPathFragments)(opts.projectRoot, 'tsconfig.json');
|
|
49
37
|
if (tree.exists(projectTsConfigPath)) {
|
|
50
38
|
(0, devkit_1.updateJson)(tree, projectTsConfigPath, (json) => {
|
|
@@ -130,10 +118,6 @@ In this case you need to provide a devServerTarget,'<projectName>:<targetName>[:
|
|
|
130
118
|
if (!options.baseUrl && !devServerTarget) {
|
|
131
119
|
throw new Error('Either baseUrl or devServerTarget must be provided');
|
|
132
120
|
}
|
|
133
|
-
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
134
|
-
options.addPlugin ??=
|
|
135
|
-
process.env.NX_ADD_PLUGINS !== 'false' &&
|
|
136
|
-
nxJson.useInferencePlugins !== false;
|
|
137
121
|
return {
|
|
138
122
|
...options,
|
|
139
123
|
bundler: options.bundler ?? 'webpack',
|
|
@@ -166,7 +150,7 @@ async function promptForMissingServeData(projectName) {
|
|
|
166
150
|
baseUrl: `http://localhost:${port}`,
|
|
167
151
|
};
|
|
168
152
|
}
|
|
169
|
-
async function addFiles(tree, options, projectGraph
|
|
153
|
+
async function addFiles(tree, options, projectGraph) {
|
|
170
154
|
const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, options.project);
|
|
171
155
|
const hasTsConfig = tree.exists((0, devkit_1.joinPathFragments)(projectConfig.root, 'tsconfig.json'));
|
|
172
156
|
const offsetFromProjectRoot = options.directory
|
|
@@ -194,12 +178,12 @@ async function addFiles(tree, options, projectGraph, hasPlugin) {
|
|
|
194
178
|
let webServerCommands;
|
|
195
179
|
let ciWebServerCommand;
|
|
196
180
|
let ciBaseUrl;
|
|
197
|
-
if (
|
|
181
|
+
if (options.webServerCommands && options.ciWebServerCommand) {
|
|
198
182
|
webServerCommands = options.webServerCommands;
|
|
199
183
|
ciWebServerCommand = options.ciWebServerCommand;
|
|
200
184
|
ciBaseUrl = options.ciBaseUrl;
|
|
201
185
|
}
|
|
202
|
-
else if (
|
|
186
|
+
else if (options.devServerTarget) {
|
|
203
187
|
webServerCommands = {};
|
|
204
188
|
webServerCommands.default = 'nx run ' + options.devServerTarget;
|
|
205
189
|
const parsedTarget = (0, devkit_1.parseTargetString)(options.devServerTarget, projectGraph);
|
|
@@ -214,6 +198,13 @@ async function addFiles(tree, options, projectGraph, hasPlugin) {
|
|
|
214
198
|
ciWebServerCommand = `nx run ${parsedTarget.project}:serve-static`;
|
|
215
199
|
}
|
|
216
200
|
}
|
|
201
|
+
if (typeof options.port === 'number') {
|
|
202
|
+
for (const name of Object.keys(webServerCommands ?? {})) {
|
|
203
|
+
webServerCommands[name] += ` --port=${options.port}`;
|
|
204
|
+
}
|
|
205
|
+
if (ciWebServerCommand)
|
|
206
|
+
ciWebServerCommand += ` --port=${options.port}`;
|
|
207
|
+
}
|
|
217
208
|
const e2ePresetOptions = {
|
|
218
209
|
cypressDir: options.directory,
|
|
219
210
|
bundler: options.bundler === 'vite' ? 'vite' : undefined,
|
|
@@ -242,48 +233,6 @@ async function addFiles(tree, options, projectGraph, hasPlugin) {
|
|
|
242
233
|
(0, devkit_1.toJS)(tree);
|
|
243
234
|
}
|
|
244
235
|
}
|
|
245
|
-
function addTarget(tree, opts, projectGraph) {
|
|
246
|
-
const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, opts.project);
|
|
247
|
-
projectConfig.targets ??= {};
|
|
248
|
-
projectConfig.targets.e2e = {
|
|
249
|
-
executor: '@nx/cypress:cypress',
|
|
250
|
-
options: {
|
|
251
|
-
cypressConfig: (0, devkit_1.joinPathFragments)(projectConfig.root, `cypress.config.${opts.js ? 'js' : 'ts'}`),
|
|
252
|
-
testingType: 'e2e',
|
|
253
|
-
},
|
|
254
|
-
};
|
|
255
|
-
if (opts.devServerTarget) {
|
|
256
|
-
const parsedTarget = (0, devkit_1.parseTargetString)(opts.devServerTarget, projectGraph);
|
|
257
|
-
projectConfig.targets.e2e.options = {
|
|
258
|
-
...projectConfig.targets.e2e.options,
|
|
259
|
-
devServerTarget: opts.devServerTarget,
|
|
260
|
-
port: opts.port,
|
|
261
|
-
};
|
|
262
|
-
const devServerProjectConfig = (0, devkit_1.readProjectConfiguration)(tree, parsedTarget.project);
|
|
263
|
-
// Add production e2e target if serve target is found
|
|
264
|
-
if (parsedTarget.configuration !== 'production' &&
|
|
265
|
-
devServerProjectConfig.targets?.[parsedTarget.target]?.configurations?.['production']) {
|
|
266
|
-
projectConfig.targets.e2e.configurations ??= {};
|
|
267
|
-
projectConfig.targets.e2e.configurations['production'] = {
|
|
268
|
-
devServerTarget: `${parsedTarget.project}:${parsedTarget.target}:production`,
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
// Add ci/static e2e target if serve target is found
|
|
272
|
-
if (devServerProjectConfig.targets?.['serve-static']) {
|
|
273
|
-
projectConfig.targets.e2e.configurations ??= {};
|
|
274
|
-
projectConfig.targets.e2e.configurations.ci = {
|
|
275
|
-
devServerTarget: `${parsedTarget.project}:serve-static`,
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
else if (opts.baseUrl) {
|
|
280
|
-
projectConfig.targets.e2e.options = {
|
|
281
|
-
...projectConfig.targets.e2e.options,
|
|
282
|
-
baseUrl: opts.baseUrl,
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
(0, devkit_1.updateProjectConfiguration)(tree, opts.project, projectConfig);
|
|
286
|
-
}
|
|
287
236
|
function createPackageJson(tree, options) {
|
|
288
237
|
const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, options.project);
|
|
289
238
|
const packageJsonPath = (0, devkit_1.joinPathFragments)(projectConfig.root, 'package.json');
|
|
@@ -8,40 +8,6 @@ const devkit_1 = require("@nx/devkit");
|
|
|
8
8
|
const plugin_1 = require("../../plugins/plugin");
|
|
9
9
|
const assert_supported_cypress_version_1 = require("../../utils/assert-supported-cypress-version");
|
|
10
10
|
const versions_1 = require("../../utils/versions");
|
|
11
|
-
function setupE2ETargetDefaults(tree) {
|
|
12
|
-
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
13
|
-
if (!nxJson.namedInputs) {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
// E2e targets depend on all their project's sources + production sources of dependencies
|
|
17
|
-
const productionFileSet = !!nxJson.namedInputs?.production;
|
|
18
|
-
const existing = findExistingE2eDefault(nxJson.targetDefaults);
|
|
19
|
-
const patch = {};
|
|
20
|
-
if (existing?.cache === undefined)
|
|
21
|
-
patch.cache = true;
|
|
22
|
-
if (existing?.inputs === undefined) {
|
|
23
|
-
patch.inputs = ['default', productionFileSet ? '^production' : '^default'];
|
|
24
|
-
}
|
|
25
|
-
if (Object.keys(patch).length > 0) {
|
|
26
|
-
(0, internal_1.upsertTargetDefault)(tree, nxJson, { target: 'e2e', ...patch });
|
|
27
|
-
(0, devkit_1.updateNxJson)(tree, nxJson);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
function findExistingE2eDefault(td) {
|
|
31
|
-
if (!td)
|
|
32
|
-
return undefined;
|
|
33
|
-
const value = td['e2e'];
|
|
34
|
-
if (value === undefined)
|
|
35
|
-
return undefined;
|
|
36
|
-
if (Array.isArray(value)) {
|
|
37
|
-
const found = value.find((e) => e.filter === undefined);
|
|
38
|
-
if (!found)
|
|
39
|
-
return undefined;
|
|
40
|
-
const { filter: _f, ...rest } = found;
|
|
41
|
-
return rest;
|
|
42
|
-
}
|
|
43
|
-
return value;
|
|
44
|
-
}
|
|
45
11
|
function updateDependencies(tree, options) {
|
|
46
12
|
const tasks = [];
|
|
47
13
|
tasks.push((0, devkit_1.removeDependenciesFromPackageJson)(tree, ['@nx/cypress'], []));
|
|
@@ -86,21 +52,12 @@ function updateProductionFileset(tree) {
|
|
|
86
52
|
(0, devkit_1.updateNxJson)(tree, nxJson);
|
|
87
53
|
}
|
|
88
54
|
async function cypressInitGenerator(tree, options) {
|
|
89
|
-
return cypressInitGeneratorInternal(tree,
|
|
55
|
+
return cypressInitGeneratorInternal(tree, options);
|
|
90
56
|
}
|
|
91
57
|
async function cypressInitGeneratorInternal(tree, options) {
|
|
92
58
|
(0, assert_supported_cypress_version_1.assertSupportedCypressVersion)(tree);
|
|
93
59
|
updateProductionFileset(tree);
|
|
94
|
-
|
|
95
|
-
options.addPlugin ??=
|
|
96
|
-
process.env.NX_ADD_PLUGINS !== 'false' &&
|
|
97
|
-
nxJson.useInferencePlugins !== false;
|
|
98
|
-
if (options.addPlugin) {
|
|
99
|
-
await addPlugin(tree, await (0, devkit_1.createProjectGraphAsync)(), options.updatePackageScripts);
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
setupE2ETargetDefaults(tree);
|
|
103
|
-
}
|
|
60
|
+
await addPlugin(tree, await (0, devkit_1.createProjectGraphAsync)(), options.updatePackageScripts);
|
|
104
61
|
let installTask = () => { };
|
|
105
62
|
if (!options.skipPackageJson) {
|
|
106
63
|
installTask = updateDependencies(tree, options);
|
|
@@ -8,7 +8,7 @@ Cypress 16 turned `cy.getCookie()`, `cy.getCookies()`, `cy.getAllCookies()`, `cy
|
|
|
8
8
|
|
|
9
9
|
Confirm both conditions before changing anything. If either fails, make no changes and stop.
|
|
10
10
|
|
|
11
|
-
1.
|
|
11
|
+
1. `impl.changes` or `impl.agentContext` in the step's `nx_migrate_prompt` block lists at least one file with a renamed `Cypress.Commands.overwriteQuery()` call. Search the workspace for `Cypress.Commands.overwriteQuery(` if those fields are absent.
|
|
12
12
|
2. The workspace is on Cypress 16 or later (`cypress` in `package.json`).
|
|
13
13
|
|
|
14
14
|
## Step 1: Verify the pre-pass
|
|
@@ -9,7 +9,7 @@ Migrate the workspace's Cypress projects from Cypress 15 to 16. Cypress 16 remov
|
|
|
9
9
|
1. Confirm `cypress` in `package.json` resolves to 16 or later. If it does not, make no changes and stop.
|
|
10
10
|
2. Confirm Node is 22, 24, or 26+ (`node -v`). Cypress 16 declares `engines` `^22.0.0 || ^24.0.0 || >=26.0.0` and refuses to install on Node 20 or 25.
|
|
11
11
|
3. List the Cypress projects: every project with a `cypress.config.{ts,js,mjs,cjs}` file. Search their spec and support files for the strings in the steps below.
|
|
12
|
-
4.
|
|
12
|
+
4. When the step's `nx_migrate_prompt` block has `impl.agentContext`, review the deterministic migrations' entries in it: each entry (a non-literal `experimentalFastVisibility`, a removed `execTimeout`, a removed `experimentalSourceRewriting: true`, an unresolved spread, a renamed `overwriteQuery` callback) is pending work for the matching step.
|
|
13
13
|
|
|
14
14
|
## Step 1: Replace `Cypress.env()`
|
|
15
15
|
|
|
@@ -139,7 +139,7 @@ it('renders on mobile', { viewportWidth: 375 }, () => {
|
|
|
139
139
|
|
|
140
140
|
`cy.getCookie()`, `cy.getCookies()`, `cy.getAllCookies()`, `cy.getAllLocalStorage()` and `cy.getAllSessionStorage()` are queries: they retry assertions chained with `.should()` and follow `defaultCommandTimeout` (4000ms) instead of `responseTimeout` (30000ms). A `.then()` callback still runs once and does not retry. Change nothing unless a test fails; then move the assertion from `.then()` onto `.should()` so it retries, or pass `{ timeout }` when a cookie read needs longer than 4 seconds.
|
|
141
141
|
|
|
142
|
-
The deterministic migration renamed `Cypress.Commands.overwrite()` to `overwriteQuery()` for those five names;
|
|
142
|
+
The deterministic migration renamed `Cypress.Commands.overwrite()` to `overwriteQuery()` for those five names; `impl.agentContext` lists each renamed call when present, and otherwise search the workspace for `Cypress.Commands.overwriteQuery(`. Do not re-apply the rename. Adapt each renamed callback to the query contract: `overwriteQuery` calls the callback with the command as `this`, the original query function as the first argument, and then the arguments the test passed, and the callback must return a function that takes the subject. Cypress calls that returned function, possibly many times while retrying, to compute the result. It must not return a chainable, call `cy.*` commands or use `.then()`. The original query reads `this` as well, so every callback must be a `function` expression that calls `originalFn.call(this, ...)`. An arrow callback, or a plain `originalFn(...)` call, fails to typecheck (TS2684, `QueryFnWithOriginalFn` declares `this: Command`) and throws at runtime. Convert arrow callbacks even when they only forward their arguments:
|
|
143
143
|
|
|
144
144
|
**Before:**
|
|
145
145
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Replace removed @nx/cypress executors with inferred tasks
|
|
2
|
+
|
|
3
|
+
Nx 24 removed `@nx/cypress:cypress` (including old `@nrwl` aliases).
|
|
4
|
+
|
|
5
|
+
Before changing anything, check for leftover work: a project target, a package.json target or a `targetDefaults` entry still uses a removed executor, or the advisory context reports a conversion error. If none applies, make no changes and stop. Treat each advisory-context item as pending work.
|
|
6
|
+
|
|
7
|
+
1. Inspect all affected projects and workspace defaults. Repair config-loading errors first. Do not restore executor registrations or install older Nx packages.
|
|
8
|
+
2. Preserve Cypress config, browser choice, testing type, CI options and dev-server orchestration. Ensure webServerCommands/target dependencies start the intended app.
|
|
9
|
+
3. Register `@nx/cypress/plugin` with options/includes that preserve custom target names and mixed inferred/explicit projects. Replace remaining removed-executor targets, including those in package.json and filtered targetDefaults. Transfer their behavior before removing obsolete defaults. Preserve configurations, dependencies, cache inputs/outputs and environment behavior. Do not silently delete unsupported options or unrelated targets. Migrate old @nrwl aliases too.
|
|
10
|
+
4. Run project graph creation with the daemon and graph cache disabled (`NX_DAEMON=false NX_CACHE_PROJECT_GRAPH=false`), using the workspace package manager. Inspect resolved project targets and verify each migrated entrypoint exists and is inferred (a thin alias to an inferred target is acceptable). Resolve graph errors before completing this migration, even if task validation was skipped. Configs must load for subsequent migrations.
|
|
11
|
+
5. By default run relevant real builds/tests with task cache disabled, including alternate configurations. Honor an existing preference to skip expensive task validation; otherwise offer that option when validation is slow. Skipping expensive tasks never skips conversion or the graph check. Report each skipped task as unverified. Never weaken assertions or change application behavior to make checks pass.
|
|
12
|
+
6. Report converted projects, graph validation, task results and any skipped checks. Unconverted targets or a broken graph mean the migration is incomplete. Investigate task failures and distinguish missing environment prerequisites from conversion regressions; do not claim a working workspace with unresolved regressions.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = update;
|
|
4
|
+
const internal_1 = require("@nx/devkit/internal");
|
|
5
|
+
function update(tree) {
|
|
6
|
+
return (0, internal_1.migrateRemovedExecutors)(tree, ['@nx/cypress:cypress', '@nrwl/cypress:cypress'], async (tree, options) => {
|
|
7
|
+
const { convertToInferred } = require('../../generators/convert-to-inferred/convert-to-inferred');
|
|
8
|
+
return convertToInferred(tree, options);
|
|
9
|
+
});
|
|
10
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#### Convert `@nx/cypress:cypress` targets to inferred tasks
|
|
2
|
+
|
|
3
|
+
Nx 24 removes the `@nx/cypress:cypress` executor and its `@nrwl/cypress:cypress` alias. The migration registers `@nx/cypress/plugin` in `nx.json` and converts the targets that use those executors to inferred tasks. It moves the dev server and `baseUrl` settings into `cypress.config.*`. A target with nothing left to override is removed from project configuration. When several projects share the same remaining options, those options move to a `targetDefaults` entry scoped to the plugin. `targetDefaults` entries keyed by a removed executor are removed once no target uses that executor.
|
|
4
|
+
|
|
5
|
+
The migration lists the targets the converter cannot convert safely. In agentic runs, an AI prompt finishes them and validates the project graph.
|
|
6
|
+
|
|
7
|
+
#### Sample code changes
|
|
8
|
+
|
|
9
|
+
##### Before
|
|
10
|
+
|
|
11
|
+
```json title="myapp-e2e/project.json"
|
|
12
|
+
{
|
|
13
|
+
"name": "myapp-e2e",
|
|
14
|
+
"targets": {
|
|
15
|
+
"e2e": {
|
|
16
|
+
"executor": "@nx/cypress:cypress",
|
|
17
|
+
"options": {
|
|
18
|
+
"cypressConfig": "myapp-e2e/cypress.config.ts",
|
|
19
|
+
"testingType": "e2e",
|
|
20
|
+
"devServerTarget": "myapp:serve"
|
|
21
|
+
},
|
|
22
|
+
"configurations": {
|
|
23
|
+
"ci": {
|
|
24
|
+
"devServerTarget": "myapp:static-serve"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
##### After
|
|
33
|
+
|
|
34
|
+
```json title="myapp-e2e/project.json"
|
|
35
|
+
{
|
|
36
|
+
"name": "myapp-e2e",
|
|
37
|
+
"targets": {
|
|
38
|
+
"e2e": {
|
|
39
|
+
"options": {
|
|
40
|
+
"config-file": "./cypress.config.ts"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```json title="nx.json"
|
|
48
|
+
{
|
|
49
|
+
"plugins": [
|
|
50
|
+
{
|
|
51
|
+
"plugin": "@nx/cypress/plugin",
|
|
52
|
+
"options": {
|
|
53
|
+
"ciTargetName": "e2e-ci",
|
|
54
|
+
"componentTestingTargetName": "component-test",
|
|
55
|
+
"openTargetName": "open-cypress",
|
|
56
|
+
"targetName": "e2e"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
```
|
|
@@ -297,7 +297,7 @@ async function buildCypressTargets(configFilePath, projectRoot, options, context
|
|
|
297
297
|
else {
|
|
298
298
|
targets[targetName].parallelism = false;
|
|
299
299
|
}
|
|
300
|
-
targets[targetName] = (0, internal_1.applyTestFileDirectives)(targets[targetName], context.workspaceRoot, file,
|
|
300
|
+
targets[targetName] = (0, internal_1.applyTestFileDirectives)(targets[targetName], context.workspaceRoot, file, { ignoreServerInputs: true });
|
|
301
301
|
}
|
|
302
302
|
targets[options.ciTargetName] = {
|
|
303
303
|
executor: 'nx:noop',
|
|
@@ -21,6 +21,5 @@ export interface CyLinterOptions {
|
|
|
21
21
|
* This is useful when adding linting to a brand new project vs an existing one
|
|
22
22
|
**/
|
|
23
23
|
overwriteExisting?: boolean;
|
|
24
|
-
addPlugin?: boolean;
|
|
25
24
|
}
|
|
26
25
|
export declare function addLinterToCyProject(tree: Tree, options: CyLinterOptions): Promise<GeneratorCallback>;
|
|
@@ -24,7 +24,6 @@ async function addLinterToCyProject(tree, options) {
|
|
|
24
24
|
enableTypedLinting,
|
|
25
25
|
skipPackageJson: options.skipPackageJson,
|
|
26
26
|
rootProject: options.rootProject,
|
|
27
|
-
addPlugin: options.addPlugin,
|
|
28
27
|
}));
|
|
29
28
|
}
|
|
30
29
|
// Everything below configures ESLint — predefined configs, `extends`, ignore
|
package/executors.json
CHANGED
package/migrations.json
CHANGED
|
@@ -108,6 +108,13 @@
|
|
|
108
108
|
"implementation": "./dist/src/migrations/update-23-3-0/upgrade-to-cypress-16",
|
|
109
109
|
"prompt": "./dist/src/migrations/update-23-3-0/ai-instructions-for-cypress-16.md",
|
|
110
110
|
"documentation": "./dist/src/migrations/update-23-3-0/upgrade-to-cypress-16.md"
|
|
111
|
+
},
|
|
112
|
+
"remove-deprecated-executors": {
|
|
113
|
+
"version": "24.0.0-beta.3",
|
|
114
|
+
"description": "Converts targets using the removed `@nx/cypress` executors to inferred tasks; an AI prompt finishes the targets the converter cannot convert safely and validates the project graph.",
|
|
115
|
+
"implementation": "./dist/src/migrations/update-24-0-0/remove-deprecated-executors",
|
|
116
|
+
"prompt": "./dist/src/migrations/update-24-0-0/finish-inferred-conversion.md",
|
|
117
|
+
"documentation": "./dist/src/migrations/update-24-0-0/remove-deprecated-executors.md"
|
|
111
118
|
}
|
|
112
119
|
},
|
|
113
120
|
"packageJsonUpdates": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nx/cypress",
|
|
3
|
-
"version": "24.0.0-beta.
|
|
3
|
+
"version": "24.0.0-beta.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"files": [
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"executors.json",
|
|
11
11
|
"generators.json"
|
|
12
12
|
],
|
|
13
|
-
"description": "The Nx Plugin for Cypress
|
|
13
|
+
"description": "The Nx Plugin for Cypress infers tasks and provides generators allowing your workspace to use the powerful Cypress integration testing capabilities.",
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
16
16
|
"url": "https://github.com/nrwl/nx.git",
|
|
@@ -84,17 +84,16 @@
|
|
|
84
84
|
"supportsOptionalMigrations": true
|
|
85
85
|
},
|
|
86
86
|
"dependencies": {
|
|
87
|
-
"@nx/devkit": "24.0.0-beta.
|
|
88
|
-
"@nx/eslint": "24.0.0-beta.
|
|
89
|
-
"@nx/js": "24.0.0-beta.
|
|
87
|
+
"@nx/devkit": "24.0.0-beta.3",
|
|
88
|
+
"@nx/eslint": "24.0.0-beta.3",
|
|
89
|
+
"@nx/js": "24.0.0-beta.3",
|
|
90
90
|
"@phenomnomnominal/tsquery": "~6.2.0",
|
|
91
|
-
"detect-port": "^2.1.0",
|
|
92
91
|
"semver": "^7.6.3",
|
|
93
92
|
"tree-kill": "^1.2.2",
|
|
94
93
|
"tslib": "^2.3.0"
|
|
95
94
|
},
|
|
96
95
|
"devDependencies": {
|
|
97
|
-
"nx": "24.0.0-beta.
|
|
96
|
+
"nx": "24.0.0-beta.3"
|
|
98
97
|
},
|
|
99
98
|
"peerDependencies": {
|
|
100
99
|
"cypress": ">= 13 < 17"
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.default = cypressExecutor;
|
|
4
|
-
const devkit_1 = require("@nx/devkit");
|
|
5
|
-
const fs_1 = require("fs");
|
|
6
|
-
const path_1 = require("path");
|
|
7
|
-
const ct_helpers_1 = require("../../utils/ct-helpers");
|
|
8
|
-
const deprecation_1 = require("../../utils/deprecation");
|
|
9
|
-
const start_dev_server_1 = require("../../utils/start-dev-server");
|
|
10
|
-
const Cypress = require('cypress'); // @NOTE: Importing via ES6 messes the whole test dependencies.
|
|
11
|
-
async function cypressExecutor(options, context) {
|
|
12
|
-
(0, deprecation_1.warnCypressExecutorDeprecation)();
|
|
13
|
-
options = normalizeOptions(options, context);
|
|
14
|
-
// this is used by cypress component testing presets to build the executor contexts with the correct configuration options.
|
|
15
|
-
process.env.NX_CYPRESS_TARGET_CONFIGURATION = context.configurationName;
|
|
16
|
-
let success;
|
|
17
|
-
const generatorInstance = (0, start_dev_server_1.startDevServer)(options, context);
|
|
18
|
-
for await (const devServerValues of generatorInstance) {
|
|
19
|
-
try {
|
|
20
|
-
success = await runCypress(devServerValues.baseUrl, {
|
|
21
|
-
...options,
|
|
22
|
-
portLockFilePath: devServerValues.portLockFilePath,
|
|
23
|
-
});
|
|
24
|
-
if (!options.watch) {
|
|
25
|
-
generatorInstance.return();
|
|
26
|
-
break;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
catch (e) {
|
|
30
|
-
devkit_1.logger.error(e.message);
|
|
31
|
-
success = false;
|
|
32
|
-
if (!options.watch)
|
|
33
|
-
break;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return { success };
|
|
37
|
-
}
|
|
38
|
-
function normalizeOptions(options, context) {
|
|
39
|
-
options.env = options.env || {};
|
|
40
|
-
options.testingType ??= 'e2e';
|
|
41
|
-
if (options.testingType === 'component') {
|
|
42
|
-
const project = context?.projectGraph?.nodes?.[context.projectName];
|
|
43
|
-
if (project?.data?.root) {
|
|
44
|
-
options.ctTailwindPath = (0, ct_helpers_1.getTempTailwindPath)(context);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return options;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* @whatItDoes Initialize the Cypress test runner with the provided project configuration.
|
|
51
|
-
* By default, Cypress will run tests from the CLI without the GUI and provide directly the results in the console output.
|
|
52
|
-
* If `watch` is `true`: Open Cypress in the interactive GUI to interact directly with the application.
|
|
53
|
-
*/
|
|
54
|
-
async function runCypress(baseUrl, opts) {
|
|
55
|
-
// Cypress expects the folder where a cypress config is present
|
|
56
|
-
const projectFolderPath = (0, path_1.dirname)(opts.cypressConfig);
|
|
57
|
-
const options = {
|
|
58
|
-
project: projectFolderPath,
|
|
59
|
-
configFile: (0, path_1.basename)(opts.cypressConfig),
|
|
60
|
-
};
|
|
61
|
-
// If not, will use the `baseUrl` normally from `cypress.json`
|
|
62
|
-
if (baseUrl) {
|
|
63
|
-
options.config = { baseUrl };
|
|
64
|
-
}
|
|
65
|
-
if (opts.browser) {
|
|
66
|
-
options.browser = opts.browser;
|
|
67
|
-
}
|
|
68
|
-
if (opts.env) {
|
|
69
|
-
options.env = {
|
|
70
|
-
...options.env,
|
|
71
|
-
...opts.env,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
if (opts.spec) {
|
|
75
|
-
options.spec = opts.spec;
|
|
76
|
-
}
|
|
77
|
-
options.tag = opts.tag;
|
|
78
|
-
options.exit = opts.exit;
|
|
79
|
-
options.headed = opts.headed;
|
|
80
|
-
options.runnerUi = opts.runnerUi;
|
|
81
|
-
if (opts.headless) {
|
|
82
|
-
options.headless = opts.headless;
|
|
83
|
-
}
|
|
84
|
-
options.record = opts.record;
|
|
85
|
-
options.key = opts.key;
|
|
86
|
-
options.parallel = opts.parallel;
|
|
87
|
-
options.ciBuildId = opts.ciBuildId?.toString();
|
|
88
|
-
options.group = opts.group;
|
|
89
|
-
// renamed in cy 10
|
|
90
|
-
options.config ??= {};
|
|
91
|
-
options.config[opts.testingType] = {
|
|
92
|
-
excludeSpecPattern: opts.ignoreTestFiles,
|
|
93
|
-
};
|
|
94
|
-
if (opts.reporter) {
|
|
95
|
-
options.reporter = opts.reporter;
|
|
96
|
-
}
|
|
97
|
-
if (opts.reporterOptions) {
|
|
98
|
-
options.reporterOptions = opts.reporterOptions;
|
|
99
|
-
}
|
|
100
|
-
if (opts.quiet) {
|
|
101
|
-
options.quiet = opts.quiet;
|
|
102
|
-
}
|
|
103
|
-
if (opts.autoCancelAfterFailures !== undefined) {
|
|
104
|
-
options.autoCancelAfterFailures = opts.autoCancelAfterFailures;
|
|
105
|
-
}
|
|
106
|
-
options.testingType = opts.testingType;
|
|
107
|
-
const result = await (opts.watch
|
|
108
|
-
? Cypress.open(options)
|
|
109
|
-
: Cypress.run(options));
|
|
110
|
-
cleanupTmpFile(opts.ctTailwindPath);
|
|
111
|
-
cleanupTmpFile(opts.portLockFilePath);
|
|
112
|
-
if (process.env.NX_VERBOSE_LOGGING === 'true' && opts.portLockFilePath) {
|
|
113
|
-
(0, fs_1.readdirSync)((0, path_1.dirname)(opts.portLockFilePath)).forEach((f) => {
|
|
114
|
-
if (f.endsWith('.txt')) {
|
|
115
|
-
devkit_1.logger.debug(`Lock file ${f} still present`);
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* `cypress.open` is returning `0` and is not of the same type as `cypress.run`.
|
|
121
|
-
* `cypress.open` is the graphical UI, so it will be obvious to know what wasn't
|
|
122
|
-
* working. Forcing the build to success when `cypress.open` is used.
|
|
123
|
-
*/
|
|
124
|
-
return !result.totalFailed && !result.failures;
|
|
125
|
-
}
|
|
126
|
-
function cleanupTmpFile(path) {
|
|
127
|
-
try {
|
|
128
|
-
if (path && (0, fs_1.existsSync)(path)) {
|
|
129
|
-
(0, fs_1.unlinkSync)(path);
|
|
130
|
-
}
|
|
131
|
-
return true;
|
|
132
|
-
}
|
|
133
|
-
catch (err) {
|
|
134
|
-
return false;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 2,
|
|
3
|
-
"title": "Cypress Target",
|
|
4
|
-
"description": "Run Cypress for e2e, integration and component testing.",
|
|
5
|
-
"type": "object",
|
|
6
|
-
"outputCapture": "pipe",
|
|
7
|
-
"cli": "nx",
|
|
8
|
-
"x-deprecated": "The `@nx/cypress:cypress` executor is deprecated and will be removed in Nx v24. Run `nx g @nx/cypress:convert-to-inferred` to migrate to the `@nx/cypress/plugin` inferred plugin. See https://nx.dev/docs/guides/tasks--caching/convert-to-inferred for details.",
|
|
9
|
-
"presets": [
|
|
10
|
-
{
|
|
11
|
-
"name": "Starting Dev Server",
|
|
12
|
-
"keys": ["cypressConfig", "devServerTarget"]
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
"name": "Custom Base Url",
|
|
16
|
-
"keys": ["cypressConfig", "baseUrl"]
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
"name": "Component Testing",
|
|
20
|
-
"keys": ["cypressConfig", "devServerTarget", "testingType", "skipServe"]
|
|
21
|
-
}
|
|
22
|
-
],
|
|
23
|
-
"properties": {
|
|
24
|
-
"cypressConfig": {
|
|
25
|
-
"type": "string",
|
|
26
|
-
"description": "The path of the Cypress configuration json file.",
|
|
27
|
-
"x-completion-type": "file",
|
|
28
|
-
"x-completion-glob": "cypress?(*)@(.js|.ts|.json)"
|
|
29
|
-
},
|
|
30
|
-
"watch": {
|
|
31
|
-
"type": "boolean",
|
|
32
|
-
"description": "Recompile and run tests when files change.",
|
|
33
|
-
"default": false
|
|
34
|
-
},
|
|
35
|
-
"devServerTarget": {
|
|
36
|
-
"type": "string",
|
|
37
|
-
"description": "Dev server target to run tests against."
|
|
38
|
-
},
|
|
39
|
-
"headed": {
|
|
40
|
-
"type": "boolean",
|
|
41
|
-
"description": "Displays the browser instead of running headlessly. Set this to `true` if your run depends on a Chrome extension being loaded.",
|
|
42
|
-
"default": false
|
|
43
|
-
},
|
|
44
|
-
"headless": {
|
|
45
|
-
"type": "boolean",
|
|
46
|
-
"description": "Hide the browser instead of running headed.",
|
|
47
|
-
"default": false,
|
|
48
|
-
"x-deprecated": "Cypress runs headless by default. Use the --watch flag to control head/headless behavior instead. It will be removed in Nx v23."
|
|
49
|
-
},
|
|
50
|
-
"exit": {
|
|
51
|
-
"type": "boolean",
|
|
52
|
-
"description": "Whether or not the Cypress Test Runner will stay open after running tests in a spec file.",
|
|
53
|
-
"default": true
|
|
54
|
-
},
|
|
55
|
-
"key": {
|
|
56
|
-
"type": "string",
|
|
57
|
-
"description": "The key cypress should use to run tests in parallel/record the run (CI only)."
|
|
58
|
-
},
|
|
59
|
-
"record": {
|
|
60
|
-
"type": "boolean",
|
|
61
|
-
"description": "Whether or not Cypress should record the results of the tests.",
|
|
62
|
-
"default": false
|
|
63
|
-
},
|
|
64
|
-
"parallel": {
|
|
65
|
-
"aliases": ["p"],
|
|
66
|
-
"type": "boolean",
|
|
67
|
-
"description": "Whether or not Cypress should run its tests in parallel (CI only).",
|
|
68
|
-
"default": false
|
|
69
|
-
},
|
|
70
|
-
"baseUrl": {
|
|
71
|
-
"type": "string",
|
|
72
|
-
"description": "The address (with the port) which your application is running on.",
|
|
73
|
-
"x-priority": "important"
|
|
74
|
-
},
|
|
75
|
-
"browser": {
|
|
76
|
-
"type": "string",
|
|
77
|
-
"description": "The browser to run tests in."
|
|
78
|
-
},
|
|
79
|
-
"env": {
|
|
80
|
-
"type": "object",
|
|
81
|
-
"description": "A key-value Pair of environment variables to pass to Cypress runner."
|
|
82
|
-
},
|
|
83
|
-
"spec": {
|
|
84
|
-
"type": "string",
|
|
85
|
-
"description": "A comma delimited glob string that is provided to the Cypress runner to specify which spec files to run. i.e. `**examples/**,**actions.spec**`."
|
|
86
|
-
},
|
|
87
|
-
"ciBuildId": {
|
|
88
|
-
"oneOf": [
|
|
89
|
-
{
|
|
90
|
-
"type": "string"
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
"type": "number"
|
|
94
|
-
}
|
|
95
|
-
],
|
|
96
|
-
"description": "A unique identifier for a run to enable grouping or parallelization."
|
|
97
|
-
},
|
|
98
|
-
"group": {
|
|
99
|
-
"type": "string",
|
|
100
|
-
"description": "A named group for recorded runs in the Cypress dashboard."
|
|
101
|
-
},
|
|
102
|
-
"ignoreTestFiles": {
|
|
103
|
-
"aliases": ["excludeSpecPattern"],
|
|
104
|
-
"oneOf": [
|
|
105
|
-
{
|
|
106
|
-
"type": "string"
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
"type": "array",
|
|
110
|
-
"items": {
|
|
111
|
-
"type": "string"
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
],
|
|
115
|
-
"description": "A String or Array of glob patterns used to ignore test files that would otherwise be shown in your list of tests. Cypress uses minimatch with the options: `{dot: true, matchBase: true}`. We suggest using https://globster.xyz to test what files would match."
|
|
116
|
-
},
|
|
117
|
-
"reporter": {
|
|
118
|
-
"type": "string",
|
|
119
|
-
"description": "The reporter used during cypress run."
|
|
120
|
-
},
|
|
121
|
-
"reporterOptions": {
|
|
122
|
-
"oneOf": [
|
|
123
|
-
{
|
|
124
|
-
"type": "string"
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
"type": "object"
|
|
128
|
-
}
|
|
129
|
-
],
|
|
130
|
-
"description": "The reporter options used. Supported options depend on the reporter. https://docs.cypress.io/guides/tooling/reporters#Reporter-Options"
|
|
131
|
-
},
|
|
132
|
-
"skipServe": {
|
|
133
|
-
"type": "boolean",
|
|
134
|
-
"description": "Skip dev-server build.",
|
|
135
|
-
"default": false
|
|
136
|
-
},
|
|
137
|
-
"testingType": {
|
|
138
|
-
"type": "string",
|
|
139
|
-
"description": "Specify the type of tests to execute.",
|
|
140
|
-
"enum": ["component", "e2e"],
|
|
141
|
-
"default": "e2e"
|
|
142
|
-
},
|
|
143
|
-
"tag": {
|
|
144
|
-
"type": "string",
|
|
145
|
-
"description": "A comma delimited list to identify a run with.",
|
|
146
|
-
"aliases": ["t"]
|
|
147
|
-
},
|
|
148
|
-
"port": {
|
|
149
|
-
"oneOf": [
|
|
150
|
-
{
|
|
151
|
-
"type": "string",
|
|
152
|
-
"enum": ["cypress-auto"]
|
|
153
|
-
},
|
|
154
|
-
{
|
|
155
|
-
"type": "number"
|
|
156
|
-
}
|
|
157
|
-
],
|
|
158
|
-
"description": "Pass a specified port value to the devServerTarget, if the value is 'cypress-auto' a free port will automatically be picked for the devServerTarget."
|
|
159
|
-
},
|
|
160
|
-
"quiet": {
|
|
161
|
-
"aliases": ["q"],
|
|
162
|
-
"type": "boolean",
|
|
163
|
-
"description": "If passed, Cypress output will not be printed to stdout. Only output from the configured Mocha reporter will print.",
|
|
164
|
-
"default": false
|
|
165
|
-
},
|
|
166
|
-
"runnerUi": {
|
|
167
|
-
"type": "boolean",
|
|
168
|
-
"description": "Displays the Cypress Runner UI. Useful for when Test Replay is enabled and you would still like the Cypress Runner UI to be displayed for screenshots and video."
|
|
169
|
-
},
|
|
170
|
-
"autoCancelAfterFailures": {
|
|
171
|
-
"oneOf": [
|
|
172
|
-
{
|
|
173
|
-
"type": "number"
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
"type": "boolean",
|
|
177
|
-
"const": false
|
|
178
|
-
}
|
|
179
|
-
],
|
|
180
|
-
"description": "Specify the number of failures to cancel a run being recorded to the Cypress Cloud or `false` to disable auto-cancellation."
|
|
181
|
-
}
|
|
182
|
-
},
|
|
183
|
-
"additionalProperties": true,
|
|
184
|
-
"required": ["cypressConfig"],
|
|
185
|
-
"examplesFile": "../../../docs/cypress-examples.md"
|
|
186
|
-
}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
export declare const CYPRESS_EXECUTOR_DEPRECATION_MESSAGE = "The `@nx/cypress:cypress` executor is deprecated and will be removed in Nx v24. Run `nx g @nx/cypress:convert-to-inferred` to migrate to the `@nx/cypress/plugin` inferred targets. See https://nx.dev/docs/guides/tasks--caching/convert-to-inferred for details.";
|
|
2
|
-
export declare function warnCypressExecutorDeprecation(): void;
|
|
3
|
-
export declare function warnCypressExecutorGenerating(): void;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CYPRESS_EXECUTOR_DEPRECATION_MESSAGE = void 0;
|
|
4
|
-
exports.warnCypressExecutorDeprecation = warnCypressExecutorDeprecation;
|
|
5
|
-
exports.warnCypressExecutorGenerating = warnCypressExecutorGenerating;
|
|
6
|
-
const devkit_1 = require("@nx/devkit");
|
|
7
|
-
// TODO(v24): Remove the @nx/cypress:cypress executor. The inferred plugin
|
|
8
|
-
// (@nx/cypress/plugin) and the convert-to-inferred generator stay supported.
|
|
9
|
-
exports.CYPRESS_EXECUTOR_DEPRECATION_MESSAGE = 'The `@nx/cypress:cypress` executor is deprecated and will be removed in Nx v24. Run `nx g @nx/cypress:convert-to-inferred` to migrate to the `@nx/cypress/plugin` inferred targets. See https://nx.dev/docs/guides/tasks--caching/convert-to-inferred for details.';
|
|
10
|
-
function warnCypressExecutorDeprecation() {
|
|
11
|
-
devkit_1.logger.warn(exports.CYPRESS_EXECUTOR_DEPRECATION_MESSAGE);
|
|
12
|
-
}
|
|
13
|
-
// Fired when the @nx/cypress:configuration or :component-configuration
|
|
14
|
-
// generator is about to generate a target that uses the deprecated executor —
|
|
15
|
-
// i.e. when @nx/cypress/plugin isn't registered in nx.json. Surfaces the
|
|
16
|
-
// deprecation at generation time rather than only when the user later runs the
|
|
17
|
-
// generated target.
|
|
18
|
-
function warnCypressExecutorGenerating() {
|
|
19
|
-
devkit_1.logger.warn('Generating a target that uses the deprecated `@nx/cypress:cypress` executor. The executor will be removed in Nx v24. Run `nx g @nx/cypress:convert-to-inferred` next to migrate this target to the `@nx/cypress/plugin` inferred plugin and prevent future generators from emitting executor targets. See https://nx.dev/docs/guides/tasks--caching/convert-to-inferred for details.');
|
|
20
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { ExecutorContext } from '@nx/devkit';
|
|
2
|
-
import { CypressExecutorOptions } from '../executors/cypress/cypress.impl';
|
|
3
|
-
export declare function startDevServer(opts: Omit<CypressExecutorOptions, 'cypressConfig'>, context: ExecutorContext): AsyncGenerator<{
|
|
4
|
-
baseUrl: any;
|
|
5
|
-
portLockFilePath?: undefined;
|
|
6
|
-
} | {
|
|
7
|
-
baseUrl: any;
|
|
8
|
-
portLockFilePath: string;
|
|
9
|
-
}, void, unknown>;
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.startDevServer = startDevServer;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
const devkit_1 = require("@nx/devkit");
|
|
6
|
-
const path_1 = require("path");
|
|
7
|
-
const detect_port_1 = tslib_1.__importDefault(require("detect-port"));
|
|
8
|
-
const fs_1 = require("fs");
|
|
9
|
-
const os_1 = require("os");
|
|
10
|
-
const internal_1 = require("@nx/devkit/internal");
|
|
11
|
-
const PORT_LOCK_DIR = (0, path_1.join)((0, os_1.tmpdir)(), 'nx-cypress-port-locks');
|
|
12
|
-
async function* startDevServer(opts, context) {
|
|
13
|
-
// no dev server, return the provisioned base url
|
|
14
|
-
if (!opts.devServerTarget || opts.skipServe) {
|
|
15
|
-
yield { baseUrl: opts.baseUrl };
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
const parsedDevServerTarget = (0, devkit_1.parseTargetString)(opts.devServerTarget, context);
|
|
19
|
-
const [targetSupportsWatchOpt] = getValueFromSchema(context, parsedDevServerTarget, 'watch');
|
|
20
|
-
const overrides = {
|
|
21
|
-
// @NOTE: Do not forward watch option if not supported by the target dev server,
|
|
22
|
-
// this is relevant for running Cypress against dev server target that does not support this option,
|
|
23
|
-
// for instance @nguniversal/builders:ssr-dev-server.
|
|
24
|
-
...(targetSupportsWatchOpt ? { watch: opts.watch } : {}),
|
|
25
|
-
};
|
|
26
|
-
if (opts.port === 'cypress-auto') {
|
|
27
|
-
const freePort = await getPortForProject(context, parsedDevServerTarget);
|
|
28
|
-
overrides['port'] = freePort;
|
|
29
|
-
}
|
|
30
|
-
else if (opts.port !== undefined) {
|
|
31
|
-
overrides['port'] = opts.port;
|
|
32
|
-
// zero is a special case that means any valid port so there is no reason to try to 'lock it'
|
|
33
|
-
if (opts.port !== 0) {
|
|
34
|
-
const didLock = attemptToLockPort(opts.port);
|
|
35
|
-
if (!didLock) {
|
|
36
|
-
devkit_1.logger.warn((0, devkit_1.stripIndents) `${opts.port} is potentially already in use by another cypress run.
|
|
37
|
-
If the port is in use, try using a different port value or passing --port='cypress-auto' to find a free port.`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
for await (const output of await (0, devkit_1.runExecutor)(parsedDevServerTarget, overrides, context)) {
|
|
42
|
-
if (!output.success && !opts.watch)
|
|
43
|
-
throw new Error('Could not compile application files');
|
|
44
|
-
if (!opts.baseUrl &&
|
|
45
|
-
!output.baseUrl &&
|
|
46
|
-
!output.info?.baseUrl &&
|
|
47
|
-
(output.port || output.info?.port)) {
|
|
48
|
-
output.baseUrl = `http://localhost:${output.info.port}`;
|
|
49
|
-
}
|
|
50
|
-
yield {
|
|
51
|
-
baseUrl: opts.baseUrl || output.baseUrl || output.info?.baseUrl,
|
|
52
|
-
portLockFilePath: overrides.port && (0, path_1.join)(PORT_LOCK_DIR, `${overrides.port}.lock`),
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* try to find a free port for the project to run on
|
|
58
|
-
* will return undefined if no port is found or the project doesn't have a port option
|
|
59
|
-
**/
|
|
60
|
-
async function getPortForProject(context, target, defaultPort = 4200) {
|
|
61
|
-
const fmtTarget = (0, devkit_1.targetToTargetString)(target);
|
|
62
|
-
const [hasPortOpt, schemaPortValue] = getValueFromSchema(context, target, 'port');
|
|
63
|
-
let freePort;
|
|
64
|
-
if (hasPortOpt) {
|
|
65
|
-
let normalizedPortValue;
|
|
66
|
-
if (!schemaPortValue) {
|
|
67
|
-
devkit_1.logger.info(`NX ${fmtTarget} did not have a defined port value, checking for free port with the default value of ${defaultPort}`);
|
|
68
|
-
normalizedPortValue = defaultPort;
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
normalizedPortValue = Number(schemaPortValue);
|
|
72
|
-
}
|
|
73
|
-
if (isNaN(normalizedPortValue)) {
|
|
74
|
-
devkit_1.output.warn({
|
|
75
|
-
title: `Port Not a Number`,
|
|
76
|
-
bodyLines: [
|
|
77
|
-
`The port value found was not a number or can't be parsed to a number`,
|
|
78
|
-
`When reading the devServerTarget (${fmtTarget}) schema, expected ${schemaPortValue} to be a number but got NaN.`,
|
|
79
|
-
`Nx will use the default value of ${defaultPort} instead.`,
|
|
80
|
-
`You can manually specify a port by setting the 'port' option`,
|
|
81
|
-
],
|
|
82
|
-
});
|
|
83
|
-
normalizedPortValue = defaultPort;
|
|
84
|
-
}
|
|
85
|
-
try {
|
|
86
|
-
let attempts = 0;
|
|
87
|
-
// make sure when this check happens in parallel,
|
|
88
|
-
// we don't let the same port be used by multiple projects
|
|
89
|
-
do {
|
|
90
|
-
freePort = await (0, detect_port_1.default)(freePort || normalizedPortValue);
|
|
91
|
-
if (attemptToLockPort(freePort)) {
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
attempts++;
|
|
95
|
-
// increment port in case the lock file isn't cleaned up
|
|
96
|
-
freePort++;
|
|
97
|
-
} while (attempts < 20);
|
|
98
|
-
devkit_1.logger.info(`NX Using port ${freePort} for ${fmtTarget}`);
|
|
99
|
-
}
|
|
100
|
-
catch (err) {
|
|
101
|
-
throw new Error((0, devkit_1.stripIndents) `Unable to find a free port for the dev server, ${fmtTarget}.
|
|
102
|
-
You can disable auto port detection by specifing a port or not passing a value to --port`);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
devkit_1.output.warn({
|
|
107
|
-
title: `No Port Option Found`,
|
|
108
|
-
bodyLines: [
|
|
109
|
-
`The 'port' option is set to 'cypress-auto', but the devServerTarget (${fmtTarget}) does not have a port option.`,
|
|
110
|
-
`Because of this, Nx is unable to verify the port is free before starting the dev server.`,
|
|
111
|
-
`This might cause issues if the devServerTarget is trying to use a port that is already in use.`,
|
|
112
|
-
],
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
return freePort;
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Check if the given target has the given property in it's options.
|
|
119
|
-
* if the property is does not have a default value or is not in the actual executor options,
|
|
120
|
-
* the value will be undefined even if it's in the executor schema.
|
|
121
|
-
**/
|
|
122
|
-
function getValueFromSchema(context, target, property) {
|
|
123
|
-
let targetOpts;
|
|
124
|
-
try {
|
|
125
|
-
targetOpts = (0, devkit_1.readTargetOptions)(target, context);
|
|
126
|
-
}
|
|
127
|
-
catch (e) {
|
|
128
|
-
throw new Error(`Unable to read the target options for ${(0, devkit_1.targetToTargetString)(target)}.
|
|
129
|
-
Are you sure this is a valid target?
|
|
130
|
-
Was trying to read the target for the property: '${property}', but got the following error:
|
|
131
|
-
${e.message || e}`);
|
|
132
|
-
}
|
|
133
|
-
let targetHasOpt = Object.keys(targetOpts).includes(property);
|
|
134
|
-
if (!targetHasOpt) {
|
|
135
|
-
// NOTE: readTargetOptions doesn't apply non defaulted values, i.e. @nx/vite has a port options but is optional
|
|
136
|
-
// so we double check the schema if readTargetOptions didn't return a value for the property
|
|
137
|
-
const projectConfig = context.projectsConfigurations?.projects?.[target.project];
|
|
138
|
-
const targetConfig = projectConfig.targets[target.target];
|
|
139
|
-
const [collection, executor] = (0, internal_1.parseExecutor)(targetConfig.executor);
|
|
140
|
-
const { schema } = (0, internal_1.getExecutorInformation)(collection, executor, context.root, context.projectsConfigurations.projects);
|
|
141
|
-
// NOTE: schema won't have a default since readTargetOptions would have
|
|
142
|
-
// already set that and this check wouldn't need to be made
|
|
143
|
-
targetHasOpt = Object.keys(schema.properties).includes(property);
|
|
144
|
-
}
|
|
145
|
-
return [targetHasOpt, targetOpts[property]];
|
|
146
|
-
}
|
|
147
|
-
function attemptToLockPort(port) {
|
|
148
|
-
try {
|
|
149
|
-
(0, fs_1.mkdirSync)(PORT_LOCK_DIR, { recursive: true });
|
|
150
|
-
}
|
|
151
|
-
catch {
|
|
152
|
-
// ignore if already exists
|
|
153
|
-
}
|
|
154
|
-
const portLockFilePath = (0, path_1.join)(PORT_LOCK_DIR, `${port}.lock`);
|
|
155
|
-
try {
|
|
156
|
-
if ((0, fs_1.existsSync)(portLockFilePath)) {
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
|
-
(0, fs_1.writeFileSync)(portLockFilePath, 'locked');
|
|
160
|
-
return true;
|
|
161
|
-
}
|
|
162
|
-
catch (err) {
|
|
163
|
-
return false;
|
|
164
|
-
}
|
|
165
|
-
}
|