@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.
Files changed (57) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +9 -4
  3. package/generators.json +3 -3
  4. package/index.d.ts +2 -2
  5. package/index.js +4 -4
  6. package/migrations.json +24 -0
  7. package/package.json +6 -6
  8. package/plugin.d.ts +1 -0
  9. package/plugin.js +6 -0
  10. package/plugins/cypress-preset.d.ts +27 -19
  11. package/plugins/cypress-preset.js +102 -11
  12. package/src/executors/cypress/cypress.impl.d.ts +1 -1
  13. package/src/executors/cypress/cypress.impl.js +6 -152
  14. package/src/executors/cypress/schema.json +11 -1
  15. package/src/generators/base-setup/base-setup.d.ts +1 -0
  16. package/src/generators/base-setup/base-setup.js +26 -3
  17. package/src/generators/base-setup/files/config-js-cjs/__directory__/support/commands.js__ext__ +35 -0
  18. package/src/generators/base-setup/files/config-js-cjs/cypress.config.js__ext__ +3 -0
  19. package/src/generators/base-setup/files/config-js-esm/__directory__/support/commands.js__ext__ +35 -0
  20. package/src/generators/base-setup/files/config-ts/__directory__/support/commands.ts__ext__ +35 -0
  21. package/src/generators/base-setup/files/config-ts/cypress.config.ts__ext__ +3 -0
  22. package/src/generators/component-configuration/component-configuration.d.ts +3 -2
  23. package/src/generators/component-configuration/component-configuration.js +50 -27
  24. package/src/generators/component-configuration/files/__directory__/support/component-index.html +4 -1
  25. package/src/generators/component-configuration/schema.d.ts +7 -0
  26. package/src/generators/component-configuration/schema.json +1 -1
  27. package/src/generators/configuration/configuration.d.ts +4 -0
  28. package/src/generators/configuration/configuration.js +90 -26
  29. package/src/generators/configuration/schema.json +1 -1
  30. package/src/generators/cypress-project/cypress-project.js +25 -5
  31. package/src/generators/cypress-project/schema.d.ts +1 -0
  32. package/src/generators/cypress-project/schema.json +1 -1
  33. package/src/generators/init/init.d.ts +2 -0
  34. package/src/generators/init/init.js +71 -15
  35. package/src/generators/init/schema.d.ts +4 -2
  36. package/src/generators/init/schema.json +19 -1
  37. package/src/generators/migrate-to-cypress-11/schema.json +1 -1
  38. package/src/migrations/update-18-1-0/update-cypress-version-13-6-6.d.ts +2 -0
  39. package/src/migrations/update-18-1-0/update-cypress-version-13-6-6.js +12 -0
  40. package/src/plugins/plugin.d.ts +9 -0
  41. package/src/plugins/plugin.js +215 -0
  42. package/src/utils/add-linter.d.ts +1 -0
  43. package/src/utils/add-linter.js +4 -4
  44. package/src/utils/config.d.ts +6 -7
  45. package/src/utils/config.js +39 -12
  46. package/src/utils/constants.d.ts +4 -0
  47. package/src/utils/constants.js +7 -0
  48. package/src/utils/ct-helpers.d.ts +1 -1
  49. package/src/utils/ct-helpers.js +6 -2
  50. package/src/utils/start-dev-server.d.ts +9 -0
  51. package/src/utils/start-dev-server.js +157 -0
  52. package/src/utils/versions.d.ts +3 -3
  53. package/src/utils/versions.js +3 -3
  54. /package/src/generators/base-setup/files/{__directory__ → common/__directory__}/fixtures/example.json +0 -0
  55. /package/src/generators/base-setup/files/{__directory__ → common/__directory__}/support/commands.ts__ext__ +0 -0
  56. /package/src/generators/base-setup/files/{__directory__ → common/__directory__}/tsconfig.json__ext__ +0 -0
  57. /package/src/generators/base-setup/files/{cypress.config.ts__ext__ → config-js-esm/cypress.config.js__ext__} +0 -0
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  (The MIT License)
2
2
 
3
- Copyright (c) 2017-2023 Narwhal Technologies Inc.
3
+ Copyright (c) 2017-2024 Narwhal Technologies Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining
6
6
  a copy of this software and associated documentation files (the
package/README.md CHANGED
@@ -1,4 +1,9 @@
1
- <p style="text-align: center;"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx.png" width="600" alt="Nx - Smart, Fast and Extensible Build System"></p>
1
+ <p style="text-align: center;">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-dark.svg">
4
+ <img alt="Nx - Smart Monorepos · Fast CI" src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-light.svg" width="100%">
5
+ </picture>
6
+ </p>
2
7
 
3
8
  <div style="text-align: center;">
4
9
 
@@ -15,9 +20,9 @@
15
20
 
16
21
  <hr>
17
22
 
18
- # Nx: Smart, Fast and Extensible Build System
23
+ # Nx: Smart Monorepos · Fast CI
19
24
 
20
- Nx is a next generation build system with first class monorepo support and powerful integrations.
25
+ Nx is a build system with built-in tooling and advanced CI capabilities. It helps you maintain and scale monorepos, both locally and on CI.
21
26
 
22
27
  This package is a [Cypress plugin for Nx](https://nx.dev/cypress/overview).
23
28
 
@@ -59,5 +64,5 @@ npx nx@latest init
59
64
  - [Blog Posts About Nx](https://blog.nrwl.io/nx/home)
60
65
 
61
66
  <p style="text-align: center;"><a href="https://nx.dev/#learning-materials" target="_blank" rel="noreferrer"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-courses-and-videos.svg"
62
- width="100%" alt="Nx - Smart, Fast and Extensible Build System"></a></p>
67
+ width="100%" alt="Nx - Smart Monorepos · Fast CI"></a></p>
63
68
 
package/generators.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "version": "0.1",
4
4
  "generators": {
5
5
  "init": {
6
- "factory": "./src/generators/init/init#cypressInitGenerator",
6
+ "factory": "./src/generators/init/init#cypressInitGeneratorInternal",
7
7
  "schema": "./src/generators/init/schema.json",
8
8
  "description": "Initialize the `@nrwl/cypress` plugin.",
9
9
  "aliases": ["ng-add"],
@@ -17,13 +17,13 @@
17
17
  },
18
18
  "configuration": {
19
19
  "aliases": ["cypress-e2e-configuration", "e2e", "e2e-config"],
20
- "factory": "./src/generators/configuration/configuration",
20
+ "factory": "./src/generators/configuration/configuration#configurationGeneratorInternal",
21
21
  "schema": "./src/generators/configuration/schema.json",
22
22
  "description": "Add a Cypress E2E Configuration to an existing project."
23
23
  },
24
24
  "component-configuration": {
25
25
  "aliases": ["cypress-component-configuration"],
26
- "factory": "./src/generators/component-configuration/component-configuration",
26
+ "factory": "./src/generators/component-configuration/component-configuration#componentConfigurationGeneratorInternal",
27
27
  "schema": "./src/generators/component-configuration/schema.json",
28
28
  "description": "Set up Cypress Component Test for a project",
29
29
  "hidden": true
package/index.d.ts CHANGED
@@ -2,10 +2,10 @@ import { configurationGenerator } from './src/generators/configuration/configura
2
2
  import { componentConfigurationGenerator } from './src/generators/component-configuration/component-configuration';
3
3
  import { cypressProjectGenerator as _cypressProjectGenerator } from './src/generators/cypress-project/cypress-project';
4
4
  export { configurationGenerator, componentConfigurationGenerator };
5
- /** @deprecated Use `configurationGenerator` instead. It will be removed in Nx 18. */
5
+ /** @deprecated Use `configurationGenerator` instead. It will be removed in Nx 19. */
6
6
  export declare const cypressComponentConfiguration: typeof componentConfigurationGenerator;
7
7
  export { configurationGenerator as cypressE2EConfigurationGenerator };
8
- /** @deprecated Add a new project and call `configurationGenerator` instead. It will be removed in Nx 18. */
8
+ /** @deprecated Add a new project and call `configurationGenerator` instead. It will be removed in Nx 19. */
9
9
  export declare const cypressProjectGenerator: typeof _cypressProjectGenerator;
10
10
  export { cypressInitGenerator } from './src/generators/init/init';
11
11
  export { migrateCypressProject } from './src/generators/migrate-to-cypress-11/migrate-to-cypress-11';
package/index.js CHANGED
@@ -8,11 +8,11 @@ const component_configuration_1 = require("./src/generators/component-configurat
8
8
  Object.defineProperty(exports, "componentConfigurationGenerator", { enumerable: true, get: function () { return component_configuration_1.componentConfigurationGenerator; } });
9
9
  const cypress_project_1 = require("./src/generators/cypress-project/cypress-project");
10
10
  // Maintain backwards compatibility with the old names in case community plugins used them.
11
- // TODO(v18): Remove old name
12
- /** @deprecated Use `configurationGenerator` instead. It will be removed in Nx 18. */
11
+ // TODO(v19): Remove old name
12
+ /** @deprecated Use `configurationGenerator` instead. It will be removed in Nx 19. */
13
13
  exports.cypressComponentConfiguration = component_configuration_1.componentConfigurationGenerator;
14
- // TODO(v18): Remove project generator
15
- /** @deprecated Add a new project and call `configurationGenerator` instead. It will be removed in Nx 18. */
14
+ // TODO(v19): Remove project generator
15
+ /** @deprecated Add a new project and call `configurationGenerator` instead. It will be removed in Nx 19. */
16
16
  exports.cypressProjectGenerator = cypress_project_1.cypressProjectGenerator;
17
17
  var init_1 = require("./src/generators/init/init");
18
18
  Object.defineProperty(exports, "cypressInitGenerator", { enumerable: true, get: function () { return init_1.cypressInitGenerator; } });
package/migrations.json CHANGED
@@ -47,6 +47,12 @@
47
47
  "version": "16.8.0-beta.4",
48
48
  "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",
49
49
  "implementation": "./src/migrations/update-16-8-0/cypress-13"
50
+ },
51
+ "update-cypress-version-13-6-6": {
52
+ "cli": "nx",
53
+ "version": "18.1.0-beta.3",
54
+ "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.",
55
+ "implementation": "./src/migrations/update-18-1-0/update-cypress-version-13-6-6"
50
56
  }
51
57
  },
52
58
  "packageJsonUpdates": {
@@ -82,6 +88,24 @@
82
88
  "alwaysAddToPackageJson": false
83
89
  }
84
90
  }
91
+ },
92
+ "17.2.0-beta.2": {
93
+ "version": "17.2.0-beta.2",
94
+ "packages": {
95
+ "vite": {
96
+ "version": "^5.0.0",
97
+ "alwaysAddToPackageJson": false
98
+ }
99
+ }
100
+ },
101
+ "17.3.0": {
102
+ "version": "17.3.0-beta.3",
103
+ "packages": {
104
+ "@types/node": {
105
+ "version": "18.16.9",
106
+ "alwaysAddToPackageJson": false
107
+ }
108
+ }
85
109
  }
86
110
  }
87
111
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/cypress",
3
- "version": "17.0.3",
3
+ "version": "17.0.4",
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": "17.0.4",
38
+ "@nx/eslint": "17.0.4",
39
+ "@nx/js": "17.0.4",
37
40
  "@phenomnomnominal/tsquery": "~5.0.1",
38
41
  "detect-port": "^1.5.1",
39
- "semver": "7.5.3",
42
+ "semver": "^7.5.3",
40
43
  "tslib": "^2.3.0",
41
- "@nx/devkit": "17.0.3",
42
- "@nx/js": "17.0.3",
43
- "@nx/eslint": "17.0.3",
44
- "@nrwl/cypress": "17.0.3"
44
+ "@nrwl/cypress": "17.0.4"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "cypress": ">= 3 < 14"
package/plugin.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { createNodes, createDependencies } from './src/plugins/plugin';
package/plugin.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createDependencies = exports.createNodes = void 0;
4
+ var plugin_1 = require("./src/plugins/plugin");
5
+ Object.defineProperty(exports, "createNodes", { enumerable: true, get: function () { return plugin_1.createNodes; } });
6
+ Object.defineProperty(exports, "createDependencies", { enumerable: true, get: function () { return plugin_1.createDependencies; } });
@@ -10,7 +10,9 @@ export interface NxComponentTestingOptions {
10
10
  * @example 'component-test'
11
11
  */
12
12
  ctTargetName?: string;
13
+ buildTarget?: string;
13
14
  bundler?: 'vite' | 'webpack';
15
+ compiler?: 'swc' | 'babel';
14
16
  }
15
17
  export declare function nxBaseCypressPreset(pathToConfig: string, options?: {
16
18
  testingType: 'component' | 'e2e';
@@ -29,26 +31,19 @@ export declare function nxBaseCypressPreset(pathToConfig: string, options?: {
29
31
  * }
30
32
  * })
31
33
  *
32
- * @param pathToConfig will be used to construct the output paths for videos and screenshots
33
34
  */
34
- export declare function nxE2EPreset(pathToConfig: string, options?: NxCypressE2EPresetOptions): {
35
- fileServerFolder: string;
36
- supportFile: string;
37
- specPattern: string;
38
- fixturesFolder: string;
39
- videosFolder: string;
40
- screenshotsFolder: string;
41
- chromeWebSecurity: boolean;
42
- } | {
43
- setupNodeEvents(on: any): void;
44
- fileServerFolder: string;
45
- supportFile: string;
46
- specPattern: string;
47
- fixturesFolder: string;
48
- videosFolder: string;
49
- screenshotsFolder: string;
50
- chromeWebSecurity: boolean;
51
- };
35
+ export declare function nxE2EPreset(pathToConfig: string, options?: NxCypressE2EPresetOptions): any;
36
+ export interface WebServerConfig {
37
+ /**
38
+ * Timeout to wait for the webserver to start listening
39
+ */
40
+ timeout?: number;
41
+ /**
42
+ * Reuse an existing web server if it exists
43
+ * If this is false, an error will be thrown if the server is already running
44
+ */
45
+ reuseExistingServer?: boolean;
46
+ }
52
47
  export type NxCypressE2EPresetOptions = {
53
48
  bundler?: string;
54
49
  /**
@@ -57,5 +52,18 @@ export type NxCypressE2EPresetOptions = {
57
52
  * default is 'src'
58
53
  **/
59
54
  cypressDir?: string;
55
+ /**
56
+ * A map of commandName -> command to start the web server for testing.
57
+ * Currently only default is read.
58
+ */
59
+ webServerCommands?: Record<string, string>;
60
+ /**
61
+ * A command to start the web server - used for e2e tests distributed by Nx.
62
+ */
63
+ ciWebServerCommand?: string;
64
+ /**
65
+ * Configures how the web server command is started and monitored.
66
+ */
67
+ webServerConfig?: WebServerConfig;
60
68
  };
61
69
  export {};
@@ -5,6 +5,10 @@ const devkit_1 = require("@nx/devkit");
5
5
  const path_1 = require("path");
6
6
  const fs_1 = require("fs");
7
7
  const preprocessor_vite_1 = require("../src/plugins/preprocessor-vite");
8
+ const constants_1 = require("../src/utils/constants");
9
+ const child_process_1 = require("child_process");
10
+ const http_1 = require("http");
11
+ const https_1 = require("https");
8
12
  function nxBaseCypressPreset(pathToConfig, options) {
9
13
  // used to set babel settings for react CT.
10
14
  process.env.NX_CYPRESS_COMPONENT_TEST =
@@ -25,6 +29,26 @@ function nxBaseCypressPreset(pathToConfig, options) {
25
29
  };
26
30
  }
27
31
  exports.nxBaseCypressPreset = nxBaseCypressPreset;
32
+ function startWebServer(webServerCommand) {
33
+ const serverProcess = (0, child_process_1.spawn)(webServerCommand, {
34
+ cwd: devkit_1.workspaceRoot,
35
+ shell: true,
36
+ // Detaching the process on unix will create a process group, allowing us to kill it later
37
+ // Windows is fine so we leave it attached to this process
38
+ detached: process.platform !== 'win32',
39
+ stdio: 'inherit',
40
+ });
41
+ return () => {
42
+ if (process.platform === 'win32') {
43
+ serverProcess.kill();
44
+ }
45
+ else {
46
+ // child.kill() does not work on linux
47
+ // process.kill will kill the whole process group on unix
48
+ process.kill(-serverProcess.pid, 'SIGKILL');
49
+ }
50
+ };
51
+ }
28
52
  /**
29
53
  * nx E2E Preset for Cypress
30
54
  * @description
@@ -39,25 +63,92 @@ exports.nxBaseCypressPreset = nxBaseCypressPreset;
39
63
  * }
40
64
  * })
41
65
  *
42
- * @param pathToConfig will be used to construct the output paths for videos and screenshots
43
66
  */
44
67
  function nxE2EPreset(pathToConfig, options) {
45
68
  const basePath = options?.cypressDir || 'src';
46
- const baseConfig = {
69
+ const baseConfig /*Cypress.EndToEndConfigOptions & {
70
+ [NX_PLUGIN_OPTIONS]: unknown;
71
+ }*/ = {
47
72
  ...nxBaseCypressPreset(pathToConfig),
48
73
  fileServerFolder: '.',
49
- supportFile: `${basePath}/support/e2e.ts`,
74
+ supportFile: `${basePath}/support/e2e.{js,ts}`,
50
75
  specPattern: `${basePath}/**/*.cy.{js,jsx,ts,tsx}`,
51
76
  fixturesFolder: `${basePath}/fixtures`,
52
- };
53
- if (options?.bundler === 'vite') {
54
- return {
55
- ...baseConfig,
56
- setupNodeEvents(on) {
77
+ [constants_1.NX_PLUGIN_OPTIONS]: {
78
+ webServerCommand: options?.webServerCommands?.default,
79
+ webServerCommands: options?.webServerCommands,
80
+ ciWebServerCommand: options?.ciWebServerCommand,
81
+ },
82
+ async setupNodeEvents(on, config) {
83
+ const webServerCommands = config.env?.webServerCommands ?? options?.webServerCommands;
84
+ const webServerCommand = config.env?.webServerCommand ?? webServerCommands?.default;
85
+ if (options?.bundler === 'vite') {
57
86
  on('file:preprocessor', (0, preprocessor_vite_1.default)());
58
- },
59
- };
60
- }
87
+ }
88
+ if (!options?.webServerCommands) {
89
+ return;
90
+ }
91
+ if (!webServerCommand) {
92
+ return;
93
+ }
94
+ if (config.baseUrl && webServerCommand) {
95
+ if (await isServerUp(config.baseUrl)) {
96
+ if (options?.webServerConfig?.reuseExistingServer === undefined
97
+ ? true
98
+ : options.webServerConfig.reuseExistingServer) {
99
+ console.log(`Reusing the server already running on ${config.baseUrl}`);
100
+ return;
101
+ }
102
+ else {
103
+ throw new Error(`Web server is already running at ${config.baseUrl}`);
104
+ }
105
+ }
106
+ const killWebServer = startWebServer(webServerCommand);
107
+ on('after:run', () => {
108
+ killWebServer();
109
+ });
110
+ await waitForServer(config.baseUrl, options.webServerConfig);
111
+ }
112
+ },
113
+ };
61
114
  return baseConfig;
62
115
  }
63
116
  exports.nxE2EPreset = nxE2EPreset;
117
+ function waitForServer(url, webServerConfig) {
118
+ return new Promise((resolve, reject) => {
119
+ let pollTimeout;
120
+ const { protocol } = new URL(url);
121
+ const timeoutDuration = webServerConfig?.timeout ?? 60 * 1000;
122
+ const timeout = setTimeout(() => {
123
+ clearTimeout(pollTimeout);
124
+ reject(new Error(`Web server failed to start in ${timeoutDuration}ms. This can be configured in cypress.config.ts.`));
125
+ }, timeoutDuration);
126
+ const makeRequest = protocol === 'https:' ? https_1.request : http_1.request;
127
+ function pollForServer() {
128
+ const request = makeRequest(url, () => {
129
+ clearTimeout(timeout);
130
+ resolve();
131
+ });
132
+ request.on('error', () => {
133
+ pollTimeout = setTimeout(pollForServer, 100);
134
+ });
135
+ // Don't forget to end the request
136
+ request.end();
137
+ }
138
+ pollForServer();
139
+ });
140
+ }
141
+ function isServerUp(url) {
142
+ const { protocol } = new URL(url);
143
+ const makeRequest = protocol === 'https:' ? https_1.request : http_1.request;
144
+ return new Promise((resolve) => {
145
+ const request = makeRequest(url, () => {
146
+ resolve(true);
147
+ });
148
+ request.on('error', () => {
149
+ resolve(false);
150
+ });
151
+ // Don't forget to end the request
152
+ request.end();
153
+ });
154
+ }
@@ -25,7 +25,7 @@ export interface CypressExecutorOptions extends Json {
25
25
  copyFiles?: string;
26
26
  ciBuildId?: string | number;
27
27
  group?: string;
28
- ignoreTestFiles?: string;
28
+ ignoreTestFiles?: string | string[];
29
29
  reporter?: string;
30
30
  reporterOptions?: string | Json;
31
31
  skipServe?: boolean;
@@ -1,19 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const devkit_1 = require("@nx/devkit");
4
- const executor_utils_1 = require("nx/src/command-line/run/executor-utils");
5
4
  const fs_1 = require("fs");
6
5
  const path_1 = require("path");
7
6
  const ct_helpers_1 = require("../../utils/ct-helpers");
8
7
  const cypress_version_1 = require("../../utils/cypress-version");
9
- const detectPort = require("detect-port");
8
+ const start_dev_server_1 = require("../../utils/start-dev-server");
10
9
  const Cypress = require('cypress'); // @NOTE: Importing via ES6 messes the whole test dependencies.
11
10
  async function cypressExecutor(options, context) {
12
11
  options = normalizeOptions(options, context);
13
12
  // this is used by cypress component testing presets to build the executor contexts with the correct configuration options.
14
13
  process.env.NX_CYPRESS_TARGET_CONFIGURATION = context.configurationName;
15
14
  let success;
16
- for await (const devServerValues of startDevServer(options, context)) {
15
+ for await (const devServerValues of (0, start_dev_server_1.startDevServer)(options, context)) {
17
16
  try {
18
17
  success = await runCypress(devServerValues.baseUrl, {
19
18
  ...options,
@@ -90,50 +89,6 @@ A generator to migrate from v8 to v10 is provided. See https://nx.dev/cypress/v1
90
89
  `);
91
90
  }
92
91
  }
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);
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.
118
- If the port is in use, try using a different port value or passing --port='cypress-auto' to find a free port.`);
119
- }
120
- }
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}`;
130
- }
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
- }
136
- }
137
92
  /**
138
93
  * @whatItDoes Initialize the Cypress test runner with the provided project configuration.
139
94
  * By default, Cypress will run tests from the CLI without the GUI and provide directly the results in the console output.
@@ -155,7 +110,10 @@ async function runCypress(baseUrl, opts) {
155
110
  options.browser = opts.browser;
156
111
  }
157
112
  if (opts.env) {
158
- options.env = opts.env;
113
+ options.env = {
114
+ ...options.env,
115
+ ...opts.env,
116
+ };
159
117
  }
160
118
  if (opts.spec) {
161
119
  options.spec = opts.spec;
@@ -211,110 +169,6 @@ async function runCypress(baseUrl, opts) {
211
169
  */
212
170
  return !result.totalFailed && !result.failures;
213
171
  }
214
- /**
215
- * try to find a free port for the project to run on
216
- * will return undefined if no port is found or the project doesn't have a port option
217
- **/
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;
227
- }
228
- else {
229
- normalizedPortValue = Number(schemaPortValue);
230
- }
231
- if (isNaN(normalizedPortValue)) {
232
- devkit_1.output.warn({
233
- title: `Port Not a Number`,
234
- bodyLines: [
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`,
239
- ],
240
- });
241
- normalizedPortValue = defaultPort;
242
- }
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;
274
- }
275
- /**
276
- * Check if the given target has the given property in it's options.
277
- * if the property is does not have a default value or is not in the actual executor options,
278
- * the value will be undefined even if it's in the executor schema.
279
- **/
280
- function getValueFromSchema(context, target, property) {
281
- let targetOpts;
282
- try {
283
- targetOpts = (0, devkit_1.readTargetOptions)(target, context);
284
- }
285
- catch (e) {
286
- throw new Error(`Unable to read the target options for ${(0, devkit_1.targetToTargetString)(target)}.
287
- Are you sure this is a valid target?
288
- Was trying to read the target for the property: '${property}', but got the following error:
289
- ${e.message || e}`);
290
- }
291
- let targetHasOpt = Object.keys(targetOpts).includes(property);
292
- if (!targetHasOpt) {
293
- // NOTE: readTargetOptions doesn't apply non defaulted values, i.e. @nx/vite has a port options but is optional
294
- // so we double check the schema if readTargetOptions didn't return a value for the property
295
- const projectConfig = context.projectsConfigurations?.projects?.[target.project];
296
- const targetConfig = projectConfig.targets[target.target];
297
- const [collection, executor] = targetConfig.executor.split(':');
298
- const { schema } = (0, executor_utils_1.getExecutorInformation)(collection, executor, context.root);
299
- // NOTE: schema won't have a default since readTargetOptions would have
300
- // already set that and this check wouldn't need to be made
301
- targetHasOpt = Object.keys(schema.properties).includes(property);
302
- }
303
- return [targetHasOpt, targetOpts[property]];
304
- }
305
- function attemptToLockPort(port) {
306
- const portLockFilePath = (0, path_1.join)(__dirname, `${port}.txt`);
307
- try {
308
- if ((0, fs_1.existsSync)(portLockFilePath)) {
309
- return false;
310
- }
311
- (0, fs_1.writeFileSync)(portLockFilePath, 'locked');
312
- return true;
313
- }
314
- catch (err) {
315
- return false;
316
- }
317
- }
318
172
  function cleanupTmpFile(path) {
319
173
  try {
320
174
  if (path && (0, fs_1.existsSync)(path)) {
@@ -113,7 +113,17 @@
113
113
  },
114
114
  "ignoreTestFiles": {
115
115
  "aliases": ["excludeSpecPattern"],
116
- "type": "string",
116
+ "oneOf": [
117
+ {
118
+ "type": "string"
119
+ },
120
+ {
121
+ "type": "array",
122
+ "items": {
123
+ "type": "string"
124
+ }
125
+ }
126
+ ],
117
127
  "description": "A String or Array of glob patterns used to ignore test files that would otherwise be shown in your list of tests. Cypress uses minimatch with the options: `{dot: true, matchBase: true}`. We suggest using https://globster.xyz to test what files would match."
118
128
  },
119
129
  "reporter": {
@@ -6,6 +6,7 @@ export interface CypressBaseSetupSchema {
6
6
  * default is `cypress`
7
7
  * */
8
8
  directory?: string;
9
+ js?: boolean;
9
10
  jsx?: boolean;
10
11
  }
11
12
  export declare function addBaseCypressSetup(tree: Tree, options: CypressBaseSetupSchema): void;