@nx/cypress 17.0.3 → 17.0.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/LICENSE +1 -1
- package/README.md +9 -4
- package/generators.json +3 -3
- package/index.d.ts +2 -2
- package/index.js +4 -4
- package/migrations.json +24 -0
- package/package.json +6 -6
- package/plugin.d.ts +1 -0
- package/plugin.js +6 -0
- package/plugins/cypress-preset.d.ts +27 -19
- package/plugins/cypress-preset.js +102 -11
- package/src/executors/cypress/cypress.impl.d.ts +1 -1
- package/src/executors/cypress/cypress.impl.js +6 -152
- package/src/executors/cypress/schema.json +11 -1
- package/src/generators/base-setup/base-setup.d.ts +1 -0
- package/src/generators/base-setup/base-setup.js +26 -3
- package/src/generators/base-setup/files/config-js-cjs/__directory__/support/commands.js__ext__ +35 -0
- package/src/generators/base-setup/files/config-js-cjs/cypress.config.js__ext__ +3 -0
- package/src/generators/base-setup/files/config-js-esm/__directory__/support/commands.js__ext__ +35 -0
- package/src/generators/base-setup/files/config-ts/__directory__/support/commands.ts__ext__ +35 -0
- package/src/generators/base-setup/files/config-ts/cypress.config.ts__ext__ +3 -0
- package/src/generators/component-configuration/component-configuration.d.ts +3 -2
- package/src/generators/component-configuration/component-configuration.js +50 -27
- package/src/generators/component-configuration/files/__directory__/support/component-index.html +4 -1
- package/src/generators/component-configuration/schema.d.ts +7 -0
- package/src/generators/component-configuration/schema.json +1 -1
- package/src/generators/configuration/configuration.d.ts +4 -0
- package/src/generators/configuration/configuration.js +90 -26
- package/src/generators/configuration/schema.json +1 -1
- package/src/generators/cypress-project/cypress-project.js +25 -5
- package/src/generators/cypress-project/schema.d.ts +1 -0
- package/src/generators/cypress-project/schema.json +1 -1
- package/src/generators/init/init.d.ts +2 -0
- package/src/generators/init/init.js +71 -15
- package/src/generators/init/schema.d.ts +4 -2
- package/src/generators/init/schema.json +19 -1
- package/src/generators/migrate-to-cypress-11/schema.json +1 -1
- package/src/migrations/update-18-1-0/update-cypress-version-13-6-6.d.ts +2 -0
- package/src/migrations/update-18-1-0/update-cypress-version-13-6-6.js +12 -0
- package/src/plugins/plugin.d.ts +9 -0
- package/src/plugins/plugin.js +215 -0
- package/src/utils/add-linter.d.ts +1 -0
- package/src/utils/add-linter.js +4 -4
- package/src/utils/config.d.ts +6 -7
- package/src/utils/config.js +39 -12
- package/src/utils/constants.d.ts +4 -0
- package/src/utils/constants.js +7 -0
- package/src/utils/ct-helpers.d.ts +1 -1
- package/src/utils/ct-helpers.js +6 -2
- package/src/utils/start-dev-server.d.ts +9 -0
- package/src/utils/start-dev-server.js +157 -0
- package/src/utils/versions.d.ts +3 -3
- package/src/utils/versions.js +3 -3
- /package/src/generators/base-setup/files/{__directory__ → common/__directory__}/fixtures/example.json +0 -0
- /package/src/generators/base-setup/files/{__directory__ → common/__directory__}/support/commands.ts__ext__ +0 -0
- /package/src/generators/base-setup/files/{__directory__ → common/__directory__}/tsconfig.json__ext__ +0 -0
- /package/src/generators/base-setup/files/{cypress.config.ts__ext__ → config-js-esm/cypress.config.js__ext__} +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.cypressInitGenerator = void 0;
|
|
3
|
+
exports.cypressInitGeneratorInternal = exports.cypressInitGenerator = exports.addPlugin = void 0;
|
|
4
4
|
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const update_package_scripts_1 = require("@nx/devkit/src/utils/update-package-scripts");
|
|
6
|
+
const plugin_1 = require("../../plugins/plugin");
|
|
5
7
|
const versions_1 = require("../../utils/versions");
|
|
6
|
-
const js_1 = require("@nx/js");
|
|
7
8
|
function setupE2ETargetDefaults(tree) {
|
|
8
9
|
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
9
10
|
if (!nxJson.namedInputs) {
|
|
@@ -13,31 +14,86 @@ function setupE2ETargetDefaults(tree) {
|
|
|
13
14
|
nxJson.targetDefaults ??= {};
|
|
14
15
|
const productionFileSet = !!nxJson.namedInputs?.production;
|
|
15
16
|
nxJson.targetDefaults.e2e ??= {};
|
|
17
|
+
nxJson.targetDefaults.e2e.cache ??= true;
|
|
16
18
|
nxJson.targetDefaults.e2e.inputs ??= [
|
|
17
19
|
'default',
|
|
18
20
|
productionFileSet ? '^production' : '^default',
|
|
19
21
|
];
|
|
20
22
|
(0, devkit_1.updateNxJson)(tree, nxJson);
|
|
21
23
|
}
|
|
22
|
-
function updateDependencies(tree) {
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
function updateDependencies(tree, options) {
|
|
25
|
+
const tasks = [];
|
|
26
|
+
tasks.push((0, devkit_1.removeDependenciesFromPackageJson)(tree, ['@nx/cypress'], []));
|
|
27
|
+
tasks.push((0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
|
|
25
28
|
['@nx/cypress']: versions_1.nxVersion,
|
|
26
29
|
cypress: versions_1.cypressVersion,
|
|
27
|
-
|
|
30
|
+
}, undefined, options.keepExistingVersions));
|
|
31
|
+
return (0, devkit_1.runTasksInSerial)(...tasks);
|
|
32
|
+
}
|
|
33
|
+
function addPlugin(tree) {
|
|
34
|
+
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
35
|
+
nxJson.plugins ??= [];
|
|
36
|
+
for (const plugin of nxJson.plugins) {
|
|
37
|
+
if (typeof plugin === 'string'
|
|
38
|
+
? plugin === '@nx/cypress/plugin'
|
|
39
|
+
: plugin.plugin === '@nx/cypress/plugin') {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
nxJson.plugins.push({
|
|
44
|
+
plugin: '@nx/cypress/plugin',
|
|
45
|
+
options: {
|
|
46
|
+
targetName: 'e2e',
|
|
47
|
+
componentTestingTargetName: 'component-test',
|
|
48
|
+
ciTargetName: 'e2e-ci',
|
|
49
|
+
openTargetName: 'open-cypress',
|
|
50
|
+
},
|
|
28
51
|
});
|
|
52
|
+
(0, devkit_1.updateNxJson)(tree, nxJson);
|
|
53
|
+
}
|
|
54
|
+
exports.addPlugin = addPlugin;
|
|
55
|
+
function updateProductionFileset(tree) {
|
|
56
|
+
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
57
|
+
const productionFileset = nxJson.namedInputs?.production;
|
|
58
|
+
if (productionFileset) {
|
|
59
|
+
nxJson.namedInputs.production = Array.from(new Set([
|
|
60
|
+
...productionFileset,
|
|
61
|
+
'!{projectRoot}/cypress/**/*',
|
|
62
|
+
'!{projectRoot}/**/*.cy.[jt]s?(x)',
|
|
63
|
+
'!{projectRoot}/cypress.config.[jt]s',
|
|
64
|
+
]));
|
|
65
|
+
}
|
|
66
|
+
(0, devkit_1.updateNxJson)(tree, nxJson);
|
|
29
67
|
}
|
|
30
68
|
async function cypressInitGenerator(tree, options) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
69
|
+
return cypressInitGeneratorInternal(tree, { addPlugin: false, ...options });
|
|
70
|
+
}
|
|
71
|
+
exports.cypressInitGenerator = cypressInitGenerator;
|
|
72
|
+
async function cypressInitGeneratorInternal(tree, options) {
|
|
73
|
+
updateProductionFileset(tree);
|
|
74
|
+
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
75
|
+
options.addPlugin ??=
|
|
76
|
+
process.env.NX_ADD_PLUGINS !== 'false' &&
|
|
77
|
+
nxJson.useInferencePlugins !== false;
|
|
78
|
+
if (options.addPlugin) {
|
|
79
|
+
addPlugin(tree);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
setupE2ETargetDefaults(tree);
|
|
83
|
+
}
|
|
84
|
+
let installTask = () => { };
|
|
37
85
|
if (!options.skipPackageJson) {
|
|
38
|
-
|
|
86
|
+
installTask = updateDependencies(tree, options);
|
|
39
87
|
}
|
|
40
|
-
|
|
88
|
+
if (options.updatePackageScripts) {
|
|
89
|
+
global.NX_CYPRESS_INIT_GENERATOR_RUNNING = true;
|
|
90
|
+
await (0, update_package_scripts_1.updatePackageScripts)(tree, plugin_1.createNodes);
|
|
91
|
+
global.NX_CYPRESS_INIT_GENERATOR_RUNNING = false;
|
|
92
|
+
}
|
|
93
|
+
if (!options.skipFormat) {
|
|
94
|
+
await (0, devkit_1.formatFiles)(tree);
|
|
95
|
+
}
|
|
96
|
+
return installTask;
|
|
41
97
|
}
|
|
42
|
-
exports.
|
|
98
|
+
exports.cypressInitGeneratorInternal = cypressInitGeneratorInternal;
|
|
43
99
|
exports.default = cypressInitGenerator;
|
|
@@ -1,16 +1,34 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "
|
|
2
|
+
"$schema": "https://json-schema.org/schema",
|
|
3
3
|
"$id": "NxCypressInit",
|
|
4
4
|
"cli": "nx",
|
|
5
5
|
"title": "Add Cypress Configuration to the workspace",
|
|
6
6
|
"description": "Add Cypress Configuration to the workspace.",
|
|
7
7
|
"type": "object",
|
|
8
8
|
"properties": {
|
|
9
|
+
"skipFormat": {
|
|
10
|
+
"description": "Skip formatting files.",
|
|
11
|
+
"type": "boolean",
|
|
12
|
+
"default": false,
|
|
13
|
+
"x-priority": "internal"
|
|
14
|
+
},
|
|
9
15
|
"skipPackageJson": {
|
|
10
16
|
"type": "boolean",
|
|
11
17
|
"default": false,
|
|
12
18
|
"description": "Do not add dependencies to `package.json`.",
|
|
13
19
|
"x-priority": "internal"
|
|
20
|
+
},
|
|
21
|
+
"keepExistingVersions": {
|
|
22
|
+
"type": "boolean",
|
|
23
|
+
"x-priority": "internal",
|
|
24
|
+
"description": "Keep existing dependencies versions",
|
|
25
|
+
"default": false
|
|
26
|
+
},
|
|
27
|
+
"updatePackageScripts": {
|
|
28
|
+
"type": "boolean",
|
|
29
|
+
"x-priority": "internal",
|
|
30
|
+
"description": "Update `package.json` scripts with inferred targets",
|
|
31
|
+
"default": false
|
|
14
32
|
}
|
|
15
33
|
}
|
|
16
34
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const devkit_1 = require("@nx/devkit");
|
|
4
|
+
const cypress_version_1 = require("../../utils/cypress-version");
|
|
5
|
+
async function default_1(tree) {
|
|
6
|
+
if ((0, cypress_version_1.installedCypressVersion)() < 13) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
(0, devkit_1.addDependenciesToPackageJson)(tree, {}, { cypress: '^13.6.6' });
|
|
10
|
+
await (0, devkit_1.formatFiles)(tree);
|
|
11
|
+
}
|
|
12
|
+
exports.default = default_1;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CreateDependencies, CreateNodes } from '@nx/devkit';
|
|
2
|
+
export interface CypressPluginOptions {
|
|
3
|
+
ciTargetName?: string;
|
|
4
|
+
targetName?: string;
|
|
5
|
+
openTargetName?: string;
|
|
6
|
+
componentTestingTargetName?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const createDependencies: CreateDependencies;
|
|
9
|
+
export declare const createNodes: CreateNodes<CypressPluginOptions>;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createNodes = exports.createDependencies = void 0;
|
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const js_1 = require("@nx/js");
|
|
7
|
+
const get_named_inputs_1 = require("@nx/devkit/src/utils/get-named-inputs");
|
|
8
|
+
const fs_1 = require("fs");
|
|
9
|
+
const workspace_context_1 = require("nx/src/utils/workspace-context");
|
|
10
|
+
const calculate_hash_for_create_nodes_1 = require("@nx/devkit/src/utils/calculate-hash-for-create-nodes");
|
|
11
|
+
const cache_directory_1 = require("nx/src/utils/cache-directory");
|
|
12
|
+
const constants_1 = require("../utils/constants");
|
|
13
|
+
const config_utils_1 = require("@nx/devkit/src/utils/config-utils");
|
|
14
|
+
const cachePath = (0, path_1.join)(cache_directory_1.projectGraphCacheDirectory, 'cypress.hash');
|
|
15
|
+
const targetsCache = (0, fs_1.existsSync)(cachePath) ? readTargetsCache() : {};
|
|
16
|
+
const calculatedTargets = {};
|
|
17
|
+
function readTargetsCache() {
|
|
18
|
+
return (0, devkit_1.readJsonFile)(cachePath);
|
|
19
|
+
}
|
|
20
|
+
function writeTargetsToCache(targets) {
|
|
21
|
+
(0, devkit_1.writeJsonFile)(cachePath, targets);
|
|
22
|
+
}
|
|
23
|
+
const createDependencies = () => {
|
|
24
|
+
writeTargetsToCache(calculatedTargets);
|
|
25
|
+
return [];
|
|
26
|
+
};
|
|
27
|
+
exports.createDependencies = createDependencies;
|
|
28
|
+
exports.createNodes = [
|
|
29
|
+
'**/cypress.config.{js,ts,mjs,cjs}',
|
|
30
|
+
async (configFilePath, options, context) => {
|
|
31
|
+
options = normalizeOptions(options);
|
|
32
|
+
const projectRoot = (0, path_1.dirname)(configFilePath);
|
|
33
|
+
// Do not create a project if package.json and project.json isn't there.
|
|
34
|
+
const siblingFiles = (0, fs_1.readdirSync)((0, path_1.join)(context.workspaceRoot, projectRoot));
|
|
35
|
+
if (!siblingFiles.includes('package.json') &&
|
|
36
|
+
!siblingFiles.includes('project.json')) {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
const hash = (0, calculate_hash_for_create_nodes_1.calculateHashForCreateNodes)(projectRoot, options, context, [
|
|
40
|
+
(0, js_1.getLockFileName)((0, devkit_1.detectPackageManager)(context.workspaceRoot)),
|
|
41
|
+
]);
|
|
42
|
+
const { targets, targetGroups } = targetsCache[hash]
|
|
43
|
+
? targetsCache[hash]
|
|
44
|
+
: await buildCypressTargets(configFilePath, projectRoot, options, context);
|
|
45
|
+
calculatedTargets[hash] = { targets, targetGroups };
|
|
46
|
+
const project = {
|
|
47
|
+
projectType: 'application',
|
|
48
|
+
targets,
|
|
49
|
+
metadata: {
|
|
50
|
+
technologies: ['cypress'],
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
if (targetGroups) {
|
|
54
|
+
project.metadata.targetGroups = targetGroups;
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
projects: {
|
|
58
|
+
[projectRoot]: project,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
function getOutputs(projectRoot, cypressConfig, testingType) {
|
|
64
|
+
function getOutput(path) {
|
|
65
|
+
if (path.startsWith('..')) {
|
|
66
|
+
return (0, devkit_1.joinPathFragments)('{workspaceRoot}', projectRoot, path);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
return (0, devkit_1.joinPathFragments)('{projectRoot}', path);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const { screenshotsFolder, videosFolder, e2e, component } = cypressConfig;
|
|
73
|
+
const outputs = [];
|
|
74
|
+
if (videosFolder) {
|
|
75
|
+
outputs.push(getOutput(videosFolder));
|
|
76
|
+
}
|
|
77
|
+
if (screenshotsFolder) {
|
|
78
|
+
outputs.push(getOutput(screenshotsFolder));
|
|
79
|
+
}
|
|
80
|
+
switch (testingType) {
|
|
81
|
+
case 'e2e': {
|
|
82
|
+
if (e2e.videosFolder) {
|
|
83
|
+
outputs.push(getOutput(e2e.videosFolder));
|
|
84
|
+
}
|
|
85
|
+
if (e2e.screenshotsFolder) {
|
|
86
|
+
outputs.push(getOutput(e2e.screenshotsFolder));
|
|
87
|
+
}
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
case 'component': {
|
|
91
|
+
if (component.videosFolder) {
|
|
92
|
+
outputs.push(getOutput(component.videosFolder));
|
|
93
|
+
}
|
|
94
|
+
if (component.screenshotsFolder) {
|
|
95
|
+
outputs.push(getOutput(component.screenshotsFolder));
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return outputs;
|
|
101
|
+
}
|
|
102
|
+
async function buildCypressTargets(configFilePath, projectRoot, options, context) {
|
|
103
|
+
const cypressConfig = await (0, config_utils_1.loadConfigFile)((0, path_1.join)(context.workspaceRoot, configFilePath));
|
|
104
|
+
const pluginPresetOptions = {
|
|
105
|
+
...cypressConfig.e2e?.[constants_1.NX_PLUGIN_OPTIONS],
|
|
106
|
+
...cypressConfig.env,
|
|
107
|
+
...cypressConfig.e2e?.env,
|
|
108
|
+
};
|
|
109
|
+
const webServerCommands = pluginPresetOptions?.webServerCommands;
|
|
110
|
+
const namedInputs = (0, get_named_inputs_1.getNamedInputs)(projectRoot, context);
|
|
111
|
+
const targets = {};
|
|
112
|
+
let targetGroups;
|
|
113
|
+
if ('e2e' in cypressConfig) {
|
|
114
|
+
targets[options.targetName] = {
|
|
115
|
+
command: `cypress run`,
|
|
116
|
+
options: { cwd: projectRoot },
|
|
117
|
+
cache: true,
|
|
118
|
+
inputs: getInputs(namedInputs),
|
|
119
|
+
outputs: getOutputs(projectRoot, cypressConfig, 'e2e'),
|
|
120
|
+
};
|
|
121
|
+
if (webServerCommands?.default) {
|
|
122
|
+
delete webServerCommands.default;
|
|
123
|
+
}
|
|
124
|
+
if (Object.keys(webServerCommands ?? {}).length > 0) {
|
|
125
|
+
targets[options.targetName].configurations ??= {};
|
|
126
|
+
for (const [configuration, webServerCommand] of Object.entries(webServerCommands ?? {})) {
|
|
127
|
+
targets[options.targetName].configurations[configuration] = {
|
|
128
|
+
command: `cypress run --env webServerCommand="${webServerCommand}"`,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const ciWebServerCommand = pluginPresetOptions?.ciWebServerCommand;
|
|
133
|
+
if (ciWebServerCommand) {
|
|
134
|
+
const specPatterns = Array.isArray(cypressConfig.e2e.specPattern)
|
|
135
|
+
? cypressConfig.e2e.specPattern.map((p) => (0, path_1.join)(projectRoot, p))
|
|
136
|
+
: [(0, path_1.join)(projectRoot, cypressConfig.e2e.specPattern)];
|
|
137
|
+
const excludeSpecPatterns = !cypressConfig.e2e
|
|
138
|
+
.excludeSpecPattern
|
|
139
|
+
? cypressConfig.e2e.excludeSpecPattern
|
|
140
|
+
: Array.isArray(cypressConfig.e2e.excludeSpecPattern)
|
|
141
|
+
? cypressConfig.e2e.excludeSpecPattern.map((p) => (0, path_1.join)(projectRoot, p))
|
|
142
|
+
: [(0, path_1.join)(projectRoot, cypressConfig.e2e.excludeSpecPattern)];
|
|
143
|
+
const specFiles = (0, workspace_context_1.globWithWorkspaceContext)(context.workspaceRoot, specPatterns, excludeSpecPatterns);
|
|
144
|
+
const dependsOn = [];
|
|
145
|
+
const outputs = getOutputs(projectRoot, cypressConfig, 'e2e');
|
|
146
|
+
const inputs = getInputs(namedInputs);
|
|
147
|
+
const groupName = 'E2E (CI)';
|
|
148
|
+
targetGroups = { [groupName]: [] };
|
|
149
|
+
const ciTargetGroup = targetGroups[groupName];
|
|
150
|
+
for (const file of specFiles) {
|
|
151
|
+
const relativeSpecFilePath = (0, devkit_1.normalizePath)((0, path_1.relative)(projectRoot, file));
|
|
152
|
+
const targetName = options.ciTargetName + '--' + relativeSpecFilePath;
|
|
153
|
+
ciTargetGroup.push(targetName);
|
|
154
|
+
targets[targetName] = {
|
|
155
|
+
outputs,
|
|
156
|
+
inputs,
|
|
157
|
+
cache: true,
|
|
158
|
+
command: `cypress run --env webServerCommand="${ciWebServerCommand}" --spec ${relativeSpecFilePath}`,
|
|
159
|
+
options: {
|
|
160
|
+
cwd: projectRoot,
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
dependsOn.push({
|
|
164
|
+
target: targetName,
|
|
165
|
+
projects: 'self',
|
|
166
|
+
params: 'forward',
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
targets[options.ciTargetName] = {
|
|
170
|
+
executor: 'nx:noop',
|
|
171
|
+
cache: true,
|
|
172
|
+
inputs,
|
|
173
|
+
outputs,
|
|
174
|
+
dependsOn,
|
|
175
|
+
};
|
|
176
|
+
ciTargetGroup.push(options.ciTargetName);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
targetGroups = null;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if ('component' in cypressConfig) {
|
|
183
|
+
// This will not override the e2e target if it is the same
|
|
184
|
+
targets[options.componentTestingTargetName] ??= {
|
|
185
|
+
command: `cypress run --component`,
|
|
186
|
+
options: { cwd: projectRoot },
|
|
187
|
+
cache: true,
|
|
188
|
+
inputs: getInputs(namedInputs),
|
|
189
|
+
outputs: getOutputs(projectRoot, cypressConfig, 'component'),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
targets[options.openTargetName] = {
|
|
193
|
+
command: `cypress open`,
|
|
194
|
+
options: { cwd: projectRoot },
|
|
195
|
+
};
|
|
196
|
+
return { targets, targetGroups };
|
|
197
|
+
}
|
|
198
|
+
function normalizeOptions(options) {
|
|
199
|
+
options ??= {};
|
|
200
|
+
options.targetName ??= 'e2e';
|
|
201
|
+
options.openTargetName ??= 'open-cypress';
|
|
202
|
+
options.componentTestingTargetName ??= 'component-test';
|
|
203
|
+
options.ciTargetName ??= 'e2e-ci';
|
|
204
|
+
return options;
|
|
205
|
+
}
|
|
206
|
+
function getInputs(namedInputs) {
|
|
207
|
+
return [
|
|
208
|
+
...('production' in namedInputs
|
|
209
|
+
? ['default', '^production']
|
|
210
|
+
: ['default', '^default']),
|
|
211
|
+
{
|
|
212
|
+
externalDependencies: ['cypress'],
|
|
213
|
+
},
|
|
214
|
+
];
|
|
215
|
+
}
|
|
@@ -17,5 +17,6 @@ export interface CyLinterOptions {
|
|
|
17
17
|
* This is useful when adding linting to a brand new project vs an existing one
|
|
18
18
|
**/
|
|
19
19
|
overwriteExisting?: boolean;
|
|
20
|
+
addPlugin?: boolean;
|
|
20
21
|
}
|
|
21
22
|
export declare function addLinterToCyProject(tree: Tree, options: CyLinterOptions): Promise<() => void>;
|
package/src/utils/add-linter.js
CHANGED
|
@@ -20,17 +20,16 @@ async function addLinterToCyProject(tree, options) {
|
|
|
20
20
|
linter: options.linter,
|
|
21
21
|
skipFormat: true,
|
|
22
22
|
tsConfigPaths: [(0, devkit_1.joinPathFragments)(projectConfig.root, 'tsconfig.json')],
|
|
23
|
-
eslintFilePatterns: [
|
|
24
|
-
`${projectConfig.root}/**/*.${options.js ? 'js' : '{js,ts}'}`,
|
|
25
|
-
],
|
|
26
23
|
setParserOptionsProject: options.setParserOptionsProject,
|
|
27
24
|
skipPackageJson: options.skipPackageJson,
|
|
28
25
|
rootProject: options.rootProject,
|
|
26
|
+
addPlugin: options.addPlugin,
|
|
29
27
|
}));
|
|
30
28
|
}
|
|
31
29
|
if (!options.linter || options.linter !== eslint_1.Linter.EsLint) {
|
|
32
30
|
return (0, devkit_1.runTasksInSerial)(...tasks);
|
|
33
31
|
}
|
|
32
|
+
options.overwriteExisting = options.overwriteExisting || !eslintFile;
|
|
34
33
|
tasks.push(!options.skipPackageJson
|
|
35
34
|
? (0, devkit_1.addDependenciesToPackageJson)(tree, {}, { 'eslint-plugin-cypress': versions_1.eslintPluginCypressVersion })
|
|
36
35
|
: () => { });
|
|
@@ -38,6 +37,7 @@ async function addLinterToCyProject(tree, options) {
|
|
|
38
37
|
const overrides = [];
|
|
39
38
|
if (options.rootProject) {
|
|
40
39
|
(0, eslint_file_1.addPluginsToLintConfig)(tree, projectConfig.root, '@nx');
|
|
40
|
+
overrides.push(global_eslint_config_1.typeScriptOverride);
|
|
41
41
|
overrides.push(global_eslint_config_1.javaScriptOverride);
|
|
42
42
|
}
|
|
43
43
|
(0, eslint_file_1.addExtendsToLintConfig)(tree, projectConfig.root, 'plugin:cypress/recommended');
|
|
@@ -55,7 +55,7 @@ async function addLinterToCyProject(tree, options) {
|
|
|
55
55
|
};
|
|
56
56
|
const addCy6Override = cyVersion && cyVersion < 7;
|
|
57
57
|
if (options.overwriteExisting) {
|
|
58
|
-
overrides.
|
|
58
|
+
overrides.unshift({
|
|
59
59
|
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
|
60
60
|
parserOptions: !options.setParserOptionsProject
|
|
61
61
|
? undefined
|
package/src/utils/config.d.ts
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { type Tree } from '@nx/devkit';
|
|
2
|
+
import type { NxComponentTestingOptions, NxCypressE2EPresetOptions } from '../../plugins/cypress-preset';
|
|
3
|
+
export declare const CYPRESS_CONFIG_FILE_NAME_PATTERN = "cypress.config.{js,ts,mjs,cjs}";
|
|
4
|
+
export declare function addDefaultE2EConfig(cyConfigContents: string, options: NxCypressE2EPresetOptions, baseUrl: string): Promise<string>;
|
|
5
5
|
/**
|
|
6
6
|
* Adds the nxComponentTestingPreset to the cypress config file
|
|
7
7
|
* Make sure after calling this the correct import statement is addeda
|
|
8
8
|
* to bring in the nxComponentTestingPreset function
|
|
9
9
|
**/
|
|
10
|
-
export declare function addDefaultCTConfig(cyConfigContents: string, options?:
|
|
11
|
-
bundler?: string;
|
|
12
|
-
}): Promise<string>;
|
|
10
|
+
export declare function addDefaultCTConfig(cyConfigContents: string, options?: NxComponentTestingOptions): Promise<string>;
|
|
13
11
|
/**
|
|
14
12
|
* Adds the mount command for Cypress
|
|
15
13
|
* Make sure after calling this the correct import statement is added
|
|
16
14
|
* to bring in the correct mount from cypress.
|
|
17
15
|
**/
|
|
18
16
|
export declare function addMountDefinition(cmpCommandFileContents: string): Promise<string>;
|
|
17
|
+
export declare function getProjectCypressConfigPath(tree: Tree, projectRoot: string): string;
|
package/src/utils/config.js
CHANGED
|
@@ -1,30 +1,40 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.addMountDefinition = exports.addDefaultCTConfig = exports.addDefaultE2EConfig = void 0;
|
|
4
|
-
const
|
|
5
|
-
|
|
3
|
+
exports.getProjectCypressConfigPath = exports.addMountDefinition = exports.addDefaultCTConfig = exports.addDefaultE2EConfig = exports.CYPRESS_CONFIG_FILE_NAME_PATTERN = void 0;
|
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
exports.CYPRESS_CONFIG_FILE_NAME_PATTERN = 'cypress.config.{js,ts,mjs,cjs}';
|
|
6
|
+
const TS_QUERY_COMMON_JS_EXPORT_SELECTOR = 'BinaryExpression:has(Identifier[name="module"]):has(Identifier[name="exports"])';
|
|
7
|
+
const TS_QUERY_EXPORT_CONFIG_PREFIX = `:matches(ExportAssignment, ${TS_QUERY_COMMON_JS_EXPORT_SELECTOR}) `;
|
|
8
|
+
async function addDefaultE2EConfig(cyConfigContents, options, baseUrl) {
|
|
6
9
|
if (!cyConfigContents) {
|
|
7
10
|
throw new Error('The passed in cypress config file is empty!');
|
|
8
11
|
}
|
|
9
12
|
const { tsquery } = await Promise.resolve().then(() => require('@phenomnomnominal/tsquery'));
|
|
13
|
+
const isCommonJS = tsquery.query(cyConfigContents, TS_QUERY_COMMON_JS_EXPORT_SELECTOR).length >
|
|
14
|
+
0;
|
|
10
15
|
const testingTypeConfig = tsquery.query(cyConfigContents, `${TS_QUERY_EXPORT_CONFIG_PREFIX} PropertyAssignment:has(Identifier[name="e2e"])`);
|
|
11
16
|
let updatedConfigContents = cyConfigContents;
|
|
12
17
|
if (testingTypeConfig.length === 0) {
|
|
13
|
-
const configValue = options
|
|
14
|
-
? `nxE2EPreset(__filename, { cypressDir: '${options.directory}', bundler: 'vite' })`
|
|
15
|
-
: `nxE2EPreset(__filename, { cypressDir: '${options.directory}' })`;
|
|
18
|
+
const configValue = `nxE2EPreset(__filename, ${JSON.stringify(options)})`;
|
|
16
19
|
updatedConfigContents = tsquery.replace(cyConfigContents, `${TS_QUERY_EXPORT_CONFIG_PREFIX} ObjectLiteralExpression:first-child`, (node) => {
|
|
20
|
+
let baseUrlContents = baseUrl ? `,\nbaseUrl: '${baseUrl}'` : '';
|
|
17
21
|
if (node.properties.length > 0) {
|
|
18
22
|
return `{
|
|
19
23
|
${node.properties.map((p) => p.getText()).join(',\n')},
|
|
20
|
-
e2e:
|
|
24
|
+
e2e: { ...${configValue}${baseUrlContents} }
|
|
21
25
|
}`;
|
|
22
26
|
}
|
|
23
27
|
return `{
|
|
24
|
-
e2e:
|
|
28
|
+
e2e: { ...${configValue}${baseUrlContents} }
|
|
25
29
|
}`;
|
|
26
30
|
});
|
|
27
|
-
|
|
31
|
+
return isCommonJS
|
|
32
|
+
? `const { nxE2EPreset } = require('@nx/cypress/plugins/cypress-preset');
|
|
33
|
+
|
|
34
|
+
${updatedConfigContents}`
|
|
35
|
+
: `import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset';
|
|
36
|
+
|
|
37
|
+
${updatedConfigContents}`;
|
|
28
38
|
}
|
|
29
39
|
return updatedConfigContents;
|
|
30
40
|
}
|
|
@@ -42,9 +52,16 @@ async function addDefaultCTConfig(cyConfigContents, options = {}) {
|
|
|
42
52
|
const testingTypeConfig = tsquery.query(cyConfigContents, `${TS_QUERY_EXPORT_CONFIG_PREFIX} PropertyAssignment:has(Identifier[name="component"])`);
|
|
43
53
|
let updatedConfigContents = cyConfigContents;
|
|
44
54
|
if (testingTypeConfig.length === 0) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
55
|
+
let configValue = 'nxComponentTestingPreset(__filename)';
|
|
56
|
+
if (options) {
|
|
57
|
+
if (options.bundler !== 'vite') {
|
|
58
|
+
// vite is the default bundler, so we don't need to set it
|
|
59
|
+
delete options.bundler;
|
|
60
|
+
}
|
|
61
|
+
if (Object.keys(options).length) {
|
|
62
|
+
configValue = `nxComponentTestingPreset(__filename, ${JSON.stringify(options)})`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
48
65
|
updatedConfigContents = tsquery.replace(cyConfigContents, `${TS_QUERY_EXPORT_CONFIG_PREFIX} ObjectLiteralExpression:first-child`, (node) => {
|
|
49
66
|
if (node.properties.length > 0) {
|
|
50
67
|
return `{
|
|
@@ -86,3 +103,13 @@ async function addMountDefinition(cmpCommandFileContents) {
|
|
|
86
103
|
return `${updatedInterface}\n${mountCommand}`;
|
|
87
104
|
}
|
|
88
105
|
exports.addMountDefinition = addMountDefinition;
|
|
106
|
+
function getProjectCypressConfigPath(tree, projectRoot) {
|
|
107
|
+
const cypressConfigPaths = (0, devkit_1.glob)(tree, [
|
|
108
|
+
(0, devkit_1.joinPathFragments)(projectRoot, exports.CYPRESS_CONFIG_FILE_NAME_PATTERN),
|
|
109
|
+
]);
|
|
110
|
+
if (cypressConfigPaths.length === 0) {
|
|
111
|
+
throw new Error(`Could not find a cypress config file in ${projectRoot}.`);
|
|
112
|
+
}
|
|
113
|
+
return cypressConfigPaths[0];
|
|
114
|
+
}
|
|
115
|
+
exports.getProjectCypressConfigPath = getProjectCypressConfigPath;
|
|
@@ -11,6 +11,6 @@ export declare function getTempTailwindPath(context: ExecutorContext): string;
|
|
|
11
11
|
* Checks if the childProjectName is a descendent of the parentProjectName
|
|
12
12
|
* in the project graph
|
|
13
13
|
**/
|
|
14
|
-
export declare function isCtProjectUsingBuildProject(graph: ProjectGraph, parentProjectName: string, childProjectName: string): boolean;
|
|
14
|
+
export declare function isCtProjectUsingBuildProject(graph: ProjectGraph, parentProjectName: string, childProjectName: string, seen?: Set<string>): boolean;
|
|
15
15
|
export declare function getProjectConfigByPath(graph: ProjectGraph, configPath: string): ProjectConfiguration;
|
|
16
16
|
export declare function createExecutorContext(graph: ProjectGraph, targets: Record<string, TargetConfiguration>, projectName: string, targetName: string, configurationName: string): ExecutorContext;
|
package/src/utils/ct-helpers.js
CHANGED
|
@@ -30,14 +30,18 @@ exports.getTempTailwindPath = getTempTailwindPath;
|
|
|
30
30
|
* Checks if the childProjectName is a descendent of the parentProjectName
|
|
31
31
|
* in the project graph
|
|
32
32
|
**/
|
|
33
|
-
function isCtProjectUsingBuildProject(graph, parentProjectName, childProjectName) {
|
|
33
|
+
function isCtProjectUsingBuildProject(graph, parentProjectName, childProjectName, seen = new Set()) {
|
|
34
|
+
if (seen.has(parentProjectName)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
seen.add(parentProjectName);
|
|
34
38
|
const isProjectDirectDep = graph.dependencies[parentProjectName].some((p) => p.target === childProjectName);
|
|
35
39
|
if (isProjectDirectDep) {
|
|
36
40
|
return true;
|
|
37
41
|
}
|
|
38
42
|
const maybeIntermediateProjects = graph.dependencies[parentProjectName].filter((p) => !graph.externalNodes[p.target]);
|
|
39
43
|
for (const maybeIntermediateProject of maybeIntermediateProjects) {
|
|
40
|
-
if (isCtProjectUsingBuildProject(graph, maybeIntermediateProject.target, childProjectName)) {
|
|
44
|
+
if (isCtProjectUsingBuildProject(graph, maybeIntermediateProject.target, childProjectName, seen)) {
|
|
41
45
|
return true;
|
|
42
46
|
}
|
|
43
47
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ExecutorContext } from '@nx/devkit';
|
|
2
|
+
import { CypressExecutorOptions } from '../executors/cypress/cypress.impl';
|
|
3
|
+
export declare function startDevServer(opts: Omit<CypressExecutorOptions, 'cypressConfig'>, context: ExecutorContext): AsyncGenerator<{
|
|
4
|
+
baseUrl: any;
|
|
5
|
+
portLockFilePath?: undefined;
|
|
6
|
+
} | {
|
|
7
|
+
baseUrl: any;
|
|
8
|
+
portLockFilePath: string;
|
|
9
|
+
}, void, unknown>;
|