@nx/cypress 19.5.5 → 19.6.0-beta.0
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/migrations.json +12 -0
- package/package.json +5 -5
- package/plugins/cypress-preset.d.ts +4 -0
- package/plugins/cypress-preset.js +1 -0
- package/src/generators/configuration/configuration.d.ts +1 -0
- package/src/generators/configuration/configuration.js +3 -0
- package/src/migrations/update-19-6-0/add-e2e-ci-target-defaults.d.ts +2 -0
- package/src/migrations/update-19-6-0/add-e2e-ci-target-defaults.js +69 -0
- package/src/migrations/update-19-6-0/update-ci-webserver-for-vite.d.ts +2 -0
- package/src/migrations/update-19-6-0/update-ci-webserver-for-vite.js +93 -0
- package/src/plugins/plugin.js +2 -1
package/migrations.json
CHANGED
|
@@ -29,6 +29,18 @@
|
|
|
29
29
|
"version": "18.1.0-beta.3",
|
|
30
30
|
"description": "Update to Cypress ^13.6.6 if the workspace is using Cypress v13 to ensure workspaces don't use v13.6.5 which has an issue when verifying Cypress.",
|
|
31
31
|
"implementation": "./src/migrations/update-18-1-0/update-cypress-version-13-6-6"
|
|
32
|
+
},
|
|
33
|
+
"update-19-6-0-update-ci-webserver-for-vite": {
|
|
34
|
+
"cli": "nx",
|
|
35
|
+
"version": "19.6.0-beta.0",
|
|
36
|
+
"description": "Update ciWebServerCommand to use previewTargetName if Vite is detected for the application.",
|
|
37
|
+
"implementation": "./src/migrations/update-19-6-0/update-ci-webserver-for-vite"
|
|
38
|
+
},
|
|
39
|
+
"update-19-6-0-add-e2e-ci-target-defaults": {
|
|
40
|
+
"cli": "nx",
|
|
41
|
+
"version": "19.6.0-beta.0",
|
|
42
|
+
"description": "Add inferred ciTargetNames to targetDefaults with dependsOn to ensure dependent application builds are scheduled before atomized tasks.",
|
|
43
|
+
"implementation": "./src/migrations/update-19-6-0/add-e2e-ci-target-defaults"
|
|
32
44
|
}
|
|
33
45
|
},
|
|
34
46
|
"packageJsonUpdates": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nx/cypress",
|
|
3
|
-
"version": "19.
|
|
3
|
+
"version": "19.6.0-beta.0",
|
|
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": {
|
|
@@ -36,13 +36,13 @@
|
|
|
36
36
|
"migrations": "./migrations.json"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@nx/devkit": "19.
|
|
40
|
-
"@nx/eslint": "19.
|
|
41
|
-
"@nx/js": "19.
|
|
39
|
+
"@nx/devkit": "19.6.0-beta.0",
|
|
40
|
+
"@nx/eslint": "19.6.0-beta.0",
|
|
41
|
+
"@nx/js": "19.6.0-beta.0",
|
|
42
42
|
"@phenomnomnominal/tsquery": "~5.0.1",
|
|
43
43
|
"detect-port": "^1.5.1",
|
|
44
44
|
"tslib": "^2.3.0",
|
|
45
|
-
"@nrwl/cypress": "19.
|
|
45
|
+
"@nrwl/cypress": "19.6.0-beta.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"cypress": ">= 3 < 14"
|
|
@@ -64,6 +64,10 @@ export type NxCypressE2EPresetOptions = {
|
|
|
64
64
|
* A command to start the web server - used for e2e tests distributed by Nx.
|
|
65
65
|
*/
|
|
66
66
|
ciWebServerCommand?: string;
|
|
67
|
+
/**
|
|
68
|
+
* The url of the web server for ciWebServerCommand
|
|
69
|
+
*/
|
|
70
|
+
ciBaseUrl?: string;
|
|
67
71
|
/**
|
|
68
72
|
* Configures how the web server command is started and monitored.
|
|
69
73
|
*/
|
|
@@ -85,6 +85,7 @@ function nxE2EPreset(pathToConfig, options) {
|
|
|
85
85
|
webServerCommand: options?.webServerCommands?.default,
|
|
86
86
|
webServerCommands: options?.webServerCommands,
|
|
87
87
|
ciWebServerCommand: options?.ciWebServerCommand,
|
|
88
|
+
ciBaseUrl: options?.ciBaseUrl,
|
|
88
89
|
},
|
|
89
90
|
async setupNodeEvents(on, config) {
|
|
90
91
|
const webServerCommands = config.env?.webServerCommands ?? options?.webServerCommands;
|
|
@@ -16,6 +16,7 @@ export interface CypressE2EConfigSchema {
|
|
|
16
16
|
rootProject?: boolean;
|
|
17
17
|
webServerCommands?: Record<string, string>;
|
|
18
18
|
ciWebServerCommand?: string;
|
|
19
|
+
ciBaseUrl?: string;
|
|
19
20
|
addPlugin?: boolean;
|
|
20
21
|
}
|
|
21
22
|
export declare function configurationGenerator(tree: Tree, options: CypressE2EConfigSchema): Promise<GeneratorCallback>;
|
|
@@ -125,9 +125,11 @@ async function addFiles(tree, options, projectGraph, hasPlugin) {
|
|
|
125
125
|
const cyFile = (0, devkit_1.joinPathFragments)(projectConfig.root, options.js ? 'cypress.config.js' : 'cypress.config.ts');
|
|
126
126
|
let webServerCommands;
|
|
127
127
|
let ciWebServerCommand;
|
|
128
|
+
let ciBaseUrl;
|
|
128
129
|
if (hasPlugin && options.webServerCommands && options.ciWebServerCommand) {
|
|
129
130
|
webServerCommands = options.webServerCommands;
|
|
130
131
|
ciWebServerCommand = options.ciWebServerCommand;
|
|
132
|
+
ciBaseUrl = options.ciBaseUrl;
|
|
131
133
|
}
|
|
132
134
|
else if (hasPlugin && options.devServerTarget) {
|
|
133
135
|
webServerCommands = {};
|
|
@@ -150,6 +152,7 @@ async function addFiles(tree, options, projectGraph, hasPlugin) {
|
|
|
150
152
|
bundler: options.bundler === 'vite' ? 'vite' : undefined,
|
|
151
153
|
webServerCommands,
|
|
152
154
|
ciWebServerCommand: ciWebServerCommand,
|
|
155
|
+
ciBaseUrl,
|
|
153
156
|
}, options.baseUrl);
|
|
154
157
|
tree.write(cyFile, updatedCyConfig);
|
|
155
158
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = addE2eCiTargetDefaults;
|
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const target_defaults_utils_1 = require("@nx/devkit/src/generators/target-defaults-utils");
|
|
6
|
+
const internal_api_1 = require("nx/src/project-graph/plugins/internal-api");
|
|
7
|
+
const devkit_internals_1 = require("nx/src/devkit-internals");
|
|
8
|
+
const tsquery_1 = require("@phenomnomnominal/tsquery");
|
|
9
|
+
async function addE2eCiTargetDefaults(tree) {
|
|
10
|
+
const pluginName = '@nx/cypress/plugin';
|
|
11
|
+
const graph = await (0, devkit_1.createProjectGraphAsync)();
|
|
12
|
+
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
13
|
+
const matchingPluginRegistrations = nxJson.plugins?.filter((p) => typeof p === 'string' ? p === pluginName : p.plugin === pluginName);
|
|
14
|
+
if (!matchingPluginRegistrations) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const { createNodesV2, } = await Promise.resolve(`${pluginName}`).then(s => require(s));
|
|
18
|
+
for (const plugin of matchingPluginRegistrations) {
|
|
19
|
+
let projectConfigs;
|
|
20
|
+
try {
|
|
21
|
+
const loadedPlugin = new internal_api_1.LoadedNxPlugin({ createNodesV2, name: pluginName }, plugin);
|
|
22
|
+
projectConfigs = await (0, devkit_internals_1.retrieveProjectConfigurations)([loadedPlugin], tree.root, nxJson);
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
if (e instanceof devkit_internals_1.ProjectConfigurationsError) {
|
|
26
|
+
projectConfigs = e.partialProjectConfigurationsResult;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
throw e;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
for (const configFile of projectConfigs.matchingProjectFiles) {
|
|
33
|
+
const configFileContents = tree.read(configFile, 'utf-8');
|
|
34
|
+
if (!configFileContents.includes('ciWebServerCommand')) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const ast = tsquery_1.tsquery.ast(configFileContents);
|
|
38
|
+
const CI_WEBSERVER_COMMAND_SELECTOR = 'ObjectLiteralExpression PropertyAssignment:has(Identifier[name=ciWebServerCommand]) > StringLiteral';
|
|
39
|
+
const nodes = (0, tsquery_1.tsquery)(ast, CI_WEBSERVER_COMMAND_SELECTOR, {
|
|
40
|
+
visitAllChildren: true,
|
|
41
|
+
});
|
|
42
|
+
if (!nodes.length) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const ciWebServerCommand = nodes[0].getText();
|
|
46
|
+
const NX_TARGET_REGEX = "(?<=nx run )[^']+";
|
|
47
|
+
const matches = ciWebServerCommand.match(NX_TARGET_REGEX);
|
|
48
|
+
if (!matches) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const targetString = matches[0];
|
|
52
|
+
const { project, target, configuration } = (0, devkit_1.parseTargetString)(targetString, graph);
|
|
53
|
+
const serveStaticTarget = graph.nodes[project].data.targets[target];
|
|
54
|
+
let resolvedBuildTarget;
|
|
55
|
+
if (serveStaticTarget.dependsOn) {
|
|
56
|
+
resolvedBuildTarget = serveStaticTarget.dependsOn.join(',');
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
resolvedBuildTarget =
|
|
60
|
+
(configuration
|
|
61
|
+
? serveStaticTarget.configurations[configuration].buildTarget
|
|
62
|
+
: serveStaticTarget.options.buildTarget) ?? 'build';
|
|
63
|
+
}
|
|
64
|
+
const buildTarget = `^${resolvedBuildTarget}`;
|
|
65
|
+
await (0, target_defaults_utils_1.addE2eCiTargetDefaults)(tree, pluginName, buildTarget, configFile);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
await (0, devkit_1.formatFiles)(tree);
|
|
69
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = default_1;
|
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const devkit_internals_1 = require("nx/src/devkit-internals");
|
|
6
|
+
const tsquery_1 = require("@phenomnomnominal/tsquery");
|
|
7
|
+
async function default_1(tree) {
|
|
8
|
+
const pluginName = '@nx/cypress/plugin';
|
|
9
|
+
const graph = await (0, devkit_1.createProjectGraphAsync)();
|
|
10
|
+
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
11
|
+
const matchingPluginRegistrations = nxJson.plugins?.filter((p) => typeof p === 'string' ? p === pluginName : p.plugin === pluginName);
|
|
12
|
+
const { createNodesV2, } = await Promise.resolve(`${pluginName}`).then(s => require(s));
|
|
13
|
+
for (const plugin of matchingPluginRegistrations) {
|
|
14
|
+
let projectConfigs;
|
|
15
|
+
try {
|
|
16
|
+
const loadedPlugin = new devkit_internals_1.LoadedNxPlugin({ createNodesV2, name: pluginName }, plugin);
|
|
17
|
+
projectConfigs = await (0, devkit_internals_1.retrieveProjectConfigurations)([loadedPlugin], tree.root, nxJson);
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
if (e instanceof devkit_internals_1.ProjectConfigurationsError) {
|
|
21
|
+
projectConfigs = e.partialProjectConfigurationsResult;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
throw e;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
for (const configFile of projectConfigs.matchingProjectFiles) {
|
|
28
|
+
const configFileContents = tree.read(configFile, 'utf-8');
|
|
29
|
+
if (!configFileContents.includes('ciWebServerCommand')) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const ast = tsquery_1.tsquery.ast(configFileContents);
|
|
33
|
+
const CI_WEBSERVER_COMMAND_SELECTOR = 'ObjectLiteralExpression PropertyAssignment:has(Identifier[name=ciWebServerCommand]) > StringLiteral';
|
|
34
|
+
const nodes = (0, tsquery_1.tsquery)(ast, CI_WEBSERVER_COMMAND_SELECTOR, {
|
|
35
|
+
visitAllChildren: true,
|
|
36
|
+
});
|
|
37
|
+
if (!nodes.length) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const ciWebServerCommand = nodes[0].getText();
|
|
41
|
+
const NX_TARGET_REGEX = "(?<=nx run )[^']+";
|
|
42
|
+
const matches = ciWebServerCommand.match(NX_TARGET_REGEX);
|
|
43
|
+
if (!matches) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const targetString = matches[0];
|
|
47
|
+
const { project, target, configuration } = (0, devkit_1.parseTargetString)(targetString, graph);
|
|
48
|
+
const pathToViteConfig = [
|
|
49
|
+
(0, devkit_1.joinPathFragments)(graph.nodes[project].data.root, 'vite.config.ts'),
|
|
50
|
+
(0, devkit_1.joinPathFragments)(graph.nodes[project].data.root, 'vite.config.js'),
|
|
51
|
+
].find((p) => tree.exists(p));
|
|
52
|
+
if (!pathToViteConfig) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const viteConfigContents = tree.read(pathToViteConfig, 'utf-8');
|
|
56
|
+
if (!viteConfigContents.includes('preview:')) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const matchingVitePlugin = await findPluginForConfigFile(tree, '@nx/vite/plugin', pathToViteConfig);
|
|
60
|
+
const previewTargetName = matchingVitePlugin
|
|
61
|
+
? typeof matchingVitePlugin === 'string'
|
|
62
|
+
? 'preview'
|
|
63
|
+
: matchingVitePlugin.options?.previewTargetName ?? 'preview'
|
|
64
|
+
: 'preview';
|
|
65
|
+
tree.write(configFile, `${configFileContents.slice(0, nodes[0].getStart())}'nx run ${project}:${previewTargetName}',
|
|
66
|
+
ciBaseUrl: "http://localhost:4300"${configFileContents.slice(nodes[0].getEnd())}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
await (0, devkit_1.formatFiles)(tree);
|
|
70
|
+
}
|
|
71
|
+
async function findPluginForConfigFile(tree, pluginName, pathToConfigFile) {
|
|
72
|
+
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
73
|
+
if (!nxJson.plugins) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const pluginRegistrations = nxJson.plugins.filter((p) => (typeof p === 'string' ? p === pluginName : p.plugin === pluginName));
|
|
77
|
+
for (const plugin of pluginRegistrations) {
|
|
78
|
+
if (typeof plugin === 'string') {
|
|
79
|
+
return plugin;
|
|
80
|
+
}
|
|
81
|
+
if (!plugin.include && !plugin.exclude) {
|
|
82
|
+
return plugin;
|
|
83
|
+
}
|
|
84
|
+
if (plugin.include || plugin.exclude) {
|
|
85
|
+
const resolvedPlugin = await Promise.resolve(`${pluginName}`).then(s => require(s));
|
|
86
|
+
const pluginGlob = resolvedPlugin.createNodesV2?.[0] ?? resolvedPlugin.createNodes?.[0];
|
|
87
|
+
const matchingConfigFile = (0, devkit_internals_1.findMatchingConfigFiles)([pathToConfigFile], pluginGlob, plugin.include, plugin.exclude);
|
|
88
|
+
if (matchingConfigFile.length) {
|
|
89
|
+
return plugin;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
package/src/plugins/plugin.js
CHANGED
|
@@ -160,6 +160,7 @@ async function buildCypressTargets(configFilePath, projectRoot, options, context
|
|
|
160
160
|
? cypressConfig.e2e.excludeSpecPattern.map((p) => (0, path_1.join)(projectRoot, p))
|
|
161
161
|
: [(0, path_1.join)(projectRoot, cypressConfig.e2e.excludeSpecPattern)];
|
|
162
162
|
const specFiles = await (0, workspace_context_1.globWithWorkspaceContext)(context.workspaceRoot, specPatterns, excludeSpecPatterns);
|
|
163
|
+
const ciBaseUrl = pluginPresetOptions?.ciBaseUrl;
|
|
163
164
|
const dependsOn = [];
|
|
164
165
|
const outputs = getOutputs(projectRoot, cypressConfig, 'e2e');
|
|
165
166
|
const inputs = getInputs(namedInputs);
|
|
@@ -174,7 +175,7 @@ async function buildCypressTargets(configFilePath, projectRoot, options, context
|
|
|
174
175
|
outputs,
|
|
175
176
|
inputs,
|
|
176
177
|
cache: true,
|
|
177
|
-
command: `cypress run --env webServerCommand="${ciWebServerCommand}" --spec ${relativeSpecFilePath}`,
|
|
178
|
+
command: `cypress run --env webServerCommand="${ciWebServerCommand}" --spec ${relativeSpecFilePath}${ciBaseUrl ? ` --config='{"baseUrl": "${ciBaseUrl}"}'` : ''}`,
|
|
178
179
|
options: {
|
|
179
180
|
cwd: projectRoot,
|
|
180
181
|
},
|