@nx/cypress 19.0.0-beta.7 → 19.0.0-beta.9
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/generators.json +5 -0
- package/migrations.json +0 -24
- package/package.json +5 -5
- package/src/generators/convert-to-inferred/convert-to-inferred.d.ts +8 -0
- package/src/generators/convert-to-inferred/convert-to-inferred.js +86 -0
- package/src/generators/convert-to-inferred/lib/add-dev-server-target-to-config.d.ts +12 -0
- package/src/generators/convert-to-inferred/lib/add-dev-server-target-to-config.js +56 -0
- package/src/generators/convert-to-inferred/lib/add-exclude-spec-pattern.d.ts +2 -0
- package/src/generators/convert-to-inferred/lib/add-exclude-spec-pattern.js +28 -0
- package/src/generators/convert-to-inferred/lib/target-options-map.d.ts +18 -0
- package/src/generators/convert-to-inferred/lib/target-options-map.js +21 -0
- package/src/generators/convert-to-inferred/lib/upsert-baseUrl.d.ts +2 -0
- package/src/generators/convert-to-inferred/lib/upsert-baseUrl.js +33 -0
- package/src/generators/convert-to-inferred/schema.json +19 -0
- package/src/migrations/update-15-0-0/add-cypress-inputs.d.ts +0 -2
- package/src/migrations/update-15-0-0/add-cypress-inputs.js +0 -42
- package/src/migrations/update-15-0-0/update-cy-mount-usage.d.ts +0 -5
- package/src/migrations/update-15-0-0/update-cy-mount-usage.js +0 -119
- package/src/migrations/update-15-1-0/cypress-11.d.ts +0 -6
- package/src/migrations/update-15-1-0/cypress-11.js +0 -114
- package/src/migrations/update-15-5-0/update-to-cypress-12.d.ts +0 -24
- package/src/migrations/update-15-5-0/update-to-cypress-12.js +0 -180
package/generators.json
CHANGED
|
@@ -32,6 +32,11 @@
|
|
|
32
32
|
"factory": "./src/generators/migrate-to-cypress-11/migrate-to-cypress-11#migrateCypressProject",
|
|
33
33
|
"schema": "./src/generators/migrate-to-cypress-11/schema.json",
|
|
34
34
|
"description": "Migrate existing Cypress e2e projects to Cypress v11"
|
|
35
|
+
},
|
|
36
|
+
"convert-to-inferred": {
|
|
37
|
+
"factory": "./src/generators/convert-to-inferred/convert-to-inferred",
|
|
38
|
+
"schema": "./src/generators/convert-to-inferred/schema.json",
|
|
39
|
+
"description": "Convert existing Cypress project(s) using `@nx/cypress:cypress` executor to use `@nx/cypress/plugin`."
|
|
35
40
|
}
|
|
36
41
|
}
|
|
37
42
|
}
|
package/migrations.json
CHANGED
|
@@ -1,29 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"generators": {
|
|
3
|
-
"add-cypress-inputs": {
|
|
4
|
-
"cli": "nx",
|
|
5
|
-
"version": "15.0.0-beta.0",
|
|
6
|
-
"description": "Stop hashing cypress spec files and config files for build targets and dependent tasks",
|
|
7
|
-
"factory": "./src/migrations/update-15-0-0/add-cypress-inputs"
|
|
8
|
-
},
|
|
9
|
-
"update-cy-mount-usage": {
|
|
10
|
-
"cli": "nx",
|
|
11
|
-
"version": "15.0.0-beta.4",
|
|
12
|
-
"description": "Update to using cy.mount in the commands.ts file instead of importing mount for each component test file",
|
|
13
|
-
"factory": "./src/migrations/update-15-0-0/update-cy-mount-usage"
|
|
14
|
-
},
|
|
15
|
-
"update-to-cypress-11": {
|
|
16
|
-
"cli": "nx",
|
|
17
|
-
"version": "15.1.0-beta.0",
|
|
18
|
-
"description": "Update to Cypress v11. This migration will only update if the workspace is already on v10. https://www.cypress.io/blog/2022/11/04/upcoming-changes-to-component-testing/",
|
|
19
|
-
"factory": "./src/migrations/update-15-1-0/cypress-11"
|
|
20
|
-
},
|
|
21
|
-
"update-to-cypress-12": {
|
|
22
|
-
"cli": "nx",
|
|
23
|
-
"version": "15.5.0-beta.0",
|
|
24
|
-
"description": "Update to Cypress v12. Cypress 12 contains a handful of breaking changes that might causes tests to start failing that nx cannot directly fix. Read more Cypress 12 changes: https://docs.cypress.io/guides/references/migration-guide#Migrating-to-Cypress-12-0.This migration will only run if you are already using Cypress v11.",
|
|
25
|
-
"factory": "./src/migrations/update-15-5-0/update-to-cypress-12"
|
|
26
|
-
},
|
|
27
3
|
"update-16-0-0-add-nx-packages": {
|
|
28
4
|
"cli": "nx",
|
|
29
5
|
"version": "16.0.0-beta.1",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nx/cypress",
|
|
3
|
-
"version": "19.0.0-beta.
|
|
3
|
+
"version": "19.0.0-beta.9",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The Nx Plugin for Cypress contains executors and generators allowing your workspace to use the powerful Cypress integration testing capabilities.",
|
|
6
6
|
"repository": {
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
"migrations": "./migrations.json"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@nx/devkit": "19.0.0-beta.
|
|
38
|
-
"@nx/eslint": "19.0.0-beta.
|
|
39
|
-
"@nx/js": "19.0.0-beta.
|
|
37
|
+
"@nx/devkit": "19.0.0-beta.9",
|
|
38
|
+
"@nx/eslint": "19.0.0-beta.9",
|
|
39
|
+
"@nx/js": "19.0.0-beta.9",
|
|
40
40
|
"@phenomnomnominal/tsquery": "~5.0.1",
|
|
41
41
|
"detect-port": "^1.5.1",
|
|
42
42
|
"semver": "^7.5.3",
|
|
43
43
|
"tslib": "^2.3.0",
|
|
44
|
-
"@nrwl/cypress": "19.0.0-beta.
|
|
44
|
+
"@nrwl/cypress": "19.0.0-beta.9"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"cypress": ">= 3 < 14"
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.convertToInferred = void 0;
|
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const executor_to_plugin_migrator_1 = require("@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator");
|
|
6
|
+
const plugin_1 = require("../../plugins/plugin");
|
|
7
|
+
const target_options_map_1 = require("./lib/target-options-map");
|
|
8
|
+
const upsert_baseUrl_1 = require("./lib/upsert-baseUrl");
|
|
9
|
+
const add_dev_server_target_to_config_1 = require("./lib/add-dev-server-target-to-config");
|
|
10
|
+
const add_exclude_spec_pattern_1 = require("./lib/add-exclude-spec-pattern");
|
|
11
|
+
async function convertToInferred(tree, options) {
|
|
12
|
+
const projectGraph = await (0, devkit_1.createProjectGraphAsync)();
|
|
13
|
+
await (0, executor_to_plugin_migrator_1.migrateExecutorToPlugin)(tree, projectGraph, '@nx/cypress:cypress', '@nx/cypress/plugin', (targetName) => ({
|
|
14
|
+
targetName,
|
|
15
|
+
ciTargetName: 'e2e-ci',
|
|
16
|
+
}), postTargetTransformer, plugin_1.createNodes, options.project);
|
|
17
|
+
if (!options.skipFormat) {
|
|
18
|
+
await (0, devkit_1.formatFiles)(tree);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.convertToInferred = convertToInferred;
|
|
22
|
+
function postTargetTransformer(target, tree) {
|
|
23
|
+
if (target.options) {
|
|
24
|
+
const configFilePath = target.options.cypressConfig;
|
|
25
|
+
delete target.options.cypressConfig;
|
|
26
|
+
delete target.options.copyFiles;
|
|
27
|
+
delete target.options.skipServe;
|
|
28
|
+
for (const key in target_options_map_1.targetOptionsToCliMap) {
|
|
29
|
+
if (target.options[key]) {
|
|
30
|
+
target.options[target_options_map_1.targetOptionsToCliMap[key]] = target.options[key];
|
|
31
|
+
delete target.options[key];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if ('exit' in target.options && !target.options.exit) {
|
|
35
|
+
delete target.options.exit;
|
|
36
|
+
target.options['no-exit'] = true;
|
|
37
|
+
}
|
|
38
|
+
if (target.options.testingType) {
|
|
39
|
+
delete target.options.testingType;
|
|
40
|
+
}
|
|
41
|
+
if (target.options.watch) {
|
|
42
|
+
target.options.headed = true;
|
|
43
|
+
target.options['no-exit'] = true;
|
|
44
|
+
delete target.options.watch;
|
|
45
|
+
}
|
|
46
|
+
if (target.options.baseUrl) {
|
|
47
|
+
(0, upsert_baseUrl_1.upsertBaseUrl)(tree, configFilePath, target.options.baseUrl);
|
|
48
|
+
delete target.options.baseUrl;
|
|
49
|
+
}
|
|
50
|
+
if (target.options.devServerTarget) {
|
|
51
|
+
const webServerCommands = {
|
|
52
|
+
default: `npx nx run ${target.options.devServerTarget}`,
|
|
53
|
+
};
|
|
54
|
+
delete target.options.devServerTarget;
|
|
55
|
+
if (target.configurations) {
|
|
56
|
+
for (const configuration in target.configurations) {
|
|
57
|
+
if (target.configurations[configuration]?.devServerTarget) {
|
|
58
|
+
webServerCommands[configuration] = `npx nx run ${target.configurations[configuration].devServerTarget}`;
|
|
59
|
+
delete target.configurations[configuration].devServerTarget;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
(0, add_dev_server_target_to_config_1.addDevServerTargetToConfig)(tree, configFilePath, webServerCommands, target.configurations?.ci?.devServerTarget);
|
|
64
|
+
}
|
|
65
|
+
if (target.options.ignoreTestFiles) {
|
|
66
|
+
(0, add_exclude_spec_pattern_1.addExcludeSpecPattern)(tree, configFilePath, target.options.ignoreTestFiles);
|
|
67
|
+
delete target.options.ignoreTestFiles;
|
|
68
|
+
}
|
|
69
|
+
if (Object.keys(target.options).length === 0) {
|
|
70
|
+
delete target.options;
|
|
71
|
+
}
|
|
72
|
+
if (target.configurations &&
|
|
73
|
+
Object.keys(target.configurations).length !== 0) {
|
|
74
|
+
for (const configuration in target.configurations) {
|
|
75
|
+
if (Object.keys(target.configurations[configuration]).length === 0) {
|
|
76
|
+
delete target.configurations[configuration];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (Object.keys(target.configurations).length === 0) {
|
|
80
|
+
delete target.configurations;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return target;
|
|
85
|
+
}
|
|
86
|
+
exports.default = convertToInferred;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Tree } from '@nx/devkit';
|
|
2
|
+
/**
|
|
3
|
+
* Add or update the webServerCommands and ciWebServerCommand options in the Cypress Config
|
|
4
|
+
* Scenarios Covered:
|
|
5
|
+
* 1. Only devServerTarget Exists
|
|
6
|
+
* 2. devServerTarget and configuration.ci.devServerTarget Exists
|
|
7
|
+
*
|
|
8
|
+
* For each, the following scenarios are covered:
|
|
9
|
+
* a. The command is not listed in the config, so it is added
|
|
10
|
+
* b. Replace the existing webServerCommands with the value passed in
|
|
11
|
+
*/
|
|
12
|
+
export declare function addDevServerTargetToConfig(tree: Tree, configFilePath: string, webServerCommands: Record<string, string>, ciDevServerTarget?: string): void;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.addDevServerTargetToConfig = void 0;
|
|
4
|
+
const tsquery_1 = require("@phenomnomnominal/tsquery");
|
|
5
|
+
/**
|
|
6
|
+
* Add or update the webServerCommands and ciWebServerCommand options in the Cypress Config
|
|
7
|
+
* Scenarios Covered:
|
|
8
|
+
* 1. Only devServerTarget Exists
|
|
9
|
+
* 2. devServerTarget and configuration.ci.devServerTarget Exists
|
|
10
|
+
*
|
|
11
|
+
* For each, the following scenarios are covered:
|
|
12
|
+
* a. The command is not listed in the config, so it is added
|
|
13
|
+
* b. Replace the existing webServerCommands with the value passed in
|
|
14
|
+
*/
|
|
15
|
+
function addDevServerTargetToConfig(tree, configFilePath, webServerCommands, ciDevServerTarget) {
|
|
16
|
+
let configFileContents = tree.read(configFilePath, 'utf-8');
|
|
17
|
+
let ast = tsquery_1.tsquery.ast(configFileContents);
|
|
18
|
+
const NX_E2E_PRESET_OPTIONS_SELECTOR = 'PropertyAssignment:has(Identifier[name=e2e]) CallExpression:has(Identifier[name=nxE2EPreset]) > ObjectLiteralExpression';
|
|
19
|
+
const nxE2ePresetOptionsNodes = (0, tsquery_1.tsquery)(ast, NX_E2E_PRESET_OPTIONS_SELECTOR, {
|
|
20
|
+
visitAllChildren: true,
|
|
21
|
+
});
|
|
22
|
+
if (nxE2ePresetOptionsNodes.length !== 0) {
|
|
23
|
+
let nxE2ePresetOptionsNode = nxE2ePresetOptionsNodes[0];
|
|
24
|
+
const WEB_SERVER_COMMANDS_SELECTOR = 'PropertyAssignment:has(Identifier[name=webServerCommands])';
|
|
25
|
+
const webServerCommandsNodes = (0, tsquery_1.tsquery)(nxE2ePresetOptionsNode, WEB_SERVER_COMMANDS_SELECTOR, { visitAllChildren: true });
|
|
26
|
+
if (webServerCommandsNodes.length !== 0) {
|
|
27
|
+
// Already exists, replace it
|
|
28
|
+
tree.write(configFilePath, `${configFileContents.slice(0, webServerCommandsNodes[0].getStart())}webServerCommands: ${JSON.stringify(webServerCommands)}${configFileContents.slice(webServerCommandsNodes[0].getEnd())}`);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
tree.write(configFilePath, `${configFileContents.slice(0, nxE2ePresetOptionsNode.getStart() + 1)}webServerCommands: ${JSON.stringify(webServerCommands)},${configFileContents.slice(nxE2ePresetOptionsNode.getStart() + 1)}`);
|
|
32
|
+
}
|
|
33
|
+
if (ciDevServerTarget) {
|
|
34
|
+
configFileContents = tree.read(configFilePath, 'utf-8');
|
|
35
|
+
ast = tsquery_1.tsquery.ast(configFileContents);
|
|
36
|
+
nxE2ePresetOptionsNode = (0, tsquery_1.tsquery)(ast, NX_E2E_PRESET_OPTIONS_SELECTOR, {
|
|
37
|
+
visitAllChildren: true,
|
|
38
|
+
})[0];
|
|
39
|
+
const CI_WEB_SERVER_COMMANDS_SELECTOR = 'PropertyAssignment:has(Identifier[name=ciWebServerCommand])';
|
|
40
|
+
const ciWebServerCommandsNodes = (0, tsquery_1.tsquery)(nxE2ePresetOptionsNode, CI_WEB_SERVER_COMMANDS_SELECTOR, { visitAllChildren: true });
|
|
41
|
+
if (ciWebServerCommandsNodes.length !== 0) {
|
|
42
|
+
const ciWebServerCommandNode = ciWebServerCommandsNodes[0].getChildAt(2);
|
|
43
|
+
const ciWebServerCommand = ciWebServerCommandNode
|
|
44
|
+
.getText()
|
|
45
|
+
.replace(/["']/g, '');
|
|
46
|
+
if (!ciWebServerCommand.includes(ciDevServerTarget)) {
|
|
47
|
+
tree.write(configFilePath, `${configFileContents.slice(0, ciWebServerCommandNode.getStart())}"npx nx run ${ciDevServerTarget}"${configFileContents.slice(ciWebServerCommandNode.getEnd())}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
tree.write(configFilePath, `${configFileContents.slice(0, nxE2ePresetOptionsNode.getStart() + 1)}ciWebServerCommand: "npx nx run ${ciDevServerTarget}",${configFileContents.slice(nxE2ePresetOptionsNode.getStart() + 1)}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.addDevServerTargetToConfig = addDevServerTargetToConfig;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.addExcludeSpecPattern = void 0;
|
|
4
|
+
const tsquery_1 = require("@phenomnomnominal/tsquery");
|
|
5
|
+
function addExcludeSpecPattern(tree, configFilePath, excludeSpecPattern) {
|
|
6
|
+
let configFileContents = tree.read(configFilePath, 'utf-8');
|
|
7
|
+
let ast = tsquery_1.tsquery.ast(configFileContents);
|
|
8
|
+
const E2E_CONFIG_SELECTOR = 'PropertyAssignment:has(Identifier[name=e2e]) > ObjectLiteralExpression';
|
|
9
|
+
const e2eConfigNodes = (0, tsquery_1.tsquery)(ast, E2E_CONFIG_SELECTOR, {
|
|
10
|
+
visitAllChildren: true,
|
|
11
|
+
});
|
|
12
|
+
if (e2eConfigNodes.length !== 0) {
|
|
13
|
+
const e2eConfigNode = e2eConfigNodes[0];
|
|
14
|
+
const EXCLUDE_SPEC_PATTERN_SELECTOR = 'PropertyAssignment:has(Identifier[name="excludeSpecPattern"])';
|
|
15
|
+
const excludeSpecPatternNodes = (0, tsquery_1.tsquery)(e2eConfigNode, EXCLUDE_SPEC_PATTERN_SELECTOR, { visitAllChildren: true });
|
|
16
|
+
if (excludeSpecPatternNodes.length !== 0) {
|
|
17
|
+
const excludeSpecPatternNode = excludeSpecPatternNodes[0];
|
|
18
|
+
let updatedExcludePattern = Array.isArray(excludeSpecPattern)
|
|
19
|
+
? excludeSpecPattern
|
|
20
|
+
: [excludeSpecPattern];
|
|
21
|
+
tree.write(configFilePath, `${configFileContents.slice(0, excludeSpecPatternNode.getStart())}excludeSpecPattern: ${JSON.stringify(updatedExcludePattern)}${configFileContents.slice(excludeSpecPatternNode.getEnd())}`);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
tree.write(configFilePath, `${configFileContents.slice(0, e2eConfigNode.getStart() + 1)}excludeSpecPattern: ${JSON.stringify(excludeSpecPattern)},${configFileContents.slice(e2eConfigNode.getStart() + 1)}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.addExcludeSpecPattern = addExcludeSpecPattern;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const targetOptionsToCliMap: {
|
|
2
|
+
headed: string;
|
|
3
|
+
headless: string;
|
|
4
|
+
key: string;
|
|
5
|
+
record: string;
|
|
6
|
+
parallel: string;
|
|
7
|
+
browser: string;
|
|
8
|
+
env: string;
|
|
9
|
+
spec: string;
|
|
10
|
+
ciBuildId: string;
|
|
11
|
+
group: string;
|
|
12
|
+
reporter: string;
|
|
13
|
+
reporterOptions: string;
|
|
14
|
+
tag: string;
|
|
15
|
+
port: string;
|
|
16
|
+
quiet: string;
|
|
17
|
+
runnerUi: string;
|
|
18
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.targetOptionsToCliMap = void 0;
|
|
4
|
+
exports.targetOptionsToCliMap = {
|
|
5
|
+
headed: 'headed',
|
|
6
|
+
headless: 'headless',
|
|
7
|
+
key: 'key',
|
|
8
|
+
record: 'record',
|
|
9
|
+
parallel: 'parallel',
|
|
10
|
+
browser: 'browser',
|
|
11
|
+
env: 'env',
|
|
12
|
+
spec: 'spec',
|
|
13
|
+
ciBuildId: 'ci-build-id',
|
|
14
|
+
group: 'group',
|
|
15
|
+
reporter: 'reporter',
|
|
16
|
+
reporterOptions: 'reporter-options',
|
|
17
|
+
tag: 'tag',
|
|
18
|
+
port: 'port',
|
|
19
|
+
quiet: 'quiet',
|
|
20
|
+
runnerUi: 'runner-ui',
|
|
21
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.upsertBaseUrl = void 0;
|
|
4
|
+
const tsquery_1 = require("@phenomnomnominal/tsquery");
|
|
5
|
+
function upsertBaseUrl(tree, configFilePath, baseUrlValueInProject) {
|
|
6
|
+
const configFileContents = tree.read(configFilePath, 'utf-8');
|
|
7
|
+
const ast = tsquery_1.tsquery.ast(configFileContents);
|
|
8
|
+
const BASE_URL_SELECTOR = 'PropertyAssignment:has(Identifier[name=e2e]) PropertyAssignment:has(Identifier[name="baseUrl"])';
|
|
9
|
+
const baseUrlNodes = (0, tsquery_1.tsquery)(ast, BASE_URL_SELECTOR, {
|
|
10
|
+
visitAllChildren: true,
|
|
11
|
+
});
|
|
12
|
+
if (baseUrlNodes.length !== 0) {
|
|
13
|
+
// The property exists in the config
|
|
14
|
+
const baseUrlValueNode = baseUrlNodes[0].getChildAt(2);
|
|
15
|
+
const baseUrlValue = baseUrlValueNode.getText().replace(/(["'])/, '');
|
|
16
|
+
if (baseUrlValue === baseUrlValueInProject) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
tree.write(configFilePath, `${configFileContents.slice(0, baseUrlValueNode.getStart())}"${baseUrlValueInProject}"${configFileContents.slice(baseUrlValueNode.getEnd())}`);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
const E2E_OBJECT_SELECTOR = 'PropertyAssignment:has(Identifier[name=e2e]) ObjectLiteralExpression';
|
|
23
|
+
const e2eConfigNodes = (0, tsquery_1.tsquery)(ast, E2E_OBJECT_SELECTOR, {
|
|
24
|
+
visitAllChildren: true,
|
|
25
|
+
});
|
|
26
|
+
if (e2eConfigNodes.length !== 0) {
|
|
27
|
+
const e2eConfigNode = e2eConfigNodes[0];
|
|
28
|
+
tree.write(configFilePath, `${configFileContents.slice(0, e2eConfigNode.getEnd() - 1)}baseUrl: "${baseUrlValueInProject}",
|
|
29
|
+
${configFileContents.slice(e2eConfigNode.getEnd() - 1)}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.upsertBaseUrl = upsertBaseUrl;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/schema",
|
|
3
|
+
"$id": "NxCypressConvertToInferred",
|
|
4
|
+
"description": "Convert existing Cypress project(s) using `@nx/cypress:cypress` executor to use `@nx/cypress/plugin`.",
|
|
5
|
+
"title": "Convert Cypress project from executor to plugin",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"project": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "The project to convert from using the `@nx/cypress:cypress` executor to use `@nx/cypress/plugin`.",
|
|
11
|
+
"x-priority": "important"
|
|
12
|
+
},
|
|
13
|
+
"skipFormat": {
|
|
14
|
+
"type": "boolean",
|
|
15
|
+
"description": "Whether to format files at the end of the migration.",
|
|
16
|
+
"default": false
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const devkit_1 = require("@nx/devkit");
|
|
4
|
-
const executor_options_utils_1 = require("@nx/devkit/src/generators/executor-options-utils");
|
|
5
|
-
async function default_1(tree) {
|
|
6
|
-
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
7
|
-
const { cypressTargets, hasComponentTesting } = getCypressTargetNames(tree);
|
|
8
|
-
const hasProductionFileset = !!nxJson.namedInputs?.production;
|
|
9
|
-
if (hasComponentTesting && hasProductionFileset && cypressTargets.size > 0) {
|
|
10
|
-
const productionFileset = new Set(nxJson.namedInputs.production);
|
|
11
|
-
for (const exclusion of [
|
|
12
|
-
'!{projectRoot}/cypress/**/*',
|
|
13
|
-
'!{projectRoot}/**/*.cy.[jt]s?(x)',
|
|
14
|
-
'!{projectRoot}/cypress.config.[jt]s',
|
|
15
|
-
]) {
|
|
16
|
-
productionFileset.add(exclusion);
|
|
17
|
-
}
|
|
18
|
-
nxJson.namedInputs.production = Array.from(productionFileset);
|
|
19
|
-
}
|
|
20
|
-
for (const targetName of cypressTargets) {
|
|
21
|
-
nxJson.targetDefaults ??= {};
|
|
22
|
-
const cypressTargetDefaults = (nxJson.targetDefaults[targetName] ??= {});
|
|
23
|
-
cypressTargetDefaults.inputs ??= [
|
|
24
|
-
'default',
|
|
25
|
-
hasProductionFileset ? '^production' : '^default',
|
|
26
|
-
];
|
|
27
|
-
}
|
|
28
|
-
(0, devkit_1.updateNxJson)(tree, nxJson);
|
|
29
|
-
await (0, devkit_1.formatFiles)(tree);
|
|
30
|
-
}
|
|
31
|
-
exports.default = default_1;
|
|
32
|
-
function getCypressTargetNames(tree) {
|
|
33
|
-
const cypressTargets = new Set();
|
|
34
|
-
let hasComponentTesting = false;
|
|
35
|
-
(0, executor_options_utils_1.forEachExecutorOptions)(tree, '@nrwl/cypress:cypress', (options, __, target) => {
|
|
36
|
-
cypressTargets.add(target);
|
|
37
|
-
if (options.testingType === 'component') {
|
|
38
|
-
hasComponentTesting = true;
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
return { cypressTargets, hasComponentTesting };
|
|
42
|
-
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { Tree } from '@nx/devkit';
|
|
2
|
-
export declare function updateCyMountUsage(tree: Tree): Promise<void>;
|
|
3
|
-
export declare function addMountCommand(tree: Tree, projectRoot: string, framework: string): void;
|
|
4
|
-
export declare function updateCyFile(tree: Tree, filePath: string, framework: 'angular' | 'react' | 'react18'): void;
|
|
5
|
-
export default updateCyMountUsage;
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.updateCyFile = exports.addMountCommand = exports.updateCyMountUsage = void 0;
|
|
4
|
-
const ct_helpers_1 = require("../../utils/ct-helpers");
|
|
5
|
-
const cypress_version_1 = require("../../utils/cypress-version");
|
|
6
|
-
const devkit_1 = require("@nx/devkit");
|
|
7
|
-
const executor_options_utils_1 = require("@nx/devkit/src/generators/executor-options-utils");
|
|
8
|
-
const semver_1 = require("@nx/devkit/src/utils/semver");
|
|
9
|
-
const tsquery_1 = require("@phenomnomnominal/tsquery");
|
|
10
|
-
const semver_2 = require("semver");
|
|
11
|
-
async function updateCyMountUsage(tree) {
|
|
12
|
-
if ((0, cypress_version_1.installedCypressVersion)() < 10) {
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
const projects = (0, devkit_1.getProjects)(tree);
|
|
16
|
-
const graph = await (0, devkit_1.createProjectGraphAsync)();
|
|
17
|
-
(0, executor_options_utils_1.forEachExecutorOptions)(tree, '@nrwl/cypress:cypress', (options, projectName) => {
|
|
18
|
-
if (options.testingType !== 'component' || !options.devServerTarget) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
const parsed = (0, devkit_1.parseTargetString)(options.devServerTarget, graph);
|
|
22
|
-
if (!parsed?.project || !parsed?.target) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
const buildProjectConfig = projects.get(parsed.project);
|
|
26
|
-
const framework = getFramework(tree, parsed.configuration
|
|
27
|
-
? buildProjectConfig.targets[parsed.target].configurations[parsed.configuration]
|
|
28
|
-
: buildProjectConfig.targets[parsed.target]);
|
|
29
|
-
const ctProjectConfig = projects.get(projectName);
|
|
30
|
-
addMountCommand(tree, ctProjectConfig.root, framework);
|
|
31
|
-
(0, devkit_1.visitNotIgnoredFiles)(tree, ctProjectConfig.sourceRoot, (filePath) => {
|
|
32
|
-
if (ct_helpers_1.CY_FILE_MATCHER.test(filePath)) {
|
|
33
|
-
updateCyFile(tree, filePath, framework);
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
await (0, devkit_1.formatFiles)(tree);
|
|
38
|
-
}
|
|
39
|
-
exports.updateCyMountUsage = updateCyMountUsage;
|
|
40
|
-
function addMountCommand(tree, projectRoot, framework) {
|
|
41
|
-
const commandFilePath = (0, devkit_1.joinPathFragments)(projectRoot, 'cypress', 'support', 'commands.ts');
|
|
42
|
-
if (!tree.exists(commandFilePath)) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
const commandFile = tree.read(commandFilePath, 'utf-8');
|
|
46
|
-
const mountCommand = tsquery_1.tsquery.query(commandFile, 'CallExpression:has(StringLiteral[value="mount"]) PropertyAccessExpression:has(Identifier[name="add"])');
|
|
47
|
-
if (mountCommand?.length > 0) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
const existingCommands = tsquery_1.tsquery.query(commandFile, 'InterfaceDeclaration:has(Identifier[name="Chainable"]) > MethodSignature, InterfaceDeclaration:has(Identifier[name="Chainable"]) > PropertySignature');
|
|
51
|
-
const isGlobalDeclaration = tsquery_1.tsquery.query(commandFile, 'ModuleDeclaration > Identifier[name="global"]');
|
|
52
|
-
const updatedInterface = tsquery_1.tsquery.replace(commandFile, 'ModuleDeclaration:has(Identifier[name="Cypress"])', (node) => {
|
|
53
|
-
const newModuleDelcaration = `declare global {
|
|
54
|
-
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
55
|
-
namespace Cypress {
|
|
56
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
57
|
-
interface Chainable<Subject> {
|
|
58
|
-
${existingCommands.map((c) => c.getText()).join('\n')}
|
|
59
|
-
mount: typeof mount;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}`;
|
|
63
|
-
/*
|
|
64
|
-
* this is to prevent the change being applied twice since
|
|
65
|
-
* declare global { 1
|
|
66
|
-
* interface Cypress { 2
|
|
67
|
-
* }
|
|
68
|
-
* }
|
|
69
|
-
* matches twice.
|
|
70
|
-
* i.e. if there is no global declaration, then add it
|
|
71
|
-
* or if the node is the global declaration, then add it,
|
|
72
|
-
* but not to the cypress module declaration inside the global declaration
|
|
73
|
-
*/
|
|
74
|
-
if (isGlobalDeclaration?.length === 0 ||
|
|
75
|
-
node.name.getText() === 'global') {
|
|
76
|
-
return newModuleDelcaration;
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
const updatedCommandFile = `import { mount } from 'cypress/${framework}'\n${updatedInterface}\nCypress.Commands.add('mount', mount);`;
|
|
80
|
-
tree.write(commandFilePath, updatedCommandFile);
|
|
81
|
-
}
|
|
82
|
-
exports.addMountCommand = addMountCommand;
|
|
83
|
-
function getFramework(tree, target) {
|
|
84
|
-
if (target.executor === '@nrwl/angular:webpack-browser' ||
|
|
85
|
-
target.executor === '@angular-devkit/build-angular:browser') {
|
|
86
|
-
return 'angular';
|
|
87
|
-
}
|
|
88
|
-
const pkgJson = (0, devkit_1.readJson)(tree, 'package.json');
|
|
89
|
-
const reactDomVersion = pkgJson?.dependencies?.['react-dom'];
|
|
90
|
-
const hasReact18 = reactDomVersion &&
|
|
91
|
-
(0, semver_2.gte)((0, semver_1.checkAndCleanWithSemver)('react-dom', reactDomVersion), '18.0.0');
|
|
92
|
-
if (hasReact18) {
|
|
93
|
-
return 'react18';
|
|
94
|
-
}
|
|
95
|
-
return 'react';
|
|
96
|
-
}
|
|
97
|
-
function updateCyFile(tree, filePath, framework) {
|
|
98
|
-
if (!tree.exists(filePath)) {
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
const contents = tree.read(filePath, 'utf-8');
|
|
102
|
-
const withCyMount = tsquery_1.tsquery.replace(contents, ':matches(CallExpression>Identifier[name="mount"])', (node) => {
|
|
103
|
-
return `cy.mount`;
|
|
104
|
-
});
|
|
105
|
-
const withUpdatedImports = tsquery_1.tsquery.replace(withCyMount, ':matches(ImportDeclaration, VariableStatement):has(Identifier[name="mount"]):has(StringLiteral[value="cypress/react"], StringLiteral[value="cypress/angular"], StringLiteral[value="cypress/react18"])', (node) => {
|
|
106
|
-
switch (framework) {
|
|
107
|
-
case 'angular':
|
|
108
|
-
return `import { MountConfig } from 'cypress/angular';`;
|
|
109
|
-
case 'react18':
|
|
110
|
-
case 'react':
|
|
111
|
-
return ' '; // have to return non falsy string to remove the node
|
|
112
|
-
default:
|
|
113
|
-
return node.getText().replace('mount', '');
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
tree.write(filePath, withUpdatedImports);
|
|
117
|
-
}
|
|
118
|
-
exports.updateCyFile = updateCyFile;
|
|
119
|
-
exports.default = updateCyMountUsage;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { Tree } from '@nx/devkit';
|
|
2
|
-
export declare function updateToCypress11(tree: Tree): Promise<() => void>;
|
|
3
|
-
export declare function updateMountHookUsage(tree: Tree, filePath: string): void;
|
|
4
|
-
export declare function updateUnmountUsage(tree: Tree, filePath: string): void;
|
|
5
|
-
export declare function updateProviderUsage(tree: Tree, filePath: string): void;
|
|
6
|
-
export default updateToCypress11;
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.updateProviderUsage = exports.updateUnmountUsage = exports.updateMountHookUsage = exports.updateToCypress11 = void 0;
|
|
4
|
-
const ct_helpers_1 = require("../../utils/ct-helpers");
|
|
5
|
-
const devkit_1 = require("@nx/devkit");
|
|
6
|
-
const executor_options_utils_1 = require("@nx/devkit/src/generators/executor-options-utils");
|
|
7
|
-
const tsquery_1 = require("@phenomnomnominal/tsquery");
|
|
8
|
-
const path_1 = require("path");
|
|
9
|
-
const ts = require("typescript");
|
|
10
|
-
const cypress_version_1 = require("../../utils/cypress-version");
|
|
11
|
-
const versions_1 = require("../../utils/versions");
|
|
12
|
-
async function updateToCypress11(tree) {
|
|
13
|
-
const installedVersion = (0, cypress_version_1.installedCypressVersion)();
|
|
14
|
-
if (installedVersion < 10) {
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
const projects = (0, devkit_1.getProjects)(tree);
|
|
18
|
-
(0, executor_options_utils_1.forEachExecutorOptions)(tree, '@nrwl/cypress:cypress', (options, projectName, targetName, configurationName) => {
|
|
19
|
-
if (options.testingType !== 'component' ||
|
|
20
|
-
!(options.cypressConfig && tree.exists(options.cypressConfig))) {
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
const projectConfig = projects.get(projectName);
|
|
24
|
-
const commandsFile = (0, devkit_1.joinPathFragments)(projectConfig.root, 'cypress', 'support', 'commands.ts');
|
|
25
|
-
const framework = getFramework(tree.exists(commandsFile)
|
|
26
|
-
? tree.read(commandsFile, 'utf-8')
|
|
27
|
-
: tree.read(options.cypressConfig, 'utf-8'));
|
|
28
|
-
(0, devkit_1.visitNotIgnoredFiles)(tree, projectConfig.sourceRoot, (filePath) => {
|
|
29
|
-
if (!ct_helpers_1.CY_FILE_MATCHER.test(filePath)) {
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
const frameworkFromFile = getFramework(tree.read(filePath, 'utf-8'));
|
|
33
|
-
if (framework === 'react' || frameworkFromFile === 'react') {
|
|
34
|
-
updateUnmountUsage(tree, filePath);
|
|
35
|
-
updateMountHookUsage(tree, filePath);
|
|
36
|
-
}
|
|
37
|
-
if (framework === 'angular' || frameworkFromFile === 'angular') {
|
|
38
|
-
updateProviderUsage(tree, filePath);
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
const installTask = (0, devkit_1.addDependenciesToPackageJson)(tree, {}, { cypress: versions_1.cypressVersion });
|
|
43
|
-
await (0, devkit_1.formatFiles)(tree);
|
|
44
|
-
return () => {
|
|
45
|
-
installTask();
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
exports.updateToCypress11 = updateToCypress11;
|
|
49
|
-
function updateMountHookUsage(tree, filePath) {
|
|
50
|
-
const originalContents = tree.read(filePath, 'utf-8');
|
|
51
|
-
const commentedMountHook = tsquery_1.tsquery.replace(originalContents, ':matches(ImportDeclaration, VariableStatement):has(Identifier[name="mountHook"]):has(StringLiteral[value="cypress/react"], StringLiteral[value="cypress/react18"])', (node) => {
|
|
52
|
-
return `/** TODO: mountHook is deprecate.
|
|
53
|
-
* Use a wrapper component instead.
|
|
54
|
-
* See post for details: https://www.cypress.io/blog/2022/11/04/upcoming-changes-to-component-testing/#reactmounthook-removed
|
|
55
|
-
* */\n${node.getText()}`;
|
|
56
|
-
});
|
|
57
|
-
tree.write(filePath, commentedMountHook);
|
|
58
|
-
}
|
|
59
|
-
exports.updateMountHookUsage = updateMountHookUsage;
|
|
60
|
-
function updateUnmountUsage(tree, filePath) {
|
|
61
|
-
const reactDomImport = (0, path_1.extname)(filePath).includes('ts')
|
|
62
|
-
? `import ReactDom from 'react-dom'`
|
|
63
|
-
: `const ReactDom = require('react-dom')`;
|
|
64
|
-
const originalContents = tree.read(filePath, 'utf-8');
|
|
65
|
-
const updatedImports = tsquery_1.tsquery.replace(originalContents, ':matches(ImportDeclaration, VariableStatement):has(Identifier[name="unmount"]):has(StringLiteral[value="cypress/react"], StringLiteral[value="cypress/react18"])', (node) => {
|
|
66
|
-
return `${node.getText().replace('unmount', 'getContainerEl')}
|
|
67
|
-
${reactDomImport}`;
|
|
68
|
-
});
|
|
69
|
-
const updatedUnmountApi = tsquery_1.tsquery.replace(updatedImports, 'ExpressionStatement > CallExpression:has(Identifier[name="unmount"])', (node) => {
|
|
70
|
-
if (node.expression.getText() === 'unmount') {
|
|
71
|
-
return `cy.then(() => ReactDom.unmountComponentAtNode(getContainerEl()))`;
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
tree.write(filePath, updatedUnmountApi);
|
|
75
|
-
}
|
|
76
|
-
exports.updateUnmountUsage = updateUnmountUsage;
|
|
77
|
-
function updateProviderUsage(tree, filePath) {
|
|
78
|
-
const originalContents = tree.read(filePath, 'utf-8');
|
|
79
|
-
const isTestBedImported = tsquery_1.tsquery.query(originalContents, ':matches(ImportDeclaration, VariableStatement):has(Identifier[name="TestBed"]):has(StringLiteral[value="@angular/core/testing"])')?.length > 0;
|
|
80
|
-
let updatedProviders = tsquery_1.tsquery.replace(originalContents, 'CallExpression:has(PropertyAccessExpression:has(Identifier[name="mount"]))', (node) => {
|
|
81
|
-
const expressionName = node.expression.getText();
|
|
82
|
-
if (expressionName === 'cy.mount' && node?.arguments?.length > 1) {
|
|
83
|
-
const component = node.arguments[0].getText();
|
|
84
|
-
if (ts.isObjectLiteralExpression(node.arguments[1])) {
|
|
85
|
-
const providers = node.arguments[1]?.properties
|
|
86
|
-
?.find((p) => p.name?.getText() === 'providers')
|
|
87
|
-
?.getText();
|
|
88
|
-
const noProviders = tsquery_1.tsquery.replace(node.getText(), 'PropertyAssignment:has(Identifier[name="providers"])', (n) => {
|
|
89
|
-
// set it to undefined so we don't run into a hanging comma causing invalid syntax
|
|
90
|
-
return 'providers: undefined';
|
|
91
|
-
});
|
|
92
|
-
return `TestBed.overrideComponent(${component}, { add: { ${providers} }});\n${noProviders}`;
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
return `TestBed.overrideComponent(${component}, {add: { providers: ${node.arguments[1].getText()}.providers}});\n${node.getText()}`;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
tree.write(filePath, `${isTestBedImported
|
|
100
|
-
? ''
|
|
101
|
-
: "import {TestBed} from '@angular/core/testing';\n"}${updatedProviders}`);
|
|
102
|
-
}
|
|
103
|
-
exports.updateProviderUsage = updateProviderUsage;
|
|
104
|
-
function getFramework(contents) {
|
|
105
|
-
if (contents.includes('cypress/react') || contents.includes('@nrwl/react')) {
|
|
106
|
-
return 'react';
|
|
107
|
-
}
|
|
108
|
-
if (contents.includes('cypress/angular') ||
|
|
109
|
-
contents.includes('@nrwl/angular')) {
|
|
110
|
-
return 'angular';
|
|
111
|
-
}
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
exports.default = updateToCypress11;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { GeneratorCallback, Tree } from '@nx/devkit';
|
|
2
|
-
export declare function updateToCypress12(tree: Tree): GeneratorCallback;
|
|
3
|
-
export declare function turnOffTestIsolation(tree: Tree, configPath: string): void;
|
|
4
|
-
/**
|
|
5
|
-
* Leave a comment on all apis that have been removed andsuperseded by cy.intercept
|
|
6
|
-
* stating they these API are now removed and need to update.
|
|
7
|
-
* cy.route, cy.server, Cypress.Server.defaults
|
|
8
|
-
**/
|
|
9
|
-
export declare function shouldUseCyIntercept(tree: Tree, filePath: string): void;
|
|
10
|
-
/**
|
|
11
|
-
* Leave a comment on all apis that have been removed and superseded by cy.session
|
|
12
|
-
* stating they these API are now removed and need to update.
|
|
13
|
-
* Cypress.Cookies.defaults & Cypress.Cookies.preserveOnce
|
|
14
|
-
**/
|
|
15
|
-
export declare function shouldUseCySession(tree: Tree, filePath: string): void;
|
|
16
|
-
/**
|
|
17
|
-
* leave a comment about nested cy commands in a cy.should callback
|
|
18
|
-
* */
|
|
19
|
-
export declare function shouldNotUseCyInShouldCB(tree: Tree, filePath: string): void;
|
|
20
|
-
/**
|
|
21
|
-
* leave a comment on all usages of overriding built-ins that are now banned
|
|
22
|
-
* */
|
|
23
|
-
export declare function shouldNotOverrideCommands(tree: Tree, filePath: string): void;
|
|
24
|
-
export default updateToCypress12;
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.shouldNotOverrideCommands = exports.shouldNotUseCyInShouldCB = exports.shouldUseCySession = exports.shouldUseCyIntercept = exports.turnOffTestIsolation = exports.updateToCypress12 = void 0;
|
|
4
|
-
const devkit_1 = require("@nx/devkit");
|
|
5
|
-
const executor_options_utils_1 = require("@nx/devkit/src/generators/executor-options-utils");
|
|
6
|
-
const tsquery_1 = require("@phenomnomnominal/tsquery");
|
|
7
|
-
const typescript_1 = require("typescript");
|
|
8
|
-
const cypress_version_1 = require("../../utils/cypress-version");
|
|
9
|
-
const helpers_1 = require("./helpers");
|
|
10
|
-
const JS_TS_FILE_MATCHER = /\.[jt]sx?$/;
|
|
11
|
-
function updateToCypress12(tree) {
|
|
12
|
-
if ((0, cypress_version_1.installedCypressVersion)() < 11) {
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
const projects = (0, devkit_1.getProjects)(tree);
|
|
16
|
-
(0, executor_options_utils_1.forEachExecutorOptions)(tree, '@nrwl/cypress:cypress', (options, projectName, targetName, configName) => {
|
|
17
|
-
if (!(options.cypressConfig && tree.exists(options.cypressConfig))) {
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
const projectConfig = projects.get(projectName);
|
|
21
|
-
turnOffTestIsolation(tree, options.cypressConfig);
|
|
22
|
-
(0, devkit_1.visitNotIgnoredFiles)(tree, projectConfig.root, (filePath) => {
|
|
23
|
-
if (!JS_TS_FILE_MATCHER.test(filePath)) {
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
shouldUseCyIntercept(tree, filePath);
|
|
27
|
-
shouldUseCySession(tree, filePath);
|
|
28
|
-
shouldNotUseCyInShouldCB(tree, filePath);
|
|
29
|
-
shouldNotOverrideCommands(tree, filePath);
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
console.warn((0, devkit_1.stripIndents) `Cypress 12 has lots of breaking changes that might subtly break your tests.
|
|
33
|
-
This migration marked known issues that need to be manually migrated,
|
|
34
|
-
but there can still be runtime based errors that were not detected.
|
|
35
|
-
Please consult the offical Cypress v12 migration guide for more info on these changes and the next steps.
|
|
36
|
-
https://docs.cypress.io/guides/references/migration-guide
|
|
37
|
-
`);
|
|
38
|
-
(0, devkit_1.updateJson)(tree, 'package.json', (json) => {
|
|
39
|
-
json.devDependencies.cypress = '^12.2.0';
|
|
40
|
-
return json;
|
|
41
|
-
});
|
|
42
|
-
return () => {
|
|
43
|
-
(0, devkit_1.installPackagesTask)(tree);
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
exports.updateToCypress12 = updateToCypress12;
|
|
47
|
-
function turnOffTestIsolation(tree, configPath) {
|
|
48
|
-
const config = tree.read(configPath, 'utf-8');
|
|
49
|
-
const isTestIsolationSet = tsquery_1.tsquery.query(config, 'ExportAssignment ObjectLiteralExpression > PropertyAssignment:has(Identifier[name="testIsolation"])');
|
|
50
|
-
if (isTestIsolationSet.length > 0) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
const testIsolationProperty = `/**
|
|
54
|
-
* TODO(@nrwl/cypress): In Cypress v12,the testIsolation option is turned on by default.
|
|
55
|
-
* This can cause tests to start breaking where not indended.
|
|
56
|
-
* You should consider enabling this once you verify tests do not depend on each other
|
|
57
|
-
* More Info: https://docs.cypress.io/guides/references/migration-guide#Test-Isolation
|
|
58
|
-
**/
|
|
59
|
-
testIsolation: false,`;
|
|
60
|
-
const updated = tsquery_1.tsquery.replace(config, 'ExportAssignment ObjectLiteralExpression > PropertyAssignment:has(Identifier[name="e2e"])', (node) => {
|
|
61
|
-
if ((0, typescript_1.isObjectLiteralExpression)(node.initializer)) {
|
|
62
|
-
const listOfProperties = node.initializer.properties
|
|
63
|
-
.map((j) => j.getText())
|
|
64
|
-
.join(',\n ');
|
|
65
|
-
return `e2e: {
|
|
66
|
-
${listOfProperties},
|
|
67
|
-
${testIsolationProperty}
|
|
68
|
-
}`;
|
|
69
|
-
}
|
|
70
|
-
return `e2e: {
|
|
71
|
-
...${node.initializer.getText()},
|
|
72
|
-
${testIsolationProperty}
|
|
73
|
-
}`;
|
|
74
|
-
});
|
|
75
|
-
tree.write(configPath, updated);
|
|
76
|
-
}
|
|
77
|
-
exports.turnOffTestIsolation = turnOffTestIsolation;
|
|
78
|
-
/**
|
|
79
|
-
* Leave a comment on all apis that have been removed andsuperseded by cy.intercept
|
|
80
|
-
* stating they these API are now removed and need to update.
|
|
81
|
-
* cy.route, cy.server, Cypress.Server.defaults
|
|
82
|
-
**/
|
|
83
|
-
function shouldUseCyIntercept(tree, filePath) {
|
|
84
|
-
const content = tree.read(filePath, 'utf-8');
|
|
85
|
-
const markedRemovedCommands = tsquery_1.tsquery.replace(content, ':matches(PropertyAccessExpression:has(Identifier[name="cy"]):has(Identifier[name="server"], Identifier[name="route"]), PropertyAccessExpression:has(Identifier[name="defaults"]):has(Identifier[name="Cypress"], Identifier[name="Server"]))', (node) => {
|
|
86
|
-
if ((0, helpers_1.isAlreadyCommented)(node)) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
const expression = node.expression.getText().trim();
|
|
90
|
-
// prevent extra chaining i.e. cy.route().as() will return 2 results
|
|
91
|
-
// cy.route and cy.route().as
|
|
92
|
-
// only need the first 1 so skip any extra chaining
|
|
93
|
-
if (expression === 'cy' || expression === 'Cypress.Server') {
|
|
94
|
-
return `// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
|
95
|
-
${node.getText()}`;
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
tree.write(filePath, markedRemovedCommands);
|
|
99
|
-
}
|
|
100
|
-
exports.shouldUseCyIntercept = shouldUseCyIntercept;
|
|
101
|
-
/**
|
|
102
|
-
* Leave a comment on all apis that have been removed and superseded by cy.session
|
|
103
|
-
* stating they these API are now removed and need to update.
|
|
104
|
-
* Cypress.Cookies.defaults & Cypress.Cookies.preserveOnce
|
|
105
|
-
**/
|
|
106
|
-
function shouldUseCySession(tree, filePath) {
|
|
107
|
-
const content = tree.read(filePath, 'utf-8');
|
|
108
|
-
const markedRemovedCommands = tsquery_1.tsquery.replace(content, ':matches(PropertyAccessExpression:has(Identifier[name="defaults"]):has(Identifier[name="Cypress"], Identifier[name="Cookies"]), PropertyAccessExpression:has(Identifier[name="preserveOnce"]):has(Identifier[name="Cypress"], Identifier[name="Cookies"]))', (node) => {
|
|
109
|
-
if ((0, helpers_1.isAlreadyCommented)(node)) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
const expression = node.expression.getText().trim();
|
|
113
|
-
// prevent grabbing other Cypress.<something>.defaults
|
|
114
|
-
if (expression === 'Cypress.Cookies') {
|
|
115
|
-
return `// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
|
116
|
-
${node.getText()}`;
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
tree.write(filePath, markedRemovedCommands);
|
|
120
|
-
}
|
|
121
|
-
exports.shouldUseCySession = shouldUseCySession;
|
|
122
|
-
/**
|
|
123
|
-
* leave a comment about nested cy commands in a cy.should callback
|
|
124
|
-
* */
|
|
125
|
-
function shouldNotUseCyInShouldCB(tree, filePath) {
|
|
126
|
-
const content = tree.read(filePath, 'utf-8');
|
|
127
|
-
const markedNestedCyCommands = tsquery_1.tsquery.replace(content, 'CallExpression > PropertyAccessExpression:has(Identifier[name="cy"]):has(Identifier[name="should"])', (node) => {
|
|
128
|
-
if ((0, helpers_1.isAlreadyCommented)(node) ||
|
|
129
|
-
(node.parent && !(0, typescript_1.isCallExpression)(node.parent))) {
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
const parentExpression = node.parent;
|
|
133
|
-
if (parentExpression?.arguments?.[0] &&
|
|
134
|
-
((0, typescript_1.isArrowFunction)(parentExpression.arguments[0]) ||
|
|
135
|
-
(0, typescript_1.isFunctionExpression)(parentExpression.arguments[0]))) {
|
|
136
|
-
const isUsingNestedCyCommand = tsquery_1.tsquery.query(parentExpression.arguments[0], 'CallExpression > PropertyAccessExpression:has(Identifier[name="cy"])')?.length > 0;
|
|
137
|
-
if (isUsingNestedCyCommand) {
|
|
138
|
-
return `/**
|
|
139
|
-
* TODO(@nrwl/cypress): Nesting Cypress commands in a should assertion now throws.
|
|
140
|
-
* You should use .then() to chain commands instead.
|
|
141
|
-
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
|
|
142
|
-
**/
|
|
143
|
-
${node.getText()}`;
|
|
144
|
-
}
|
|
145
|
-
return node.getText();
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
tree.write(filePath, markedNestedCyCommands);
|
|
149
|
-
}
|
|
150
|
-
exports.shouldNotUseCyInShouldCB = shouldNotUseCyInShouldCB;
|
|
151
|
-
/**
|
|
152
|
-
* leave a comment on all usages of overriding built-ins that are now banned
|
|
153
|
-
* */
|
|
154
|
-
function shouldNotOverrideCommands(tree, filePath) {
|
|
155
|
-
const content = tree.read(filePath, 'utf-8');
|
|
156
|
-
const markedOverrideUsage = tsquery_1.tsquery.replace(content, 'PropertyAccessExpression:has(Identifier[name="overwrite"]):has(Identifier[name="Cypress"])', (node) => {
|
|
157
|
-
if ((0, helpers_1.isAlreadyCommented)(node)) {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
const expression = node.expression.getText().trim();
|
|
161
|
-
// prevent grabbing other Cypress.<something>.defaults
|
|
162
|
-
if (expression === 'Cypress.Commands') {
|
|
163
|
-
// get value.
|
|
164
|
-
const overwriteExpression = node.parent;
|
|
165
|
-
const command = overwriteExpression.arguments?.[0]?.text; // need string without quotes
|
|
166
|
-
if (helpers_1.BANNED_COMMANDS.includes(command)) {
|
|
167
|
-
// overwrite
|
|
168
|
-
return `/**
|
|
169
|
-
* TODO(@nrwl/cypress): This command can no longer be overridden
|
|
170
|
-
* Consider using a different name like 'custom_${command}'
|
|
171
|
-
* More info: https://docs.cypress.io/guides/references/migration-guide#Cypress-Commands-overwrite
|
|
172
|
-
**/
|
|
173
|
-
${node.getText()}`;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
tree.write(filePath, markedOverrideUsage);
|
|
178
|
-
}
|
|
179
|
-
exports.shouldNotOverrideCommands = shouldNotOverrideCommands;
|
|
180
|
-
exports.default = updateToCypress12;
|