@nx/cypress 24.0.0-beta.2 → 24.0.0-beta.4
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/plugins/cypress-preset.js +47 -3
- 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-23-3-0/upgrade-to-cypress-16.js +3 -2
- package/dist/src/migrations/update-24-0-0/disable-wait-for-server-ready.d.ts +4 -0
- package/dist/src/migrations/update-24-0-0/disable-wait-for-server-ready.js +35 -0
- package/dist/src/migrations/update-24-0-0/disable-wait-for-server-ready.md +33 -0
- 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.d.ts +5 -0
- package/dist/src/plugins/plugin.js +4 -1
- package/dist/src/utils/add-linter.d.ts +0 -1
- package/dist/src/utils/add-linter.js +0 -1
- package/dist/src/utils/find-target-options.js +0 -24
- package/executors.json +1 -7
- package/migrations.json +14 -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';
|
|
@@ -130,6 +130,19 @@ function nxE2EPreset(pathToConfig, options) {
|
|
|
130
130
|
if (options?.bundler === 'vite') {
|
|
131
131
|
on('file:preprocessor', (0, preprocessor_vite_1.default)(options?.viteConfigOverrides));
|
|
132
132
|
}
|
|
133
|
+
// Set by Nx when the web server task already reported it is ready
|
|
134
|
+
const readyUrl = process.env.NX_READY_URL;
|
|
135
|
+
if (readyUrl && isBaseUrlOverridden(config, options)) {
|
|
136
|
+
console.log(`Testing against ${config.baseUrl} from --config or CYPRESS_BASE_URL, not the server Nx started on ${readyUrl}`);
|
|
137
|
+
await waitForServer(config.baseUrl, options?.webServerConfig, (ms) => `${config.baseUrl} did not respond within ${ms}ms. This can be configured in cypress.config.ts.`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (readyUrl) {
|
|
141
|
+
config.baseUrl = (0, devkit_1.getReadyUrl)(config.baseUrl);
|
|
142
|
+
console.log(`Testing against the server Nx started on ${config.baseUrl}`);
|
|
143
|
+
// Cypress only applies config changes returned from setupNodeEvents.
|
|
144
|
+
return config;
|
|
145
|
+
}
|
|
133
146
|
if (!options?.webServerCommands) {
|
|
134
147
|
return;
|
|
135
148
|
}
|
|
@@ -158,23 +171,54 @@ function nxE2EPreset(pathToConfig, options) {
|
|
|
158
171
|
};
|
|
159
172
|
return baseConfig;
|
|
160
173
|
}
|
|
161
|
-
|
|
174
|
+
// Cypress keeps the config file's values in rawJson, without --config, and
|
|
175
|
+
// reports CYPRESS_BASE_URL as from 'env'. A config file that reads
|
|
176
|
+
// CYPRESS_BASE_URL has it in rawJson too. Inferred CI targets pass ciBaseUrl.
|
|
177
|
+
function isBaseUrlOverridden(config, options) {
|
|
178
|
+
if (!config.baseUrl) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
if (config.resolved?.baseUrl?.from === 'env') {
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
const isInferredCiTarget = !!options?.ciWebServerCommand &&
|
|
185
|
+
config.env?.webServerCommand === options.ciWebServerCommand;
|
|
186
|
+
return (!isSameUrl(config.baseUrl, config.rawJson?.e2e?.baseUrl) &&
|
|
187
|
+
!(isInferredCiTarget && isSameUrl(config.baseUrl, options.ciBaseUrl)));
|
|
188
|
+
}
|
|
189
|
+
function isSameUrl(url, other) {
|
|
190
|
+
if (typeof other !== 'string') {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
return new URL(url).href === new URL(other).href;
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return url === other;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function waitForServer(url, webServerConfig, timeoutMessage = (ms) => `Web server failed to start in ${ms}ms. This can be configured in cypress.config.ts.`) {
|
|
162
201
|
return new Promise((resolve, reject) => {
|
|
163
202
|
let pollTimeout;
|
|
164
203
|
const { protocol } = new URL(url);
|
|
204
|
+
let settled = false;
|
|
165
205
|
const timeoutDuration = webServerConfig?.timeout ?? 60 * 1000;
|
|
166
206
|
const timeout = setTimeout(() => {
|
|
207
|
+
settled = true;
|
|
167
208
|
clearTimeout(pollTimeout);
|
|
168
|
-
reject(new Error(
|
|
209
|
+
reject(new Error(timeoutMessage(timeoutDuration)));
|
|
169
210
|
}, timeoutDuration);
|
|
170
211
|
const makeRequest = protocol === 'https:' ? https_1.request : http_1.request;
|
|
171
212
|
function pollForServer() {
|
|
172
213
|
const request = makeRequest(url, () => {
|
|
214
|
+
settled = true;
|
|
173
215
|
clearTimeout(timeout);
|
|
174
216
|
resolve();
|
|
175
217
|
});
|
|
176
218
|
request.on('error', () => {
|
|
177
|
-
|
|
219
|
+
if (!settled) {
|
|
220
|
+
pollTimeout = setTimeout(pollForServer, 100);
|
|
221
|
+
}
|
|
178
222
|
});
|
|
179
223
|
// Don't forget to end the request
|
|
180
224
|
request.end();
|
|
@@ -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
|
|
|
@@ -29,8 +29,9 @@ const NX_WEBPACK_CT_PRESETS = [
|
|
|
29
29
|
'@nx/react/plugins/component-testing',
|
|
30
30
|
];
|
|
31
31
|
// Re-collects the Cypress 16 migrations: below their version, above every
|
|
32
|
-
// 23.2.x. `--from` is exclusive.
|
|
33
|
-
|
|
32
|
+
// 23.2.x. `--from` is exclusive. `--include=all` overrides nx.json and the
|
|
33
|
+
// prompt: `required` skips the upgrade and `optional` rejects `--from`.
|
|
34
|
+
const REPLAN_COMMAND = `npx nx migrate @nx/cypress@${versions_1.nxVersion} --from=@nx/cypress@23.3.0-beta.0 --include=all`;
|
|
34
35
|
const RERUN_COMMAND = `${REPLAN_COMMAND} && npx nx migrate --run-migration=@nx/cypress:upgrade-to-cypress-16`;
|
|
35
36
|
let ts;
|
|
36
37
|
async function upgradeToCypress16(tree) {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = update;
|
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const pluginName = '@nx/cypress/plugin';
|
|
6
|
+
async function update(tree) {
|
|
7
|
+
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
8
|
+
if (!nxJson?.plugins) {
|
|
9
|
+
return { skipAgentic: true };
|
|
10
|
+
}
|
|
11
|
+
let changed = false;
|
|
12
|
+
nxJson.plugins = nxJson.plugins.map((plugin) => {
|
|
13
|
+
if (typeof plugin === 'string') {
|
|
14
|
+
if (plugin !== pluginName) {
|
|
15
|
+
return plugin;
|
|
16
|
+
}
|
|
17
|
+
changed = true;
|
|
18
|
+
return { plugin, options: { waitForServerReady: false } };
|
|
19
|
+
}
|
|
20
|
+
if (plugin.plugin !== pluginName) {
|
|
21
|
+
return plugin;
|
|
22
|
+
}
|
|
23
|
+
const options = (plugin.options ?? {});
|
|
24
|
+
if (options.waitForServerReady !== undefined) {
|
|
25
|
+
return plugin;
|
|
26
|
+
}
|
|
27
|
+
changed = true;
|
|
28
|
+
return { ...plugin, options: { ...options, waitForServerReady: false } };
|
|
29
|
+
});
|
|
30
|
+
if (!changed) {
|
|
31
|
+
return { skipAgentic: true };
|
|
32
|
+
}
|
|
33
|
+
(0, devkit_1.updateNxJson)(tree, nxJson);
|
|
34
|
+
await (0, devkit_1.formatFiles)(tree);
|
|
35
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#### Keep the Cypress e2e tasks from waiting for the dev server to be ready
|
|
2
|
+
|
|
3
|
+
The `@nx/cypress/plugin` option `waitForServerReady` now defaults to `true`.
|
|
4
|
+
With it on, the inferred e2e tasks wait for the dev server task's `readyWhen` check, and `nxE2EPreset` tests against the address the server reported instead of `baseUrl`.
|
|
5
|
+
|
|
6
|
+
This migration sets `waitForServerReady` to `false` on every `@nx/cypress/plugin` entry in `nx.json`, so existing workspaces keep the old behavior.
|
|
7
|
+
It expands a string entry into an object entry, keeps `include`, `exclude`, and other options, and leaves an entry alone when it already sets `waitForServerReady`.
|
|
8
|
+
To opt in later, set the option to `true`.
|
|
9
|
+
|
|
10
|
+
#### Sample code changes
|
|
11
|
+
|
|
12
|
+
##### Before
|
|
13
|
+
|
|
14
|
+
```json title="nx.json"
|
|
15
|
+
{
|
|
16
|
+
"plugins": ["@nx/cypress/plugin"]
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
##### After
|
|
21
|
+
|
|
22
|
+
```json title="nx.json"
|
|
23
|
+
{
|
|
24
|
+
"plugins": [
|
|
25
|
+
{
|
|
26
|
+
"plugin": "@nx/cypress/plugin",
|
|
27
|
+
"options": {
|
|
28
|
+
"waitForServerReady": false
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
```
|
|
@@ -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
|
+
}
|