@nx/cypress 16.8.0-beta.4 → 16.8.0-beta.6
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 +6 -0
- package/package.json +7 -7
- package/plugins/cypress-preset.d.ts +0 -3
- package/plugins/cypress-preset.js +15 -7
- package/src/executors/cypress/cypress.impl.d.ts +1 -0
- package/src/executors/cypress/cypress.impl.js +180 -210
- package/src/executors/cypress/schema.json +4 -0
- package/src/generators/base-setup/base-setup.js +15 -5
- package/src/generators/component-configuration/component-configuration.js +42 -30
- package/src/generators/configuration/configuration.js +87 -70
- package/src/generators/convert-tslint-to-eslint/convert-tslint-to-eslint.js +75 -78
- package/src/generators/cypress-project/cypress-project.js +76 -63
- package/src/generators/init/init.js +16 -18
- package/src/generators/migrate-to-cypress-11/conversion.util.js +22 -16
- package/src/generators/migrate-to-cypress-11/migrate-to-cypress-11.js +38 -35
- package/src/migrations/update-12-8-0/remove-typescript-plugin.js +23 -26
- package/src/migrations/update-14-6-1/update-cypress-configs-presets.js +79 -68
- package/src/migrations/update-14-7-0/update-cypress-version-if-10.js +1 -2
- package/src/migrations/update-15-0-0/add-cypress-inputs.js +24 -29
- package/src/migrations/update-15-0-0/update-cy-mount-usage.js +25 -29
- package/src/migrations/update-15-1-0/cypress-11.js +34 -37
- package/src/migrations/update-15-5-0/update-to-cypress-12.js +3 -5
- package/src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages.js +3 -6
- package/src/migrations/update-16-2-0/update-cy-tsconfig.js +25 -24
- package/src/migrations/update-16-4-0/tsconfig-sourcemaps.js +52 -49
- package/src/migrations/update-16-8-0/cypress-13.d.ts +7 -0
- package/src/migrations/update-16-8-0/cypress-13.js +179 -0
- package/src/plugins/preprocessor-vite.js +7 -5
- package/src/utils/add-linter.js +78 -81
- package/src/utils/config.js +54 -62
- package/src/utils/ct-helpers.js +6 -8
- package/src/utils/cypress-version.js +1 -1
- package/src/utils/find-target-options.js +37 -43
- package/src/utils/versions.d.ts +1 -1
- package/src/utils/versions.js +1 -1
package/migrations.json
CHANGED
|
@@ -59,6 +59,12 @@
|
|
|
59
59
|
"version": "16.4.0-beta.10",
|
|
60
60
|
"description": "Remove tsconfig.e2e.json and add settings to project tsconfig.json. tsConfigs executor option is now deprecated. The project level tsconfig.json file should be used instead.",
|
|
61
61
|
"implementation": "./src/migrations/update-16-4-0/tsconfig-sourcemaps"
|
|
62
|
+
},
|
|
63
|
+
"update-16-8-0-cypress-13": {
|
|
64
|
+
"cli": "nx",
|
|
65
|
+
"version": "16.8.0-beta.4",
|
|
66
|
+
"description": "Update to Cypress v13. Most noteable change is video recording is off by default. This migration will only update if the workspace is already on Cypress v12. https://docs.cypress.io/guides/references/migration-guide#Migrating-to-Cypress-130",
|
|
67
|
+
"implementation": "./src/migrations/update-16-8-0/cypress-13"
|
|
62
68
|
}
|
|
63
69
|
},
|
|
64
70
|
"packageJsonUpdates": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nx/cypress",
|
|
3
|
-
"version": "16.8.0-beta.
|
|
3
|
+
"version": "16.8.0-beta.6",
|
|
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,17 +34,17 @@
|
|
|
34
34
|
"migrations": "./migrations.json"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@nrwl/cypress": "16.8.0-beta.
|
|
38
|
-
"@nx/devkit": "16.8.0-beta.
|
|
39
|
-
"@nx/js": "16.8.0-beta.
|
|
40
|
-
"@nx/linter": "16.8.0-beta.
|
|
37
|
+
"@nrwl/cypress": "16.8.0-beta.6",
|
|
38
|
+
"@nx/devkit": "16.8.0-beta.6",
|
|
39
|
+
"@nx/js": "16.8.0-beta.6",
|
|
40
|
+
"@nx/linter": "16.8.0-beta.6",
|
|
41
41
|
"@phenomnomnominal/tsquery": "~5.0.1",
|
|
42
42
|
"detect-port": "^1.5.1",
|
|
43
43
|
"semver": "7.5.3",
|
|
44
44
|
"tslib": "^2.3.0"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
|
-
"cypress": ">= 3 <
|
|
47
|
+
"cypress": ">= 3 < 14"
|
|
48
48
|
},
|
|
49
49
|
"peerDependenciesMeta": {
|
|
50
50
|
"cypress": {
|
|
@@ -55,5 +55,5 @@
|
|
|
55
55
|
"access": "public"
|
|
56
56
|
},
|
|
57
57
|
"type": "commonjs",
|
|
58
|
-
"gitHead": "
|
|
58
|
+
"gitHead": "0cc6ba996f2ef09f00b932c8eacf42de642457f3"
|
|
59
59
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
interface BaseCypressPreset {
|
|
2
2
|
videosFolder: string;
|
|
3
3
|
screenshotsFolder: string;
|
|
4
|
-
video: boolean;
|
|
5
4
|
chromeWebSecurity: boolean;
|
|
6
5
|
}
|
|
7
6
|
export interface NxComponentTestingOptions {
|
|
@@ -39,7 +38,6 @@ export declare function nxE2EPreset(pathToConfig: string, options?: NxCypressE2E
|
|
|
39
38
|
fixturesFolder: string;
|
|
40
39
|
videosFolder: string;
|
|
41
40
|
screenshotsFolder: string;
|
|
42
|
-
video: boolean;
|
|
43
41
|
chromeWebSecurity: boolean;
|
|
44
42
|
} | {
|
|
45
43
|
setupNodeEvents(on: any): void;
|
|
@@ -49,7 +47,6 @@ export declare function nxE2EPreset(pathToConfig: string, options?: NxCypressE2E
|
|
|
49
47
|
fixturesFolder: string;
|
|
50
48
|
videosFolder: string;
|
|
51
49
|
screenshotsFolder: string;
|
|
52
|
-
video: boolean;
|
|
53
50
|
chromeWebSecurity: boolean;
|
|
54
51
|
};
|
|
55
52
|
export type NxCypressE2EPresetOptions = {
|
|
@@ -8,7 +8,7 @@ const preprocessor_vite_1 = require("../src/plugins/preprocessor-vite");
|
|
|
8
8
|
function nxBaseCypressPreset(pathToConfig, options) {
|
|
9
9
|
// used to set babel settings for react CT.
|
|
10
10
|
process.env.NX_CYPRESS_COMPONENT_TEST =
|
|
11
|
-
|
|
11
|
+
options?.testingType === 'component' ? 'true' : 'false';
|
|
12
12
|
// prevent from placing path outside the root of the workspace
|
|
13
13
|
// if they pass in a file or directory
|
|
14
14
|
const normalizedPath = (0, fs_1.lstatSync)(pathToConfig).isDirectory()
|
|
@@ -21,7 +21,6 @@ function nxBaseCypressPreset(pathToConfig, options) {
|
|
|
21
21
|
return {
|
|
22
22
|
videosFolder,
|
|
23
23
|
screenshotsFolder,
|
|
24
|
-
video: true,
|
|
25
24
|
chromeWebSecurity: false,
|
|
26
25
|
};
|
|
27
26
|
}
|
|
@@ -43,12 +42,21 @@ exports.nxBaseCypressPreset = nxBaseCypressPreset;
|
|
|
43
42
|
* @param pathToConfig will be used to construct the output paths for videos and screenshots
|
|
44
43
|
*/
|
|
45
44
|
function nxE2EPreset(pathToConfig, options) {
|
|
46
|
-
const basePath =
|
|
47
|
-
const baseConfig =
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
const basePath = options?.cypressDir || 'src';
|
|
46
|
+
const baseConfig = {
|
|
47
|
+
...nxBaseCypressPreset(pathToConfig),
|
|
48
|
+
fileServerFolder: '.',
|
|
49
|
+
supportFile: `${basePath}/support/e2e.ts`,
|
|
50
|
+
specPattern: `${basePath}/**/*.cy.{js,jsx,ts,tsx}`,
|
|
51
|
+
fixturesFolder: `${basePath}/fixtures`,
|
|
52
|
+
};
|
|
53
|
+
if (options?.bundler === 'vite') {
|
|
54
|
+
return {
|
|
55
|
+
...baseConfig,
|
|
56
|
+
setupNodeEvents(on) {
|
|
50
57
|
on('file:preprocessor', (0, preprocessor_vite_1.default)());
|
|
51
|
-
}
|
|
58
|
+
},
|
|
59
|
+
};
|
|
52
60
|
}
|
|
53
61
|
return baseConfig;
|
|
54
62
|
}
|
|
@@ -33,6 +33,7 @@ export interface CypressExecutorOptions extends Json {
|
|
|
33
33
|
tag?: string;
|
|
34
34
|
port?: number | 'cypress-auto';
|
|
35
35
|
quiet?: boolean;
|
|
36
|
+
runnerUi?: boolean;
|
|
36
37
|
}
|
|
37
38
|
export default function cypressExecutor(options: CypressExecutorOptions, context: ExecutorContext): Promise<{
|
|
38
39
|
success: any;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
3
|
const devkit_1 = require("@nx/devkit");
|
|
5
4
|
const executor_utils_1 = require("nx/src/command-line/run/executor-utils");
|
|
6
5
|
const fs_1 = require("fs");
|
|
@@ -9,48 +8,35 @@ const ct_helpers_1 = require("../../utils/ct-helpers");
|
|
|
9
8
|
const cypress_version_1 = require("../../utils/cypress-version");
|
|
10
9
|
const detectPort = require("detect-port");
|
|
11
10
|
const Cypress = require('cypress'); // @NOTE: Importing via ES6 messes the whole test dependencies.
|
|
12
|
-
function cypressExecutor(options, context) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
let success;
|
|
11
|
+
async function cypressExecutor(options, context) {
|
|
12
|
+
options = normalizeOptions(options, context);
|
|
13
|
+
// this is used by cypress component testing presets to build the executor contexts with the correct configuration options.
|
|
14
|
+
process.env.NX_CYPRESS_TARGET_CONFIGURATION = context.configurationName;
|
|
15
|
+
let success;
|
|
16
|
+
for await (const devServerValues of startDevServer(options, context)) {
|
|
19
17
|
try {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (!options.watch)
|
|
27
|
-
break;
|
|
28
|
-
}
|
|
29
|
-
catch (e) {
|
|
30
|
-
devkit_1.logger.error(e.message);
|
|
31
|
-
success = false;
|
|
32
|
-
if (!options.watch)
|
|
33
|
-
break;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
18
|
+
success = await runCypress(devServerValues.baseUrl, {
|
|
19
|
+
...options,
|
|
20
|
+
portLockFilePath: devServerValues.portLockFilePath,
|
|
21
|
+
});
|
|
22
|
+
if (!options.watch)
|
|
23
|
+
break;
|
|
36
24
|
}
|
|
37
|
-
catch (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
finally { if (e_1) throw e_1.error; }
|
|
25
|
+
catch (e) {
|
|
26
|
+
devkit_1.logger.error(e.message);
|
|
27
|
+
success = false;
|
|
28
|
+
if (!options.watch)
|
|
29
|
+
break;
|
|
43
30
|
}
|
|
44
|
-
|
|
45
|
-
}
|
|
31
|
+
}
|
|
32
|
+
return { success };
|
|
46
33
|
}
|
|
47
34
|
exports.default = cypressExecutor;
|
|
48
35
|
function normalizeOptions(options, context) {
|
|
49
|
-
var _a, _b, _c;
|
|
50
36
|
options.env = options.env || {};
|
|
51
37
|
if (options.testingType === 'component') {
|
|
52
|
-
const project =
|
|
53
|
-
if (
|
|
38
|
+
const project = context?.projectGraph?.nodes?.[context.projectName];
|
|
39
|
+
if (project?.data?.root) {
|
|
54
40
|
options.ctTailwindPath = (0, ct_helpers_1.getTempTailwindPath)(context);
|
|
55
41
|
}
|
|
56
42
|
}
|
|
@@ -104,202 +90,187 @@ A generator to migrate from v8 to v10 is provided. See https://nx.dev/cypress/v1
|
|
|
104
90
|
`);
|
|
105
91
|
}
|
|
106
92
|
}
|
|
107
|
-
function startDevServer(opts, context) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
93
|
+
async function* startDevServer(opts, context) {
|
|
94
|
+
// no dev server, return the provisioned base url
|
|
95
|
+
if (!opts.devServerTarget || opts.skipServe) {
|
|
96
|
+
yield { baseUrl: opts.baseUrl };
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const parsedDevServerTarget = (0, devkit_1.parseTargetString)(opts.devServerTarget, context.projectGraph);
|
|
100
|
+
const [targetSupportsWatchOpt] = getValueFromSchema(context, parsedDevServerTarget, 'watch');
|
|
101
|
+
const overrides = {
|
|
102
|
+
// @NOTE: Do not forward watch option if not supported by the target dev server,
|
|
103
|
+
// this is relevant for running Cypress against dev server target that does not support this option,
|
|
104
|
+
// for instance @nguniversal/builders:ssr-dev-server.
|
|
105
|
+
...(targetSupportsWatchOpt ? { watch: opts.watch } : {}),
|
|
106
|
+
};
|
|
107
|
+
if (opts.port === 'cypress-auto') {
|
|
108
|
+
const freePort = await getPortForProject(context, parsedDevServerTarget);
|
|
109
|
+
overrides['port'] = freePort;
|
|
110
|
+
}
|
|
111
|
+
else if (opts.port !== undefined) {
|
|
112
|
+
overrides['port'] = opts.port;
|
|
113
|
+
// zero is a special case that means any valid port so there is no reason to try to 'lock it'
|
|
114
|
+
if (opts.port !== 0) {
|
|
115
|
+
const didLock = attemptToLockPort(opts.port);
|
|
116
|
+
if (!didLock) {
|
|
117
|
+
devkit_1.logger.warn((0, devkit_1.stripIndents) `${opts.port} is potentially already in use by another cypress run.
|
|
130
118
|
If the port is in use, try using a different port value or passing --port='cypress-auto' to find a free port.`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
try {
|
|
135
|
-
for (var _g = true, _h = tslib_1.__asyncValues(yield tslib_1.__await((0, devkit_1.runExecutor)(parsedDevServerTarget, overrides, context))), _j; _j = yield tslib_1.__await(_h.next()), _d = _j.done, !_d; _g = true) {
|
|
136
|
-
_f = _j.value;
|
|
137
|
-
_g = false;
|
|
138
|
-
const output = _f;
|
|
139
|
-
if (!output.success && !opts.watch)
|
|
140
|
-
throw new Error('Could not compile application files');
|
|
141
|
-
if (!opts.baseUrl &&
|
|
142
|
-
!output.baseUrl &&
|
|
143
|
-
!((_a = output.info) === null || _a === void 0 ? void 0 : _a.baseUrl) &&
|
|
144
|
-
((_b = output.info) === null || _b === void 0 ? void 0 : _b.port)) {
|
|
145
|
-
output.baseUrl = `http://localhost:${output.info.port}`;
|
|
146
|
-
}
|
|
147
|
-
yield yield tslib_1.__await({
|
|
148
|
-
baseUrl: opts.baseUrl || output.baseUrl || ((_c = output.info) === null || _c === void 0 ? void 0 : _c.baseUrl),
|
|
149
|
-
portLockFilePath: overrides.port && (0, path_1.join)(__dirname, `${overrides.port}.txt`),
|
|
150
|
-
});
|
|
151
119
|
}
|
|
152
120
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
121
|
+
}
|
|
122
|
+
for await (const output of await (0, devkit_1.runExecutor)(parsedDevServerTarget, overrides, context)) {
|
|
123
|
+
if (!output.success && !opts.watch)
|
|
124
|
+
throw new Error('Could not compile application files');
|
|
125
|
+
if (!opts.baseUrl &&
|
|
126
|
+
!output.baseUrl &&
|
|
127
|
+
!output.info?.baseUrl &&
|
|
128
|
+
output.info?.port) {
|
|
129
|
+
output.baseUrl = `http://localhost:${output.info.port}`;
|
|
159
130
|
}
|
|
160
|
-
|
|
131
|
+
yield {
|
|
132
|
+
baseUrl: opts.baseUrl || output.baseUrl || output.info?.baseUrl,
|
|
133
|
+
portLockFilePath: overrides.port && (0, path_1.join)(__dirname, `${overrides.port}.txt`),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
161
136
|
}
|
|
162
137
|
/**
|
|
163
138
|
* @whatItDoes Initialize the Cypress test runner with the provided project configuration.
|
|
164
139
|
* By default, Cypress will run tests from the CLI without the GUI and provide directly the results in the console output.
|
|
165
140
|
* If `watch` is `true`: Open Cypress in the interactive GUI to interact directly with the application.
|
|
166
141
|
*/
|
|
167
|
-
function runCypress(baseUrl, opts) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
142
|
+
async function runCypress(baseUrl, opts) {
|
|
143
|
+
const cypressVersion = (0, cypress_version_1.installedCypressVersion)();
|
|
144
|
+
// Cypress expects the folder where a cypress config is present
|
|
145
|
+
const projectFolderPath = (0, path_1.dirname)(opts.cypressConfig);
|
|
146
|
+
const options = {
|
|
147
|
+
project: projectFolderPath,
|
|
148
|
+
configFile: (0, path_1.basename)(opts.cypressConfig),
|
|
149
|
+
};
|
|
150
|
+
// If not, will use the `baseUrl` normally from `cypress.json`
|
|
151
|
+
if (baseUrl) {
|
|
152
|
+
options.config = { baseUrl };
|
|
153
|
+
}
|
|
154
|
+
if (opts.browser) {
|
|
155
|
+
options.browser = opts.browser;
|
|
156
|
+
}
|
|
157
|
+
if (opts.env) {
|
|
158
|
+
options.env = opts.env;
|
|
159
|
+
}
|
|
160
|
+
if (opts.spec) {
|
|
161
|
+
options.spec = opts.spec;
|
|
162
|
+
}
|
|
163
|
+
options.tag = opts.tag;
|
|
164
|
+
options.exit = opts.exit;
|
|
165
|
+
options.headed = opts.headed;
|
|
166
|
+
options.runnerUi = opts.runnerUi;
|
|
167
|
+
if (opts.headless) {
|
|
168
|
+
options.headless = opts.headless;
|
|
169
|
+
}
|
|
170
|
+
options.record = opts.record;
|
|
171
|
+
options.key = opts.key;
|
|
172
|
+
options.parallel = opts.parallel;
|
|
173
|
+
options.ciBuildId = opts.ciBuildId?.toString();
|
|
174
|
+
options.group = opts.group;
|
|
175
|
+
// renamed in cy 10
|
|
176
|
+
if (cypressVersion >= 10) {
|
|
177
|
+
options.config ??= {};
|
|
178
|
+
options.config[opts.testingType] = {
|
|
179
|
+
excludeSpecPattern: opts.ignoreTestFiles,
|
|
176
180
|
};
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
options.ignoreTestFiles = opts.ignoreTestFiles;
|
|
210
|
-
}
|
|
211
|
-
if (opts.reporter) {
|
|
212
|
-
options.reporter = opts.reporter;
|
|
213
|
-
}
|
|
214
|
-
if (opts.reporterOptions) {
|
|
215
|
-
options.reporterOptions = opts.reporterOptions;
|
|
216
|
-
}
|
|
217
|
-
if (opts.quiet) {
|
|
218
|
-
options.quiet = opts.quiet;
|
|
219
|
-
}
|
|
220
|
-
options.testingType = opts.testingType;
|
|
221
|
-
const result = yield (opts.watch
|
|
222
|
-
? Cypress.open(options)
|
|
223
|
-
: Cypress.run(options));
|
|
224
|
-
cleanupTmpFile(opts.ctTailwindPath);
|
|
225
|
-
cleanupTmpFile(opts.portLockFilePath);
|
|
226
|
-
if (process.env.NX_VERBOSE_LOGGING === 'true' && opts.portLockFilePath) {
|
|
227
|
-
(0, fs_1.readdirSync)((0, path_1.dirname)(opts.portLockFilePath)).forEach((f) => {
|
|
228
|
-
if (f.endsWith('.txt')) {
|
|
229
|
-
devkit_1.logger.debug(`Lock file ${f} still present`);
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* `cypress.open` is returning `0` and is not of the same type as `cypress.run`.
|
|
235
|
-
* `cypress.open` is the graphical UI, so it will be obvious to know what wasn't
|
|
236
|
-
* working. Forcing the build to success when `cypress.open` is used.
|
|
237
|
-
*/
|
|
238
|
-
return !result.totalFailed && !result.failures;
|
|
239
|
-
});
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
options.ignoreTestFiles = opts.ignoreTestFiles;
|
|
184
|
+
}
|
|
185
|
+
if (opts.reporter) {
|
|
186
|
+
options.reporter = opts.reporter;
|
|
187
|
+
}
|
|
188
|
+
if (opts.reporterOptions) {
|
|
189
|
+
options.reporterOptions = opts.reporterOptions;
|
|
190
|
+
}
|
|
191
|
+
if (opts.quiet) {
|
|
192
|
+
options.quiet = opts.quiet;
|
|
193
|
+
}
|
|
194
|
+
options.testingType = opts.testingType;
|
|
195
|
+
const result = await (opts.watch
|
|
196
|
+
? Cypress.open(options)
|
|
197
|
+
: Cypress.run(options));
|
|
198
|
+
cleanupTmpFile(opts.ctTailwindPath);
|
|
199
|
+
cleanupTmpFile(opts.portLockFilePath);
|
|
200
|
+
if (process.env.NX_VERBOSE_LOGGING === 'true' && opts.portLockFilePath) {
|
|
201
|
+
(0, fs_1.readdirSync)((0, path_1.dirname)(opts.portLockFilePath)).forEach((f) => {
|
|
202
|
+
if (f.endsWith('.txt')) {
|
|
203
|
+
devkit_1.logger.debug(`Lock file ${f} still present`);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* `cypress.open` is returning `0` and is not of the same type as `cypress.run`.
|
|
209
|
+
* `cypress.open` is the graphical UI, so it will be obvious to know what wasn't
|
|
210
|
+
* working. Forcing the build to success when `cypress.open` is used.
|
|
211
|
+
*/
|
|
212
|
+
return !result.totalFailed && !result.failures;
|
|
240
213
|
}
|
|
241
214
|
/**
|
|
242
215
|
* try to find a free port for the project to run on
|
|
243
216
|
* will return undefined if no port is found or the project doesn't have a port option
|
|
244
217
|
**/
|
|
245
|
-
function getPortForProject(context, target, defaultPort = 4200) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
normalizedPortValue = defaultPort;
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
normalizedPortValue = Number(schemaPortValue);
|
|
258
|
-
}
|
|
259
|
-
if (isNaN(normalizedPortValue)) {
|
|
260
|
-
devkit_1.output.warn({
|
|
261
|
-
title: `Port Not a Number`,
|
|
262
|
-
bodyLines: [
|
|
263
|
-
`The port value found was not a number or can't be parsed to a number`,
|
|
264
|
-
`When reading the devServerTarget (${fmtTarget}) schema, expected ${schemaPortValue} to be a number but got NaN.`,
|
|
265
|
-
`Nx will use the default value of ${defaultPort} instead.`,
|
|
266
|
-
`You can manually specify a port by setting the 'port' option`,
|
|
267
|
-
],
|
|
268
|
-
});
|
|
269
|
-
normalizedPortValue = defaultPort;
|
|
270
|
-
}
|
|
271
|
-
try {
|
|
272
|
-
let attempts = 0;
|
|
273
|
-
// make sure when this check happens in parallel,
|
|
274
|
-
// we don't let the same port be used by multiple projects
|
|
275
|
-
do {
|
|
276
|
-
freePort = yield detectPort(freePort || normalizedPortValue);
|
|
277
|
-
if (attemptToLockPort(freePort)) {
|
|
278
|
-
break;
|
|
279
|
-
}
|
|
280
|
-
attempts++;
|
|
281
|
-
// increment port in case the lock file isn't cleaned up
|
|
282
|
-
freePort++;
|
|
283
|
-
} while (attempts < 20);
|
|
284
|
-
devkit_1.logger.info(`NX Using port ${freePort} for ${fmtTarget}`);
|
|
285
|
-
}
|
|
286
|
-
catch (err) {
|
|
287
|
-
throw new Error((0, devkit_1.stripIndents) `Unable to find a free port for the dev server, ${fmtTarget}.
|
|
288
|
-
You can disable auto port detection by specifing a port or not passing a value to --port`);
|
|
289
|
-
}
|
|
218
|
+
async function getPortForProject(context, target, defaultPort = 4200) {
|
|
219
|
+
const fmtTarget = (0, devkit_1.targetToTargetString)(target);
|
|
220
|
+
const [hasPortOpt, schemaPortValue] = getValueFromSchema(context, target, 'port');
|
|
221
|
+
let freePort;
|
|
222
|
+
if (hasPortOpt) {
|
|
223
|
+
let normalizedPortValue;
|
|
224
|
+
if (!schemaPortValue) {
|
|
225
|
+
devkit_1.logger.info(`NX ${fmtTarget} did not have a defined port value, checking for free port with the default value of ${defaultPort}`);
|
|
226
|
+
normalizedPortValue = defaultPort;
|
|
290
227
|
}
|
|
291
228
|
else {
|
|
229
|
+
normalizedPortValue = Number(schemaPortValue);
|
|
230
|
+
}
|
|
231
|
+
if (isNaN(normalizedPortValue)) {
|
|
292
232
|
devkit_1.output.warn({
|
|
293
|
-
title: `
|
|
233
|
+
title: `Port Not a Number`,
|
|
294
234
|
bodyLines: [
|
|
295
|
-
`The
|
|
296
|
-
`
|
|
297
|
-
`
|
|
235
|
+
`The port value found was not a number or can't be parsed to a number`,
|
|
236
|
+
`When reading the devServerTarget (${fmtTarget}) schema, expected ${schemaPortValue} to be a number but got NaN.`,
|
|
237
|
+
`Nx will use the default value of ${defaultPort} instead.`,
|
|
238
|
+
`You can manually specify a port by setting the 'port' option`,
|
|
298
239
|
],
|
|
299
240
|
});
|
|
241
|
+
normalizedPortValue = defaultPort;
|
|
300
242
|
}
|
|
301
|
-
|
|
302
|
-
|
|
243
|
+
try {
|
|
244
|
+
let attempts = 0;
|
|
245
|
+
// make sure when this check happens in parallel,
|
|
246
|
+
// we don't let the same port be used by multiple projects
|
|
247
|
+
do {
|
|
248
|
+
freePort = await detectPort(freePort || normalizedPortValue);
|
|
249
|
+
if (attemptToLockPort(freePort)) {
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
attempts++;
|
|
253
|
+
// increment port in case the lock file isn't cleaned up
|
|
254
|
+
freePort++;
|
|
255
|
+
} while (attempts < 20);
|
|
256
|
+
devkit_1.logger.info(`NX Using port ${freePort} for ${fmtTarget}`);
|
|
257
|
+
}
|
|
258
|
+
catch (err) {
|
|
259
|
+
throw new Error((0, devkit_1.stripIndents) `Unable to find a free port for the dev server, ${fmtTarget}.
|
|
260
|
+
You can disable auto port detection by specifing a port or not passing a value to --port`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
devkit_1.output.warn({
|
|
265
|
+
title: `No Port Option Found`,
|
|
266
|
+
bodyLines: [
|
|
267
|
+
`The 'port' option is set to 'cypress-auto', but the devServerTarget (${fmtTarget}) does not have a port option.`,
|
|
268
|
+
`Because of this, Nx is unable to verify the port is free before starting the dev server.`,
|
|
269
|
+
`This might cause issues if the devServerTarget is trying to use a port that is already in use.`,
|
|
270
|
+
],
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
return freePort;
|
|
303
274
|
}
|
|
304
275
|
/**
|
|
305
276
|
* Check if the given target has the given property in it's options.
|
|
@@ -307,7 +278,6 @@ You can disable auto port detection by specifing a port or not passing a value t
|
|
|
307
278
|
* the value will be undefined even if it's in the executor schema.
|
|
308
279
|
**/
|
|
309
280
|
function getValueFromSchema(context, target, property) {
|
|
310
|
-
var _a, _b;
|
|
311
281
|
let targetOpts;
|
|
312
282
|
try {
|
|
313
283
|
targetOpts = (0, devkit_1.readTargetOptions)(target, context);
|
|
@@ -322,7 +292,7 @@ ${e.message || e}`);
|
|
|
322
292
|
if (!targetHasOpt) {
|
|
323
293
|
// NOTE: readTargetOptions doesn't apply non defaulted values, i.e. @nx/vite has a port options but is optional
|
|
324
294
|
// so we double check the schema if readTargetOptions didn't return a value for the property
|
|
325
|
-
const projectConfig =
|
|
295
|
+
const projectConfig = context.projectsConfigurations?.projects?.[target.project];
|
|
326
296
|
const targetConfig = projectConfig.targets[target.target];
|
|
327
297
|
const [collection, executor] = targetConfig.executor.split(':');
|
|
328
298
|
const { schema } = (0, executor_utils_1.getExecutorInformation)(collection, executor, context.root);
|
|
@@ -164,6 +164,10 @@
|
|
|
164
164
|
"type": "boolean",
|
|
165
165
|
"description": "If passed, Cypress output will not be printed to stdout. Only output from the configured Mocha reporter will print.",
|
|
166
166
|
"default": false
|
|
167
|
+
},
|
|
168
|
+
"runnerUi": {
|
|
169
|
+
"type": "boolean",
|
|
170
|
+
"description": "Displays the Cypress Runner UI. Useful for when Test Replay is enabled and you would still like the Cypress Runner UI to be displayed for screenshots and video."
|
|
167
171
|
}
|
|
168
172
|
},
|
|
169
173
|
"additionalProperties": true,
|
|
@@ -10,9 +10,15 @@ function addBaseCypressSetup(tree, options) {
|
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
12
|
const opts = normalizeOptions(tree, projectConfig, options);
|
|
13
|
-
(0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files'), projectConfig.root,
|
|
13
|
+
(0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files'), projectConfig.root, {
|
|
14
|
+
...opts,
|
|
15
|
+
offsetFromRoot: (0, devkit_1.offsetFromRoot)(projectConfig.root),
|
|
16
|
+
offsetFromProjectRoot: opts.hasTsConfig ? opts.offsetFromProjectRoot : '',
|
|
17
|
+
tsConfigPath: opts.hasTsConfig
|
|
14
18
|
? `${opts.offsetFromProjectRoot}tsconfig.json`
|
|
15
|
-
: (0, js_1.getRelativePathToRootTsConfig)(tree, projectConfig.root),
|
|
19
|
+
: (0, js_1.getRelativePathToRootTsConfig)(tree, projectConfig.root),
|
|
20
|
+
ext: '',
|
|
21
|
+
});
|
|
16
22
|
if (opts.hasTsConfig) {
|
|
17
23
|
(0, devkit_1.updateJson)(tree, (0, devkit_1.joinPathFragments)(projectConfig.root, 'tsconfig.json'), (json) => {
|
|
18
24
|
// tsconfig doesn't have references so it shouldn't add them
|
|
@@ -35,12 +41,16 @@ function addBaseCypressSetup(tree, options) {
|
|
|
35
41
|
}
|
|
36
42
|
exports.addBaseCypressSetup = addBaseCypressSetup;
|
|
37
43
|
function normalizeOptions(tree, projectConfig, options) {
|
|
38
|
-
|
|
39
|
-
(_a = options.directory) !== null && _a !== void 0 ? _a : (options.directory = 'cypress');
|
|
44
|
+
options.directory ??= 'cypress';
|
|
40
45
|
const offsetFromProjectRoot = options.directory
|
|
41
46
|
.split('/')
|
|
42
47
|
.map((_) => '..')
|
|
43
48
|
.join('/');
|
|
44
49
|
const hasTsConfig = tree.exists((0, devkit_1.joinPathFragments)(projectConfig.root, 'tsconfig.json'));
|
|
45
|
-
return
|
|
50
|
+
return {
|
|
51
|
+
...options,
|
|
52
|
+
projectConfig,
|
|
53
|
+
offsetFromProjectRoot: `${offsetFromProjectRoot}/`,
|
|
54
|
+
hasTsConfig,
|
|
55
|
+
};
|
|
46
56
|
}
|