@nx/playwright 24.0.0-beta.3 → 24.0.0-beta.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.
@@ -21,7 +21,7 @@ const FALLBACK_RETRY_DELAY = 1_000;
21
21
  // nothing, so it must not replace what the previous probe saw.
22
22
  const NO_OBSERVATION = 'the deadline passed before the server responded';
23
23
  async function waitForWebserverExecutor(options, context) {
24
- const servers = options.servers ?? [];
24
+ const servers = withReadyUrl(options.servers ?? []);
25
25
  if (servers.length === 0) {
26
26
  devkit_1.logger.error('@nx/playwright:wait-for-webserver requires at least one server to wait for.');
27
27
  return { success: false };
@@ -49,6 +49,15 @@ async function waitForWebserverExecutor(options, context) {
49
49
  }
50
50
  }
51
51
  exports.default = waitForWebserverExecutor;
52
+ // The address the server task reported when it signaled readiness. It names no
53
+ // server, so it only moves the url of a gate waiting for exactly one.
54
+ function withReadyUrl(servers) {
55
+ const readyUrl = process.env.NX_READY_URL;
56
+ if (!readyUrl || servers.length !== 1 || servers[0].port != null) {
57
+ return servers;
58
+ }
59
+ return [{ ...servers[0], url: (0, devkit_1.getReadyUrl)(servers[0].url) }];
60
+ }
52
61
  // A malformed URL, an out-of-range port and a proxy that cannot be used are all
53
62
  // inputs that can never become valid, so they are rejected up front instead of
54
63
  // being retried until the timeout and reported as a slow server. An unusable
@@ -1,9 +1,10 @@
1
1
  import { defineConfig, devices } from '@playwright/test';
2
2
  import { nxE2EPreset } from '@nx/playwright/preset';
3
- <% if (webServerCommand && webServerAddress) { %>import { workspaceRoot } from '@nx/devkit';<% } %>
3
+ <% if (webServerCommand && webServerAddress) { %>import { getReadyUrl, workspaceRoot } from '@nx/devkit';<% } %>
4
4
 
5
5
  // For CI, you may want to set BASE_URL to the deployed application.
6
- const baseURL = process.env['BASE_URL'] || '<% if(webServerAddress) {%><%= webServerAddress %><% } else {%>http://localhost:3000<% } %>';
6
+ <% if (webServerCommand && webServerAddress) { %>// getReadyUrl moves the address to the port the dev server task reported.
7
+ const baseURL = process.env['BASE_URL'] || getReadyUrl('<%= webServerAddress %>');<% } else { %>const baseURL = process.env['BASE_URL'] || '<% if(webServerAddress) {%><%= webServerAddress %><% } else {%>http://localhost:3000<% } %>';<% } %>
7
8
 
8
9
  /**
9
10
  * Read environment variables from file.
@@ -32,7 +33,7 @@ export default defineConfig({
32
33
  /* Run your local dev server before starting the tests */<% if(webServerCommand && webServerAddress) {%>
33
34
  webServer: {
34
35
  command: '<%= webServerCommand %>',
35
- url: '<%= webServerAddress %>',
36
+ url: getReadyUrl('<%= webServerAddress %>'),
36
37
  reuseExistingServer: true,
37
38
  cwd: workspaceRoot
38
39
  },<% } else {%>
@@ -0,0 +1,4 @@
1
+ import { type Tree } from '@nx/devkit';
2
+ export default function update(tree: Tree): Promise<{
3
+ skipAgentic: boolean;
4
+ }>;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = update;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const pluginName = '@nx/playwright/plugin';
6
+ async function update(tree) {
7
+ const nxJson = (0, devkit_1.readNxJson)(tree);
8
+ if (!nxJson?.plugins) {
9
+ return { skipAgentic: true };
10
+ }
11
+ let changed = false;
12
+ nxJson.plugins = nxJson.plugins.map((plugin) => {
13
+ if (typeof plugin === 'string') {
14
+ if (plugin !== pluginName) {
15
+ return plugin;
16
+ }
17
+ changed = true;
18
+ return { plugin, options: { waitForServerReady: false } };
19
+ }
20
+ if (plugin.plugin !== pluginName) {
21
+ return plugin;
22
+ }
23
+ const options = (plugin.options ?? {});
24
+ if (options.waitForServerReady !== undefined) {
25
+ return plugin;
26
+ }
27
+ changed = true;
28
+ return { ...plugin, options: { ...options, waitForServerReady: false } };
29
+ });
30
+ if (!changed) {
31
+ return { skipAgentic: true };
32
+ }
33
+ (0, devkit_1.updateNxJson)(tree, nxJson);
34
+ await (0, devkit_1.formatFiles)(tree);
35
+ }
@@ -0,0 +1,40 @@
1
+ #### Keep Playwright e2e tasks from waiting for the dev server to be ready
2
+
3
+ `@nx/playwright/plugin` has a new `waitForServerReady` option, on by default. With it on, the inferred e2e tasks wait for the dev server task's `readyWhen` and get the address it reported in `NX_READY_URL`. Before, they only waited for the dev server task to start.
4
+
5
+ This migration sets `waitForServerReady: false` on every `@nx/playwright/plugin` entry in `nx.json`, so existing workspaces keep the old behavior. Entries that already set the option stay as they are. To opt in, remove the option or set it to `true`.
6
+
7
+ #### Sample code changes
8
+
9
+ ##### Before
10
+
11
+ ```json title="nx.json"
12
+ {
13
+ "plugins": [
14
+ "@nx/playwright/plugin",
15
+ {
16
+ "plugin": "@nx/playwright/plugin",
17
+ "include": ["apps/admin-e2e/**"],
18
+ "options": { "targetName": "e2e" }
19
+ }
20
+ ]
21
+ }
22
+ ```
23
+
24
+ ##### After
25
+
26
+ ```json title="nx.json"
27
+ {
28
+ "plugins": [
29
+ {
30
+ "plugin": "@nx/playwright/plugin",
31
+ "options": { "waitForServerReady": false }
32
+ },
33
+ {
34
+ "plugin": "@nx/playwright/plugin",
35
+ "include": ["apps/admin-e2e/**"],
36
+ "options": { "targetName": "e2e", "waitForServerReady": false }
37
+ }
38
+ ]
39
+ }
40
+ ```
@@ -16,6 +16,12 @@ export interface PlaywrightPluginOptions {
16
16
  * true.
17
17
  */
18
18
  waitForWebServer?: boolean;
19
+ /**
20
+ * Whether the inferred dependencies on the web server tasks wait for those
21
+ * tasks' `readyWhen` (`waitFor: 'ready'`) rather than for them to start.
22
+ * Defaults to true.
23
+ */
24
+ waitForServerReady?: boolean;
19
25
  }
20
26
  export declare const createNodes: CreateNodes<PlaywrightPluginOptions>;
21
27
  export declare const createNodesV2: CreateNodes<PlaywrightPluginOptions>;
@@ -205,7 +205,7 @@ async function buildPlaywrightTargets(configFilePath, projectRoot, options, cont
205
205
  },
206
206
  },
207
207
  };
208
- applyChainDependsOn(baseTargetConfig, e2eChain, e2eReadyTargetName);
208
+ applyChainDependsOn(baseTargetConfig, e2eChain, e2eReadyTargetName, options.waitForServerReady);
209
209
  targets[options.targetName] = {
210
210
  ...baseTargetConfig,
211
211
  cache: true,
@@ -219,10 +219,10 @@ async function buildPlaywrightTargets(configFilePath, projectRoot, options, cont
219
219
  outputs: getTargetOutputs(testOutput, reporterOutputs, context.workspaceRoot, projectRoot),
220
220
  };
221
221
  if (e2eReadyTargetName) {
222
- targets[e2eReadyTargetName] = buildWaitForWebserverTarget(e2eChain, options.webServerTimeout);
222
+ targets[e2eReadyTargetName] = buildWaitForWebserverTarget(e2eChain, options.webServerTimeout, options.waitForServerReady);
223
223
  }
224
224
  if (ciReadyTargetName && ciReadyTargetName !== e2eReadyTargetName) {
225
- targets[ciReadyTargetName] = buildWaitForWebserverTarget(ciChain, options.webServerTimeout);
225
+ targets[ciReadyTargetName] = buildWaitForWebserverTarget(ciChain, options.webServerTimeout, options.waitForServerReady);
226
226
  }
227
227
  if (options.ciTargetName) {
228
228
  // ensure the blob reporter output is the directory containing the blob
@@ -245,7 +245,7 @@ async function buildPlaywrightTargets(configFilePath, projectRoot, options, cont
245
245
  // Unconditionally: the chains can differ in ways the shared-gate check
246
246
  // deliberately ignores (an extra uncovered server flips parallelism), and
247
247
  // re-deriving is a no-op when they match.
248
- applyChainDependsOn(ciBaseTargetConfig, ciChain, ciReadyTargetName);
248
+ applyChainDependsOn(ciBaseTargetConfig, ciChain, ciReadyTargetName, options.waitForServerReady);
249
249
  const groupName = 'E2E (CI)';
250
250
  metadata = { targetGroups: { [groupName]: [] } };
251
251
  const ciTargetGroup = metadata.targetGroups[groupName];
@@ -401,6 +401,7 @@ function normalizeOptions(options) {
401
401
  ciTargetName,
402
402
  mergeReportsTargetName: `${ciTargetName}--merge-reports`,
403
403
  waitForWebServer: options?.waitForWebServer ?? true,
404
+ waitForServerReady: options?.waitForServerReady !== false,
404
405
  };
405
406
  }
406
407
  function getTestOutput(playwrightConfig) {
@@ -546,7 +547,7 @@ function sameChain(a, b) {
546
547
  return (JSON.stringify(a.commandTasks) === JSON.stringify(b.commandTasks) &&
547
548
  JSON.stringify(a.readinessServers) === JSON.stringify(b.readinessServers));
548
549
  }
549
- function buildWaitForWebserverTarget(chain, webServerTimeout) {
550
+ function buildWaitForWebserverTarget(chain, webServerTimeout, waitForServerReady) {
550
551
  return {
551
552
  executor: '@nx/playwright:wait-for-webserver',
552
553
  cache: false,
@@ -554,7 +555,7 @@ function buildWaitForWebserverTarget(chain, webServerTimeout) {
554
555
  servers: chain.readinessServers,
555
556
  ...(webServerTimeout != null ? { timeout: webServerTimeout } : {}),
556
557
  },
557
- dependsOn: getDependsOn(chain.commandTasks),
558
+ dependsOn: getDependsOn(chain.commandTasks, waitForServerReady),
558
559
  metadata: {
559
560
  technologies: ['playwright'],
560
561
  description: 'Waits for the E2E web server(s) to be ready before the Playwright test tasks run.',
@@ -693,11 +694,12 @@ function chainRequiresSerialization(chain) {
693
694
  // was inferred). Set together with parallelism so a config that inherits one
694
695
  // state (the atomized CI base copies the e2e config) cannot end up carrying
695
696
  // both.
696
- function applyChainDependsOn(targetConfig, chain, readyTargetName) {
697
+ function applyChainDependsOn(targetConfig, chain, readyTargetName, waitForServerReady) {
697
698
  if (chain.commandTasks.length) {
699
+ const serverDependsOn = getDependsOn(chain.commandTasks, waitForServerReady);
698
700
  targetConfig.dependsOn = readyTargetName
699
- ? [...getDependsOn(chain.commandTasks), { target: readyTargetName }]
700
- : getDependsOn(chain.commandTasks);
701
+ ? [...serverDependsOn, { target: readyTargetName }]
702
+ : serverDependsOn;
701
703
  }
702
704
  else {
703
705
  delete targetConfig.dependsOn;
@@ -709,7 +711,7 @@ function applyChainDependsOn(targetConfig, chain, readyTargetName) {
709
711
  delete targetConfig.parallelism;
710
712
  }
711
713
  }
712
- function getDependsOn(tasks) {
714
+ function getDependsOn(tasks, waitForServerReady) {
713
715
  const projectsPerTask = new Map();
714
716
  for (const { project, target } of tasks) {
715
717
  if (!projectsPerTask.has(target)) {
@@ -720,6 +722,7 @@ function getDependsOn(tasks) {
720
722
  return Array.from(projectsPerTask.entries()).map(([target, projects]) => ({
721
723
  projects,
722
724
  target,
725
+ ...(waitForServerReady ? { waitFor: 'ready' } : {}),
723
726
  }));
724
727
  }
725
728
  function normalizeOutput(path, workspaceRoot, projectRoot) {
package/migrations.json CHANGED
@@ -12,6 +12,12 @@
12
12
  "implementation": "./dist/src/migrations/update-24-0-0/remove-deprecated-executors",
13
13
  "prompt": "./dist/src/migrations/update-24-0-0/finish-inferred-conversion.md",
14
14
  "documentation": "./dist/src/migrations/update-24-0-0/remove-deprecated-executors.md"
15
+ },
16
+ "update-24-0-0-disable-wait-for-server-ready": {
17
+ "version": "24.0.0-beta.4",
18
+ "description": "Sets `waitForServerReady: false` on `@nx/playwright/plugin` entries in `nx.json` so existing Playwright e2e tasks keep starting without waiting for the dev server to report it is ready.",
19
+ "implementation": "./dist/src/migrations/update-24-0-0/disable-wait-for-server-ready",
20
+ "documentation": "./dist/src/migrations/update-24-0-0/disable-wait-for-server-ready.md"
15
21
  }
16
22
  }
17
23
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/playwright",
3
- "version": "24.0.0-beta.3",
3
+ "version": "24.0.0-beta.4",
4
4
  "private": false,
5
5
  "type": "commonjs",
6
6
  "files": [
@@ -75,16 +75,16 @@
75
75
  "supportsOptionalMigrations": true
76
76
  },
77
77
  "dependencies": {
78
- "@nx/devkit": "24.0.0-beta.3",
79
- "@nx/eslint": "24.0.0-beta.3",
80
- "@nx/js": "24.0.0-beta.3",
78
+ "@nx/devkit": "24.0.0-beta.4",
79
+ "@nx/eslint": "24.0.0-beta.4",
80
+ "@nx/js": "24.0.0-beta.4",
81
81
  "https-proxy-agent": "^7.0.6",
82
82
  "minimatch": "10.2.5",
83
83
  "semver": "^7.6.3",
84
84
  "tslib": "^2.3.0"
85
85
  },
86
86
  "devDependencies": {
87
- "nx": "24.0.0-beta.3"
87
+ "nx": "24.0.0-beta.4"
88
88
  },
89
89
  "peerDependencies": {
90
90
  "@playwright/test": "^1.36.0"