@rpcbase/test 0.342.0 → 0.344.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.
Files changed (43) hide show
  1. package/dist/clearDatabase.js +10 -12
  2. package/dist/clearDatabase.js.map +1 -1
  3. package/dist/cli.js +439 -556
  4. package/dist/cli.js.map +1 -1
  5. package/dist/coverage/collect.js +63 -101
  6. package/dist/coverage/collect.js.map +1 -1
  7. package/dist/coverage/config-loader.js +180 -230
  8. package/dist/coverage/config-loader.js.map +1 -1
  9. package/dist/coverage/config.js +76 -100
  10. package/dist/coverage/config.js.map +1 -1
  11. package/dist/coverage/console-text-report.js +175 -220
  12. package/dist/coverage/console-text-report.js.map +1 -1
  13. package/dist/coverage/files.js +45 -58
  14. package/dist/coverage/files.js.map +1 -1
  15. package/dist/coverage/fixtures.js +27 -38
  16. package/dist/coverage/fixtures.js.map +1 -1
  17. package/dist/coverage/global-setup.js +15 -18
  18. package/dist/coverage/global-setup.js.map +1 -1
  19. package/dist/coverage/index.js +38 -55
  20. package/dist/coverage/index.js.map +1 -1
  21. package/dist/coverage/report.js +341 -466
  22. package/dist/coverage/report.js.map +1 -1
  23. package/dist/coverage/reporter.js +47 -61
  24. package/dist/coverage/reporter.js.map +1 -1
  25. package/dist/coverage/v8-tracker.js +115 -147
  26. package/dist/coverage/v8-tracker.js.map +1 -1
  27. package/dist/index.js +46 -75
  28. package/dist/index.js.map +1 -1
  29. package/dist/runners/playwright.d.ts.map +1 -1
  30. package/dist/runners/playwright.js +398 -449
  31. package/dist/runners/playwright.js.map +1 -1
  32. package/dist/runners/process.d.ts +4 -1
  33. package/dist/runners/process.d.ts.map +1 -1
  34. package/dist/runners/process.js +111 -101
  35. package/dist/runners/process.js.map +1 -1
  36. package/dist/runners/vitest.js +124 -171
  37. package/dist/runners/vitest.js.map +1 -1
  38. package/dist/serverCoverage.js +28 -42
  39. package/dist/serverCoverage.js.map +1 -1
  40. package/dist/vitest.config.d.ts +1 -1
  41. package/dist/vitest.config.js +62 -74
  42. package/dist/vitest.config.js.map +1 -1
  43. package/package.json +1 -1
@@ -1,494 +1,443 @@
1
+ import { spawnAndCaptureStdout, spawnWithLogs, withRegisterShim } from "./process.js";
2
+ import fsPromises from "node:fs/promises";
1
3
  import fs from "node:fs";
2
- import fs$1 from "node:fs/promises";
3
4
  import path from "node:path";
4
5
  import { createRequire } from "node:module";
5
6
  import { fileURLToPath } from "node:url";
6
- import { withRegisterShim, spawnWithLogs, spawnAndCaptureStdout } from "./process.js";
7
- const require$1 = createRequire(import.meta.url);
8
- const moduleDir = path.dirname(fileURLToPath(import.meta.url));
9
- const COMBINED_COVERAGE_ENV_VAR = "RB_TEST_COMBINED_COVERAGE";
10
- const PLAYWRIGHT_RESULTS_FILE = path.join(process.cwd(), "build", "playwright", "results.json");
11
- const PLAYWRIGHT_OPTIONS_WITH_REQUIRED_VALUE = /* @__PURE__ */ new Set(["--browser", "--config", "-c", "--grep", "-g", "--grep-invert", "--global-timeout", "--max-failures", "--output", "--project", "--repeat-each", "--reporter", "--retries", "--shard", "--timeout", "--trace", "--tsconfig", "--workers", "-j"]);
12
- const PLAYWRIGHT_OPTIONS_WITH_OPTIONAL_VALUE = /* @__PURE__ */ new Set(["--only-changed", "--update-snapshots", "-u"]);
13
- const PLAYWRIGHT_OPTIONS_TO_REMOVE_FOR_WORKER_RETRY = /* @__PURE__ */ new Set(["--last-failed", "--only-changed", "--retries", "--pass-with-no-tests"]);
14
- const PLAYWRIGHT_OPTIONS_TO_REMOVE_FOR_INITIAL_FRESH_RUN = /* @__PURE__ */ new Set(["--retries"]);
15
- async function runPlaywright(userArgs, {
16
- disableCoverage = false
17
- } = {}) {
18
- const configPath = fs.existsSync(path.join(process.cwd(), "playwright.config.ts")) ? path.join(process.cwd(), "playwright.config.ts") : path.join(moduleDir, "..", "playwright.config.ts");
19
- const hasCustomConfig = userArgs.some((arg) => {
20
- if (arg === "--config" || arg === "-c") {
21
- return true;
22
- }
23
- return arg.startsWith("--config=");
24
- });
25
- ensureJsxRuntimeShim(process.cwd());
26
- const launcher = resolvePlaywrightLauncher();
27
- const env = withRegisterShim(process.env);
28
- env[COMBINED_COVERAGE_ENV_VAR] = "1";
29
- if (disableCoverage) {
30
- env.RB_DISABLE_COVERAGE = "1";
31
- }
32
- const configuredRetries = await resolveFreshWorkerRetryBudget({
33
- launcher,
34
- configPath,
35
- hasCustomConfig,
36
- userArgs,
37
- env
38
- });
39
- const resultsMtimeBeforeRun = getFileMtimeMs(PLAYWRIGHT_RESULTS_FILE);
40
- const initialRunArgs = buildPlaywrightInitialFreshWorkerArgs(userArgs);
41
- const initialPlaywrightArgs = ["test"];
42
- if (!hasCustomConfig) {
43
- initialPlaywrightArgs.push("--config", configPath);
44
- }
45
- initialPlaywrightArgs.push(...initialRunArgs);
46
- try {
47
- await runPlaywrightOnce({
48
- launcher,
49
- args: initialPlaywrightArgs,
50
- env
51
- });
52
- return;
53
- } catch (error) {
54
- const summary = await readPlaywrightReportSummary(PLAYWRIGHT_RESULTS_FILE, resultsMtimeBeforeRun);
55
- if (!summary || configuredRetries <= 0 || summary.failedFiles.length === 0) {
56
- throw error;
57
- }
58
- let lastError = error;
59
- let failedFiles = summary.failedFiles;
60
- const maxAttempts = configuredRetries;
61
- for (let attempt = 1; attempt <= maxAttempts && failedFiles.length > 0; attempt += 1) {
62
- console.warn(`[rb-test] Retrying ${failedFiles.length} failed Playwright file(s) with fresh workers (${attempt}/${maxAttempts})`);
63
- const nextFailedFiles = [];
64
- for (const failedFile of failedFiles) {
65
- const retryArgs = buildPlaywrightWorkerRetryArgs(userArgs, [failedFile]);
66
- const retryPlaywrightArgs = ["test"];
67
- if (!hasCustomConfig) {
68
- retryPlaywrightArgs.push("--config", configPath);
69
- }
70
- retryPlaywrightArgs.push(...retryArgs);
71
- try {
72
- await runPlaywrightOnce({
73
- launcher,
74
- args: retryPlaywrightArgs,
75
- env
76
- });
77
- } catch (retryError) {
78
- lastError = retryError;
79
- nextFailedFiles.push(failedFile);
80
- }
81
- }
82
- failedFiles = nextFailedFiles;
83
- }
84
- if (failedFiles.length === 0) {
85
- return;
86
- }
87
- throw lastError;
88
- }
7
+ //#region src/runners/playwright.ts
8
+ var require = createRequire(import.meta.url);
9
+ var moduleDir = path.dirname(fileURLToPath(import.meta.url));
10
+ var COMBINED_COVERAGE_ENV_VAR = "RB_TEST_COMBINED_COVERAGE";
11
+ var PLAYWRIGHT_RESULTS_FILE = path.join(process.cwd(), "build", "playwright", "results.json");
12
+ var PLAYWRIGHT_OPTIONS_WITH_REQUIRED_VALUE = new Set([
13
+ "--browser",
14
+ "--config",
15
+ "-c",
16
+ "--grep",
17
+ "-g",
18
+ "--grep-invert",
19
+ "--global-timeout",
20
+ "--max-failures",
21
+ "--output",
22
+ "--project",
23
+ "--repeat-each",
24
+ "--reporter",
25
+ "--retries",
26
+ "--shard",
27
+ "--timeout",
28
+ "--trace",
29
+ "--tsconfig",
30
+ "--workers",
31
+ "-j"
32
+ ]);
33
+ var PLAYWRIGHT_OPTIONS_WITH_OPTIONAL_VALUE = new Set([
34
+ "--only-changed",
35
+ "--update-snapshots",
36
+ "-u"
37
+ ]);
38
+ var PLAYWRIGHT_OPTIONS_TO_REMOVE_FOR_WORKER_RETRY = new Set([
39
+ "--last-failed",
40
+ "--only-changed",
41
+ "--output",
42
+ "--retries",
43
+ "--pass-with-no-tests",
44
+ "--reporter",
45
+ "--workers",
46
+ "-j"
47
+ ]);
48
+ var PLAYWRIGHT_OPTIONS_TO_REMOVE_FOR_INITIAL_FRESH_RUN = new Set(["--retries"]);
49
+ async function runPlaywright(userArgs, { disableCoverage = false } = {}) {
50
+ const configPath = fs.existsSync(path.join(process.cwd(), "playwright.config.ts")) ? path.join(process.cwd(), "playwright.config.ts") : path.join(moduleDir, "..", "playwright.config.ts");
51
+ const hasCustomConfig = userArgs.some((arg) => {
52
+ if (arg === "--config" || arg === "-c") return true;
53
+ return arg.startsWith("--config=");
54
+ });
55
+ ensureJsxRuntimeShim(process.cwd());
56
+ const launcher = resolvePlaywrightLauncher();
57
+ const env = withRegisterShim(process.env);
58
+ env[COMBINED_COVERAGE_ENV_VAR] = "1";
59
+ if (disableCoverage) env.RB_DISABLE_COVERAGE = "1";
60
+ const runtimeSettings = await resolveFreshWorkerRetrySettings({
61
+ launcher,
62
+ configPath,
63
+ hasCustomConfig,
64
+ userArgs,
65
+ env
66
+ });
67
+ const configuredRetries = runtimeSettings.retries;
68
+ const retryConcurrency = Math.max(1, runtimeSettings.workers ?? 1);
69
+ const resultsMtimeBeforeRun = getFileMtimeMs(PLAYWRIGHT_RESULTS_FILE);
70
+ const initialRunArgs = buildPlaywrightInitialFreshWorkerArgs(userArgs);
71
+ const initialPlaywrightArgs = ["test"];
72
+ if (!hasCustomConfig) initialPlaywrightArgs.push("--config", configPath);
73
+ initialPlaywrightArgs.push(...initialRunArgs);
74
+ try {
75
+ await runPlaywrightOnce({
76
+ launcher,
77
+ args: initialPlaywrightArgs,
78
+ env
79
+ });
80
+ return;
81
+ } catch (error) {
82
+ const summary = await readPlaywrightReportSummary(PLAYWRIGHT_RESULTS_FILE, resultsMtimeBeforeRun);
83
+ if (!summary || configuredRetries <= 0 || summary.failedFiles.length === 0) throw error;
84
+ let lastError = error;
85
+ let failedFiles = summary.failedFiles;
86
+ const maxAttempts = configuredRetries;
87
+ for (let attempt = 1; attempt <= maxAttempts && failedFiles.length > 0; attempt += 1) {
88
+ const activeRetryWorkers = Math.max(1, Math.min(retryConcurrency, failedFiles.length));
89
+ console.warn(`[rb-test] Retrying ${failedFiles.length} failed Playwright file(s) with fresh workers (${attempt}/${maxAttempts}, up to ${activeRetryWorkers} in parallel)`);
90
+ const retryResults = await runPlaywrightRetryAttempt({
91
+ launcher,
92
+ userArgs,
93
+ failedFiles,
94
+ retryConcurrency: activeRetryWorkers,
95
+ configPath,
96
+ hasCustomConfig,
97
+ env,
98
+ attempt
99
+ });
100
+ if (retryResults.lastError) lastError = retryResults.lastError;
101
+ failedFiles = retryResults.failedFiles;
102
+ }
103
+ if (failedFiles.length === 0) return;
104
+ throw lastError;
105
+ }
89
106
  }
90
- function runPlaywrightOnce({
91
- launcher,
92
- args,
93
- env
94
- }) {
95
- return spawnWithLogs({
96
- name: "Playwright",
97
- launcher,
98
- args,
99
- env,
100
- successMessage: "Playwright suite passed!",
101
- failureMessage: "Playwright failed"
102
- });
107
+ function runPlaywrightOnce({ launcher, args, env, outputMode = "chunk", successMessage, failureMessage }) {
108
+ return spawnWithLogs({
109
+ name: "Playwright",
110
+ launcher,
111
+ args,
112
+ env,
113
+ outputMode,
114
+ successMessage: successMessage === void 0 ? "Playwright suite passed!" : successMessage ?? void 0,
115
+ failureMessage: failureMessage === void 0 ? "Playwright failed" : failureMessage ?? void 0
116
+ });
103
117
  }
104
118
  function resolvePlaywrightLauncher() {
105
- const cliPath = resolveCliPath();
106
- if (cliPath) {
107
- return {
108
- command: process.execPath,
109
- args: [cliPath]
110
- };
111
- }
112
- const localBin = path.resolve(process.cwd(), "node_modules/.bin/playwright");
113
- if (fs.existsSync(localBin)) {
114
- return {
115
- command: localBin,
116
- args: []
117
- };
118
- }
119
- return {
120
- command: "playwright",
121
- args: []
122
- };
119
+ const cliPath = resolveCliPath();
120
+ if (cliPath) return {
121
+ command: process.execPath,
122
+ args: [cliPath]
123
+ };
124
+ const localBin = path.resolve(process.cwd(), "node_modules/.bin/playwright");
125
+ if (fs.existsSync(localBin)) return {
126
+ command: localBin,
127
+ args: []
128
+ };
129
+ return {
130
+ command: "playwright",
131
+ args: []
132
+ };
123
133
  }
124
134
  function resolveCliPath() {
125
- const searchRoots = [process.cwd(), moduleDir];
126
- for (const base of searchRoots) {
127
- try {
128
- const pkgPath = require$1.resolve("@playwright/test/package.json", {
129
- paths: [base]
130
- });
131
- const cliPath = path.join(path.dirname(pkgPath), "cli.js");
132
- if (fs.existsSync(cliPath)) {
133
- return cliPath;
134
- }
135
- } catch (_error) {
136
- }
137
- }
138
- return null;
135
+ const searchRoots = [process.cwd(), moduleDir];
136
+ for (const base of searchRoots) try {
137
+ const pkgPath = require.resolve("@playwright/test/package.json", { paths: [base] });
138
+ const cliPath = path.join(path.dirname(pkgPath), "cli.js");
139
+ if (fs.existsSync(cliPath)) return cliPath;
140
+ } catch (_error) {}
141
+ return null;
139
142
  }
140
- function buildPlaywrightWorkerRetryArgs(userArgs, failedFiles) {
141
- const optionArgs = extractPlaywrightOptionArgs(userArgs);
142
- const sanitizedOptions = removePlaywrightOptions(optionArgs, PLAYWRIGHT_OPTIONS_TO_REMOVE_FOR_WORKER_RETRY);
143
- return [...sanitizedOptions, "--retries=0", ...failedFiles];
143
+ function buildPlaywrightWorkerRetryArgs(userArgs, failedFiles, attempt) {
144
+ return [
145
+ ...removePlaywrightOptions(extractPlaywrightOptionArgs(userArgs), PLAYWRIGHT_OPTIONS_TO_REMOVE_FOR_WORKER_RETRY),
146
+ "--retries=0",
147
+ "--workers=1",
148
+ "--reporter=list",
149
+ "--output",
150
+ buildPlaywrightRetryOutputDir(attempt, failedFiles),
151
+ ...failedFiles
152
+ ];
144
153
  }
145
154
  function buildPlaywrightInitialFreshWorkerArgs(userArgs) {
146
- const optionArgs = extractPlaywrightOptionArgs(userArgs);
147
- const sanitizedOptions = removePlaywrightOptions(optionArgs, PLAYWRIGHT_OPTIONS_TO_REMOVE_FOR_INITIAL_FRESH_RUN);
148
- const positionalArgs = extractPlaywrightPositionalArgs(userArgs);
149
- return [...sanitizedOptions, "--retries=0", ...positionalArgs];
155
+ const sanitizedOptions = removePlaywrightOptions(extractPlaywrightOptionArgs(userArgs), PLAYWRIGHT_OPTIONS_TO_REMOVE_FOR_INITIAL_FRESH_RUN);
156
+ const positionalArgs = extractPlaywrightPositionalArgs(userArgs);
157
+ return [
158
+ ...sanitizedOptions,
159
+ "--retries=0",
160
+ ...positionalArgs
161
+ ];
150
162
  }
151
- async function resolveFreshWorkerRetryBudget({
152
- launcher,
153
- configPath,
154
- hasCustomConfig,
155
- userArgs,
156
- env
157
- }) {
158
- const cliRetries = resolveRetriesOverrideFromCli(userArgs);
159
- if (cliRetries != null) {
160
- return cliRetries;
161
- }
162
- const configuredRetries = await probeConfiguredPlaywrightRetries({
163
- launcher,
164
- configPath,
165
- hasCustomConfig,
166
- userArgs,
167
- env
168
- });
169
- if (configuredRetries == null) {
170
- return 0;
171
- }
172
- return configuredRetries;
163
+ async function resolveFreshWorkerRetrySettings({ launcher, configPath, hasCustomConfig, userArgs, env }) {
164
+ const configuredSettings = await probeConfiguredPlaywrightSettings({
165
+ launcher,
166
+ configPath,
167
+ hasCustomConfig,
168
+ userArgs,
169
+ env
170
+ });
171
+ return {
172
+ retries: configuredSettings.retries ?? 0,
173
+ workers: configuredSettings.workers
174
+ };
173
175
  }
174
- function resolveRetriesOverrideFromCli(userArgs) {
175
- for (let i = userArgs.length - 1; i >= 0; i -= 1) {
176
- const arg = userArgs[i];
177
- if (arg.startsWith("--retries=")) {
178
- const value = Number(arg.slice("--retries=".length));
179
- if (Number.isFinite(value)) {
180
- return Math.max(0, Math.floor(value));
181
- }
182
- return null;
183
- }
184
- if (arg === "--retries") {
185
- const valueArg = userArgs[i + 1];
186
- const value = Number(valueArg);
187
- if (valueArg != null && Number.isFinite(value)) {
188
- return Math.max(0, Math.floor(value));
189
- }
190
- return null;
191
- }
192
- }
193
- return null;
194
- }
195
- async function probeConfiguredPlaywrightRetries({
196
- launcher,
197
- configPath,
198
- hasCustomConfig,
199
- userArgs,
200
- env
201
- }) {
202
- const probeArgs = ["test"];
203
- if (!hasCustomConfig) {
204
- probeArgs.push("--config", configPath);
205
- }
206
- probeArgs.push("--list", "--reporter=json", ...extractPlaywrightProjectArgs(userArgs));
207
- try {
208
- const stdout = await spawnAndCaptureStdout({
209
- name: "Playwright retries probe",
210
- launcher,
211
- args: probeArgs,
212
- env
213
- });
214
- const json = parseJsonObjectFromText(stdout);
215
- if (!json) {
216
- return null;
217
- }
218
- return resolvePlaywrightRetriesFromReport(json);
219
- } catch (_error) {
220
- return null;
221
- }
176
+ async function probeConfiguredPlaywrightSettings({ launcher, configPath, hasCustomConfig, userArgs, env }) {
177
+ const probeArgs = ["test"];
178
+ if (!hasCustomConfig) probeArgs.push("--config", configPath);
179
+ probeArgs.push("--list", "--reporter=json", ...extractPlaywrightProjectArgs(userArgs));
180
+ try {
181
+ const json = parseJsonObjectFromText(await spawnAndCaptureStdout({
182
+ name: "Playwright retries probe",
183
+ launcher,
184
+ args: probeArgs,
185
+ env
186
+ }));
187
+ if (!json) return {
188
+ retries: null,
189
+ workers: null
190
+ };
191
+ return {
192
+ retries: resolvePlaywrightRetriesFromReport(json),
193
+ workers: resolvePlaywrightWorkersFromReport(json)
194
+ };
195
+ } catch (_error) {
196
+ return {
197
+ retries: null,
198
+ workers: null
199
+ };
200
+ }
222
201
  }
223
202
  function extractPlaywrightProjectArgs(userArgs) {
224
- const output = [];
225
- for (let i = 0; i < userArgs.length; i += 1) {
226
- const arg = userArgs[i];
227
- if (arg === "--") {
228
- break;
229
- }
230
- if (arg === "--project") {
231
- const next = userArgs[i + 1];
232
- if (next != null) {
233
- output.push(arg, next);
234
- i += 1;
235
- }
236
- continue;
237
- }
238
- if (arg.startsWith("--project=")) {
239
- output.push(arg);
240
- }
241
- }
242
- return output;
203
+ const output = [];
204
+ for (let i = 0; i < userArgs.length; i += 1) {
205
+ const arg = userArgs[i];
206
+ if (arg === "--") break;
207
+ if (arg === "--project") {
208
+ const next = userArgs[i + 1];
209
+ if (next != null) {
210
+ output.push(arg, next);
211
+ i += 1;
212
+ }
213
+ continue;
214
+ }
215
+ if (arg.startsWith("--project=")) output.push(arg);
216
+ }
217
+ return output;
243
218
  }
244
219
  function parseJsonObjectFromText(input) {
245
- try {
246
- return JSON.parse(input);
247
- } catch {
248
- const start = input.indexOf("{");
249
- const end = input.lastIndexOf("}");
250
- if (start === -1 || end === -1 || end <= start) {
251
- return null;
252
- }
253
- try {
254
- return JSON.parse(input.slice(start, end + 1));
255
- } catch {
256
- return null;
257
- }
258
- }
220
+ try {
221
+ return JSON.parse(input);
222
+ } catch {
223
+ const start = input.indexOf("{");
224
+ const end = input.lastIndexOf("}");
225
+ if (start === -1 || end === -1 || end <= start) return null;
226
+ try {
227
+ return JSON.parse(input.slice(start, end + 1));
228
+ } catch {
229
+ return null;
230
+ }
231
+ }
259
232
  }
260
233
  function extractPlaywrightPositionalArgs(userArgs) {
261
- const output = [];
262
- for (let i = 0; i < userArgs.length; i += 1) {
263
- const arg = userArgs[i];
264
- if (arg === "--") {
265
- break;
266
- }
267
- if (!arg.startsWith("-")) {
268
- output.push(arg);
269
- continue;
270
- }
271
- if (arg.includes("=")) {
272
- continue;
273
- }
274
- if (PLAYWRIGHT_OPTIONS_WITH_REQUIRED_VALUE.has(arg)) {
275
- const next = userArgs[i + 1];
276
- if (next !== void 0) {
277
- i += 1;
278
- }
279
- continue;
280
- }
281
- if (PLAYWRIGHT_OPTIONS_WITH_OPTIONAL_VALUE.has(arg)) {
282
- const next = userArgs[i + 1];
283
- if (next !== void 0 && !next.startsWith("-")) {
284
- i += 1;
285
- }
286
- }
287
- }
288
- return output;
234
+ const output = [];
235
+ for (let i = 0; i < userArgs.length; i += 1) {
236
+ const arg = userArgs[i];
237
+ if (arg === "--") break;
238
+ if (!arg.startsWith("-")) {
239
+ output.push(arg);
240
+ continue;
241
+ }
242
+ if (arg.includes("=")) continue;
243
+ if (PLAYWRIGHT_OPTIONS_WITH_REQUIRED_VALUE.has(arg)) {
244
+ if (userArgs[i + 1] !== void 0) i += 1;
245
+ continue;
246
+ }
247
+ if (PLAYWRIGHT_OPTIONS_WITH_OPTIONAL_VALUE.has(arg)) {
248
+ const next = userArgs[i + 1];
249
+ if (next !== void 0 && !next.startsWith("-")) i += 1;
250
+ }
251
+ }
252
+ return output;
289
253
  }
290
254
  function removePlaywrightOptions(optionArgs, optionsToRemove) {
291
- const output = [];
292
- for (let i = 0; i < optionArgs.length; i += 1) {
293
- const arg = optionArgs[i];
294
- const normalized = arg.split("=")[0];
295
- if (!arg.startsWith("-") || !optionsToRemove.has(normalized)) {
296
- output.push(arg);
297
- continue;
298
- }
299
- if (arg.includes("=")) {
300
- continue;
301
- }
302
- if (PLAYWRIGHT_OPTIONS_WITH_REQUIRED_VALUE.has(normalized)) {
303
- const next = optionArgs[i + 1];
304
- if (next !== void 0) {
305
- i += 1;
306
- }
307
- continue;
308
- }
309
- if (PLAYWRIGHT_OPTIONS_WITH_OPTIONAL_VALUE.has(normalized)) {
310
- const next = optionArgs[i + 1];
311
- if (next !== void 0 && !next.startsWith("-")) {
312
- i += 1;
313
- }
314
- }
315
- }
316
- return output;
255
+ const output = [];
256
+ for (let i = 0; i < optionArgs.length; i += 1) {
257
+ const arg = optionArgs[i];
258
+ const normalized = arg.split("=")[0];
259
+ if (!arg.startsWith("-") || !optionsToRemove.has(normalized)) {
260
+ output.push(arg);
261
+ continue;
262
+ }
263
+ if (arg.includes("=")) continue;
264
+ if (PLAYWRIGHT_OPTIONS_WITH_REQUIRED_VALUE.has(normalized)) {
265
+ if (optionArgs[i + 1] !== void 0) i += 1;
266
+ continue;
267
+ }
268
+ if (PLAYWRIGHT_OPTIONS_WITH_OPTIONAL_VALUE.has(normalized)) {
269
+ const next = optionArgs[i + 1];
270
+ if (next !== void 0 && !next.startsWith("-")) i += 1;
271
+ }
272
+ }
273
+ return output;
317
274
  }
318
275
  function extractPlaywrightOptionArgs(userArgs) {
319
- const output = [];
320
- for (let i = 0; i < userArgs.length; i += 1) {
321
- const arg = userArgs[i];
322
- if (arg === "--") {
323
- break;
324
- }
325
- if (!arg.startsWith("-")) {
326
- continue;
327
- }
328
- output.push(arg);
329
- if (arg.includes("=")) {
330
- continue;
331
- }
332
- if (PLAYWRIGHT_OPTIONS_WITH_REQUIRED_VALUE.has(arg)) {
333
- const next = userArgs[i + 1];
334
- if (next !== void 0) {
335
- output.push(next);
336
- i += 1;
337
- }
338
- continue;
339
- }
340
- if (PLAYWRIGHT_OPTIONS_WITH_OPTIONAL_VALUE.has(arg)) {
341
- const next = userArgs[i + 1];
342
- if (next !== void 0 && !next.startsWith("-")) {
343
- output.push(next);
344
- i += 1;
345
- }
346
- }
347
- }
348
- return output;
276
+ const output = [];
277
+ for (let i = 0; i < userArgs.length; i += 1) {
278
+ const arg = userArgs[i];
279
+ if (arg === "--") break;
280
+ if (!arg.startsWith("-")) continue;
281
+ output.push(arg);
282
+ if (arg.includes("=")) continue;
283
+ if (PLAYWRIGHT_OPTIONS_WITH_REQUIRED_VALUE.has(arg)) {
284
+ const next = userArgs[i + 1];
285
+ if (next !== void 0) {
286
+ output.push(next);
287
+ i += 1;
288
+ }
289
+ continue;
290
+ }
291
+ if (PLAYWRIGHT_OPTIONS_WITH_OPTIONAL_VALUE.has(arg)) {
292
+ const next = userArgs[i + 1];
293
+ if (next !== void 0 && !next.startsWith("-")) {
294
+ output.push(next);
295
+ i += 1;
296
+ }
297
+ }
298
+ }
299
+ return output;
349
300
  }
350
301
  async function readPlaywrightReportSummary(resultsFilePath, newerThanMtimeMs) {
351
- const stats = await fs$1.stat(resultsFilePath).catch(() => null);
352
- if (!stats) {
353
- return null;
354
- }
355
- if (newerThanMtimeMs != null && stats.mtimeMs <= newerThanMtimeMs) {
356
- return null;
357
- }
358
- const data = await readJson(resultsFilePath);
359
- if (!data || typeof data !== "object") {
360
- return null;
361
- }
362
- return {
363
- failedFiles: resolveFailedPlaywrightFiles(data)
364
- };
302
+ const stats = await fsPromises.stat(resultsFilePath).catch(() => null);
303
+ if (!stats) return null;
304
+ if (newerThanMtimeMs != null && stats.mtimeMs <= newerThanMtimeMs) return null;
305
+ const data = await readJson(resultsFilePath);
306
+ if (!data || typeof data !== "object") return null;
307
+ return { failedFiles: resolveFailedPlaywrightFiles(data) };
365
308
  }
366
309
  function getFileMtimeMs(filePath) {
367
- try {
368
- return fs.statSync(filePath).mtimeMs;
369
- } catch {
370
- return null;
371
- }
310
+ try {
311
+ return fs.statSync(filePath).mtimeMs;
312
+ } catch {
313
+ return null;
314
+ }
372
315
  }
373
316
  function resolvePlaywrightRetriesFromReport(data) {
374
- const projects = Array.isArray(data?.config?.projects) ? data.config.projects : [];
375
- const retriesFromProjects = projects.map((project) => Number(project?.retries)).filter((value) => Number.isFinite(value)).map((value) => Math.max(0, Math.floor(value)));
376
- if (retriesFromProjects.length > 0) {
377
- return Math.max(...retriesFromProjects);
378
- }
379
- const fallback = Number(data?.config?.retries);
380
- if (Number.isFinite(fallback)) {
381
- return Math.max(0, Math.floor(fallback));
382
- }
383
- return 0;
317
+ const retriesFromProjects = (Array.isArray(data?.config?.projects) ? data.config.projects : []).map((project) => Number(project?.retries)).filter((value) => Number.isFinite(value)).map((value) => Math.max(0, Math.floor(value)));
318
+ if (retriesFromProjects.length > 0) return Math.max(...retriesFromProjects);
319
+ const fallback = Number(data?.config?.retries);
320
+ if (Number.isFinite(fallback)) return Math.max(0, Math.floor(fallback));
321
+ return 0;
322
+ }
323
+ function resolvePlaywrightWorkersFromReport(data) {
324
+ const configWorkers = Number(data?.config?.workers);
325
+ if (Number.isFinite(configWorkers)) return Math.max(1, Math.floor(configWorkers));
326
+ const workersFromProjects = (Array.isArray(data?.config?.projects) ? data.config.projects : []).map((project) => Number(project?.metadata?.actualWorkers ?? project?.workers)).filter((value) => Number.isFinite(value)).map((value) => Math.max(1, Math.floor(value)));
327
+ if (workersFromProjects.length > 0) return Math.max(...workersFromProjects);
328
+ return null;
384
329
  }
385
330
  function resolveFailedPlaywrightFiles(data) {
386
- const suites = Array.isArray(data?.suites) ? data.suites : [];
387
- const rootDir = typeof data?.config?.rootDir === "string" ? data.config.rootDir : null;
388
- const failedFiles = /* @__PURE__ */ new Set();
389
- for (const suite of suites) {
390
- collectFailedPlaywrightFiles(suite, failedFiles, rootDir);
391
- }
392
- return Array.from(failedFiles).sort();
331
+ const suites = Array.isArray(data?.suites) ? data.suites : [];
332
+ const rootDir = typeof data?.config?.rootDir === "string" ? data.config.rootDir : null;
333
+ const failedFiles = /* @__PURE__ */ new Set();
334
+ for (const suite of suites) collectFailedPlaywrightFiles(suite, failedFiles, rootDir);
335
+ return Array.from(failedFiles).sort();
393
336
  }
394
337
  function collectFailedPlaywrightFiles(suite, output, rootDir) {
395
- const specs = Array.isArray(suite?.specs) ? suite.specs : [];
396
- for (const spec of specs) {
397
- if (isPlaywrightSpecSuccessful(spec)) {
398
- continue;
399
- }
400
- const file = normalizePlaywrightReportFile(spec?.file ?? suite?.file, rootDir);
401
- if (file) {
402
- output.add(file);
403
- }
404
- }
405
- const innerSuites = Array.isArray(suite?.suites) ? suite.suites : [];
406
- for (const innerSuite of innerSuites) {
407
- collectFailedPlaywrightFiles(innerSuite, output, rootDir);
408
- }
338
+ const specs = Array.isArray(suite?.specs) ? suite.specs : [];
339
+ for (const spec of specs) {
340
+ if (isPlaywrightSpecSuccessful(spec)) continue;
341
+ const file = normalizePlaywrightReportFile(spec?.file ?? suite?.file, rootDir);
342
+ if (file) output.add(file);
343
+ }
344
+ const innerSuites = Array.isArray(suite?.suites) ? suite.suites : [];
345
+ for (const innerSuite of innerSuites) collectFailedPlaywrightFiles(innerSuite, output, rootDir);
409
346
  }
410
347
  function isPlaywrightSpecSuccessful(spec) {
411
- if (typeof spec?.ok === "boolean") {
412
- return spec.ok;
413
- }
414
- const tests = Array.isArray(spec?.tests) ? spec.tests : [];
415
- if (tests.length === 0) {
416
- return false;
417
- }
418
- return tests.every((test) => {
419
- const testStatus = String(test?.status ?? "");
420
- if (testStatus === "expected" || testStatus === "skipped" || testStatus === "flaky") {
421
- return true;
422
- }
423
- const results = Array.isArray(test?.results) ? test.results : [];
424
- if (results.length === 0) {
425
- return false;
426
- }
427
- return results.every((result) => result?.status === "passed" || result?.status === "skipped");
428
- });
348
+ if (typeof spec?.ok === "boolean") return spec.ok;
349
+ const tests = Array.isArray(spec?.tests) ? spec.tests : [];
350
+ if (tests.length === 0) return false;
351
+ return tests.every((test) => {
352
+ const testStatus = String(test?.status ?? "");
353
+ if (testStatus === "expected" || testStatus === "skipped" || testStatus === "flaky") return true;
354
+ const results = Array.isArray(test?.results) ? test.results : [];
355
+ if (results.length === 0) return false;
356
+ return results.every((result) => result?.status === "passed" || result?.status === "skipped");
357
+ });
429
358
  }
430
359
  function normalizePlaywrightReportFile(filePath, rootDir) {
431
- const raw = String(filePath ?? "").trim();
432
- if (!raw) {
433
- return null;
434
- }
435
- let absolutePath;
436
- if (path.isAbsolute(raw)) {
437
- absolutePath = path.normalize(raw);
438
- } else {
439
- const cwdCandidate = path.resolve(process.cwd(), raw);
440
- const rootCandidate = rootDir ? path.resolve(rootDir, raw) : null;
441
- if (rootCandidate && fs.existsSync(rootCandidate)) {
442
- absolutePath = rootCandidate;
443
- } else if (fs.existsSync(cwdCandidate)) {
444
- absolutePath = cwdCandidate;
445
- } else {
446
- absolutePath = rootCandidate ?? cwdCandidate;
447
- }
448
- }
449
- const relative = path.relative(process.cwd(), absolutePath);
450
- if (relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative)) {
451
- return toPosixPath(relative || raw);
452
- }
453
- return toPosixPath(raw);
360
+ const raw = String(filePath ?? "").trim();
361
+ if (!raw) return null;
362
+ let absolutePath;
363
+ if (path.isAbsolute(raw)) absolutePath = path.normalize(raw);
364
+ else {
365
+ const cwdCandidate = path.resolve(process.cwd(), raw);
366
+ const rootCandidate = rootDir ? path.resolve(rootDir, raw) : null;
367
+ if (rootCandidate && fs.existsSync(rootCandidate)) absolutePath = rootCandidate;
368
+ else if (fs.existsSync(cwdCandidate)) absolutePath = cwdCandidate;
369
+ else absolutePath = rootCandidate ?? cwdCandidate;
370
+ }
371
+ const relative = path.relative(process.cwd(), absolutePath);
372
+ if (relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative)) return toPosixPath(relative || raw);
373
+ return toPosixPath(raw);
454
374
  }
455
375
  function toPosixPath(input) {
456
- return String(input ?? "").split(path.sep).join("/");
376
+ return String(input ?? "").split(path.sep).join("/");
377
+ }
378
+ function buildPlaywrightRetryOutputDir(attempt, failedFiles) {
379
+ const slug = failedFiles.map((file) => toPosixPath(file).replace(/[^a-zA-Z0-9._/-]+/g, "-").replace(/\//g, "__")).join("--");
380
+ return toPosixPath(path.join("build", "playwright", "test-results", "rb-retries", `attempt-${attempt}`, slug));
381
+ }
382
+ async function runPlaywrightRetryAttempt({ launcher, userArgs, failedFiles, retryConcurrency, configPath, hasCustomConfig, env, attempt }) {
383
+ const failedAgain = /* @__PURE__ */ new Set();
384
+ let lastError = null;
385
+ let nextIndex = 0;
386
+ async function runNext() {
387
+ while (nextIndex < failedFiles.length) {
388
+ const fileIndex = nextIndex;
389
+ nextIndex += 1;
390
+ const failedFile = failedFiles[fileIndex];
391
+ const retryArgs = buildPlaywrightWorkerRetryArgs(userArgs, [failedFile], attempt);
392
+ const retryPlaywrightArgs = ["test"];
393
+ if (!hasCustomConfig) retryPlaywrightArgs.push("--config", configPath);
394
+ retryPlaywrightArgs.push(...retryArgs);
395
+ try {
396
+ await runPlaywrightOnce({
397
+ launcher,
398
+ args: retryPlaywrightArgs,
399
+ env,
400
+ outputMode: "line",
401
+ successMessage: null
402
+ });
403
+ } catch (retryError) {
404
+ lastError = retryError;
405
+ failedAgain.add(failedFile);
406
+ }
407
+ }
408
+ }
409
+ await Promise.all(Array.from({ length: Math.max(1, Math.min(retryConcurrency, failedFiles.length)) }, () => runNext()));
410
+ return {
411
+ failedFiles: failedFiles.filter((failedFile) => failedAgain.has(failedFile)),
412
+ lastError
413
+ };
457
414
  }
458
415
  async function readJson(filePath) {
459
- try {
460
- const raw = await fs$1.readFile(filePath, "utf8");
461
- return JSON.parse(raw);
462
- } catch {
463
- return null;
464
- }
416
+ try {
417
+ const raw = await fsPromises.readFile(filePath, "utf8");
418
+ return JSON.parse(raw);
419
+ } catch {
420
+ return null;
421
+ }
465
422
  }
466
423
  function ensureJsxRuntimeShim(projectRoot) {
467
- const shimDir = path.join(projectRoot, "node_modules", "playwright");
468
- fs.mkdirSync(shimDir, {
469
- recursive: true
470
- });
471
- const shims = [{
472
- file: "jsx-runtime.js",
473
- target: "react/jsx-runtime"
474
- }, {
475
- file: "jsx-dev-runtime.js",
476
- target: "react/jsx-dev-runtime"
477
- }];
478
- for (const {
479
- file,
480
- target
481
- } of shims) {
482
- const filePath = path.join(shimDir, file);
483
- if (!fs.existsSync(filePath)) {
484
- const content = `export * from "${target}";
485
- export { default } from "${target}";
486
- `;
487
- fs.writeFileSync(filePath, content, "utf8");
488
- }
489
- }
424
+ const shimDir = path.join(projectRoot, "node_modules", "playwright");
425
+ fs.mkdirSync(shimDir, { recursive: true });
426
+ for (const { file, target } of [{
427
+ file: "jsx-runtime.js",
428
+ target: "react/jsx-runtime"
429
+ }, {
430
+ file: "jsx-dev-runtime.js",
431
+ target: "react/jsx-dev-runtime"
432
+ }]) {
433
+ const filePath = path.join(shimDir, file);
434
+ if (!fs.existsSync(filePath)) {
435
+ const content = `export * from "${target}";\nexport { default } from "${target}";\n`;
436
+ fs.writeFileSync(filePath, content, "utf8");
437
+ }
438
+ }
490
439
  }
491
- export {
492
- runPlaywright
493
- };
494
- //# sourceMappingURL=playwright.js.map
440
+ //#endregion
441
+ export { runPlaywright };
442
+
443
+ //# sourceMappingURL=playwright.js.map