@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.
Files changed (34) hide show
  1. package/dist/internal.d.ts +1 -1
  2. package/dist/plugins/cypress-preset.js +47 -3
  3. package/dist/src/executors/cypress/{cypress.impl.d.ts → schema.d.ts} +0 -8
  4. package/dist/src/executors/cypress/schema.js +2 -0
  5. package/dist/src/generators/component-configuration/component-configuration.js +7 -57
  6. package/dist/src/generators/component-configuration/schema.d.ts +0 -2
  7. package/dist/src/generators/configuration/configuration.d.ts +0 -1
  8. package/dist/src/generators/configuration/configuration.js +13 -64
  9. package/dist/src/generators/init/init.js +2 -45
  10. package/dist/src/generators/init/schema.d.ts +0 -1
  11. package/dist/src/migrations/update-23-3-0/adapt-query-command-overwrites.md +1 -1
  12. package/dist/src/migrations/update-23-3-0/ai-instructions-for-cypress-16.md +2 -2
  13. package/dist/src/migrations/update-23-3-0/upgrade-to-cypress-16.js +3 -2
  14. package/dist/src/migrations/update-24-0-0/disable-wait-for-server-ready.d.ts +4 -0
  15. package/dist/src/migrations/update-24-0-0/disable-wait-for-server-ready.js +35 -0
  16. package/dist/src/migrations/update-24-0-0/disable-wait-for-server-ready.md +33 -0
  17. package/dist/src/migrations/update-24-0-0/finish-inferred-conversion.md +12 -0
  18. package/dist/src/migrations/update-24-0-0/remove-deprecated-executors.d.ts +6 -0
  19. package/dist/src/migrations/update-24-0-0/remove-deprecated-executors.js +10 -0
  20. package/dist/src/migrations/update-24-0-0/remove-deprecated-executors.md +61 -0
  21. package/dist/src/plugins/plugin.d.ts +5 -0
  22. package/dist/src/plugins/plugin.js +4 -1
  23. package/dist/src/utils/add-linter.d.ts +0 -1
  24. package/dist/src/utils/add-linter.js +0 -1
  25. package/dist/src/utils/find-target-options.js +0 -24
  26. package/executors.json +1 -7
  27. package/migrations.json +14 -0
  28. package/package.json +6 -7
  29. package/dist/src/executors/cypress/cypress.impl.js +0 -136
  30. package/dist/src/executors/cypress/schema.json +0 -186
  31. package/dist/src/utils/deprecation.d.ts +0 -3
  32. package/dist/src/utils/deprecation.js +0 -20
  33. package/dist/src/utils/start-dev-server.d.ts +0 -9
  34. package/dist/src/utils/start-dev-server.js +0 -165
@@ -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/cypress.impl';
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
- function waitForServer(url, webServerConfig) {
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(`Web server failed to start in ${timeoutDuration}ms. This can be configured in cypress.config.ts.`));
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
- pollTimeout = setTimeout(pollForServer, 100);
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
- }>;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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.__importDefault(require("../init/init"));
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(tree, options);
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
- const nxJson = (0, devkit_1.readNxJson)(tree);
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
- if (!hasPlugin || opts.addExplicitTargets) {
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(tree, options) {
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 addTargetToProject(tree, projectConfig, opts) {
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 if (opts.addPlugin) {
32
+ else {
37
33
  await (0, init_1.addPlugin)(tree, projectGraph, false);
38
34
  }
39
- const nxJson = (0, devkit_1.readNxJson)(tree);
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, hasPlugin) {
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 (hasPlugin && options.webServerCommands && options.ciWebServerCommand) {
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 (hasPlugin && options.devServerTarget) {
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, { addPlugin: false, ...options });
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
- const nxJson = (0, devkit_1.readNxJson)(tree);
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);
@@ -3,5 +3,4 @@ export interface Schema {
3
3
  skipPackageJson?: boolean;
4
4
  keepExistingVersions?: boolean;
5
5
  updatePackageScripts?: boolean;
6
- addPlugin?: boolean;
7
6
  }
@@ -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. `<files_changed>` or `<advisory_context>` lists at least one file with a renamed `Cypress.Commands.overwriteQuery()` call. Search the workspace for `Cypress.Commands.overwriteQuery(` if the wrapper sections are absent.
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. Review `<advisory_context>` from the deterministic migrations: 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.
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; `<advisory_context>` lists each renamed call. 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:
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
- const REPLAN_COMMAND = `npx nx migrate @nx/cypress@${versions_1.nxVersion} --from=@nx/cypress@23.3.0-beta.0`;
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,4 @@
1
+ import { type Tree } from '@nx/devkit';
2
+ export default function update(tree: Tree): Promise<{
3
+ skipAgentic: boolean;
4
+ }>;
@@ -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,6 @@
1
+ import type { Tree } from '@nx/devkit';
2
+ export default function update(tree: Tree): Promise<{
3
+ nextSteps?: string[];
4
+ agentContext?: string[];
5
+ skipAgentic?: boolean;
6
+ }>;
@@ -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
+ }