@rpcbase/test 0.357.0 → 0.359.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/clearDatabase.js +12 -10
- package/dist/clearDatabase.js.map +1 -1
- package/dist/cli.js +556 -439
- package/dist/cli.js.map +1 -1
- package/dist/coverage/collect.js +101 -63
- package/dist/coverage/collect.js.map +1 -1
- package/dist/coverage/config-loader.js +230 -180
- package/dist/coverage/config-loader.js.map +1 -1
- package/dist/coverage/config.js +100 -76
- package/dist/coverage/config.js.map +1 -1
- package/dist/coverage/console-text-report.js +220 -175
- package/dist/coverage/console-text-report.js.map +1 -1
- package/dist/coverage/files.js +58 -45
- package/dist/coverage/files.js.map +1 -1
- package/dist/coverage/fixtures.js +38 -27
- package/dist/coverage/fixtures.js.map +1 -1
- package/dist/coverage/global-setup.js +18 -15
- package/dist/coverage/global-setup.js.map +1 -1
- package/dist/coverage/index.js +55 -38
- package/dist/coverage/index.js.map +1 -1
- package/dist/coverage/report.js +466 -341
- package/dist/coverage/report.js.map +1 -1
- package/dist/coverage/reporter.js +60 -47
- package/dist/coverage/reporter.js.map +1 -1
- package/dist/coverage/v8-tracker.js +147 -115
- package/dist/coverage/v8-tracker.js.map +1 -1
- package/dist/index.js +75 -46
- package/dist/index.js.map +1 -1
- package/dist/runners/playwright.js +563 -438
- package/dist/runners/playwright.js.map +1 -1
- package/dist/runners/process.d.ts.map +1 -1
- package/dist/runners/process.js +183 -125
- package/dist/runners/process.js.map +1 -1
- package/dist/runners/vitest.js +171 -124
- package/dist/runners/vitest.js.map +1 -1
- package/dist/serverCoverage.js +42 -28
- package/dist/serverCoverage.js.map +1 -1
- package/dist/vitest.config.d.ts +1 -1
- package/dist/vitest.config.js +74 -62
- package/dist/vitest.config.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,490 +1,615 @@
|
|
|
1
|
-
import { spawnAndCaptureStdout, spawnWithLogs, withRegisterShim } from "./process.js";
|
|
2
|
-
import fsPromises from "node:fs/promises";
|
|
3
1
|
import fs from "node:fs";
|
|
2
|
+
import fs$1 from "node:fs/promises";
|
|
4
3
|
import path from "node:path";
|
|
5
4
|
import { createRequire } from "node:module";
|
|
6
5
|
import { fileURLToPath } from "node:url";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
args,
|
|
113
|
-
env,
|
|
114
|
-
outputMode,
|
|
115
|
-
bufferOutput,
|
|
116
|
-
successMessage: successMessage === void 0 ? "Playwright suite passed!" : successMessage ?? void 0,
|
|
117
|
-
failureMessage: failureMessage === void 0 ? "Playwright failed" : failureMessage ?? void 0,
|
|
118
|
-
stdoutLinePrefix,
|
|
119
|
-
stderrLinePrefix
|
|
120
|
-
});
|
|
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", "--output", "--retries", "--pass-with-no-tests", "--reporter", "--workers", "-j"]);
|
|
14
|
+
const PLAYWRIGHT_OPTIONS_TO_REMOVE_FOR_INITIAL_FRESH_RUN = /* @__PURE__ */ new Set(["--retries"]);
|
|
15
|
+
const PLAYWRIGHT_RETRY_LOG_LABEL_MAX_LENGTH = 20;
|
|
16
|
+
async function runPlaywright(userArgs, {
|
|
17
|
+
disableCoverage = false
|
|
18
|
+
} = {}) {
|
|
19
|
+
const configPath = fs.existsSync(path.join(process.cwd(), "playwright.config.ts")) ? path.join(process.cwd(), "playwright.config.ts") : path.join(moduleDir, "..", "playwright.config.ts");
|
|
20
|
+
const hasCustomConfig = userArgs.some((arg) => {
|
|
21
|
+
if (arg === "--config" || arg === "-c") {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
return arg.startsWith("--config=");
|
|
25
|
+
});
|
|
26
|
+
ensureJsxRuntimeShim(process.cwd());
|
|
27
|
+
const launcher = resolvePlaywrightLauncher();
|
|
28
|
+
const env = withRegisterShim(process.env);
|
|
29
|
+
env[COMBINED_COVERAGE_ENV_VAR] = "1";
|
|
30
|
+
if (disableCoverage) {
|
|
31
|
+
env.RB_DISABLE_COVERAGE = "1";
|
|
32
|
+
}
|
|
33
|
+
const runtimeSettings = await resolveFreshWorkerRetrySettings({
|
|
34
|
+
launcher,
|
|
35
|
+
configPath,
|
|
36
|
+
hasCustomConfig,
|
|
37
|
+
userArgs,
|
|
38
|
+
env
|
|
39
|
+
});
|
|
40
|
+
const configuredRetries = runtimeSettings.retries;
|
|
41
|
+
const retryConcurrency = Math.max(1, runtimeSettings.workers ?? 1);
|
|
42
|
+
const resultsMtimeBeforeRun = getFileMtimeMs(PLAYWRIGHT_RESULTS_FILE);
|
|
43
|
+
const initialRunArgs = buildPlaywrightInitialFreshWorkerArgs(userArgs);
|
|
44
|
+
const initialPlaywrightArgs = ["test"];
|
|
45
|
+
if (!hasCustomConfig) {
|
|
46
|
+
initialPlaywrightArgs.push("--config", configPath);
|
|
47
|
+
}
|
|
48
|
+
initialPlaywrightArgs.push(...initialRunArgs);
|
|
49
|
+
try {
|
|
50
|
+
await runPlaywrightOnce({
|
|
51
|
+
launcher,
|
|
52
|
+
args: initialPlaywrightArgs,
|
|
53
|
+
env
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
} catch (error) {
|
|
57
|
+
const summary = await readPlaywrightReportSummary(PLAYWRIGHT_RESULTS_FILE, resultsMtimeBeforeRun);
|
|
58
|
+
if (!summary || configuredRetries <= 0 || summary.failedFiles.length === 0) {
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
let lastError = error;
|
|
62
|
+
let failedFiles = summary.failedFiles;
|
|
63
|
+
const maxAttempts = configuredRetries;
|
|
64
|
+
for (let attempt = 1; attempt <= maxAttempts && failedFiles.length > 0; attempt += 1) {
|
|
65
|
+
const activeRetryWorkers = Math.max(1, Math.min(retryConcurrency, failedFiles.length));
|
|
66
|
+
console.warn(`[rb-test] Retrying ${failedFiles.length} failed Playwright file(s) with fresh workers (${attempt}/${maxAttempts}, up to ${activeRetryWorkers} in parallel)`);
|
|
67
|
+
const retryResults = await runPlaywrightRetryAttempt({
|
|
68
|
+
launcher,
|
|
69
|
+
userArgs,
|
|
70
|
+
failedFiles,
|
|
71
|
+
retryConcurrency: activeRetryWorkers,
|
|
72
|
+
configPath,
|
|
73
|
+
hasCustomConfig,
|
|
74
|
+
env,
|
|
75
|
+
attempt
|
|
76
|
+
});
|
|
77
|
+
if (retryResults.lastError) {
|
|
78
|
+
lastError = retryResults.lastError;
|
|
79
|
+
}
|
|
80
|
+
failedFiles = retryResults.failedFiles;
|
|
81
|
+
}
|
|
82
|
+
if (failedFiles.length === 0) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
throw lastError;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function runPlaywrightOnce({
|
|
89
|
+
launcher,
|
|
90
|
+
args,
|
|
91
|
+
env,
|
|
92
|
+
outputMode = "chunk",
|
|
93
|
+
bufferOutput = false,
|
|
94
|
+
successMessage,
|
|
95
|
+
failureMessage,
|
|
96
|
+
stdoutLinePrefix,
|
|
97
|
+
stderrLinePrefix
|
|
98
|
+
}) {
|
|
99
|
+
return spawnWithLogs({
|
|
100
|
+
name: "Playwright",
|
|
101
|
+
launcher,
|
|
102
|
+
args,
|
|
103
|
+
env,
|
|
104
|
+
outputMode,
|
|
105
|
+
bufferOutput,
|
|
106
|
+
successMessage: successMessage === void 0 ? "Playwright suite passed!" : successMessage ?? void 0,
|
|
107
|
+
failureMessage: failureMessage === void 0 ? "Playwright failed" : failureMessage ?? void 0,
|
|
108
|
+
stdoutLinePrefix,
|
|
109
|
+
stderrLinePrefix
|
|
110
|
+
});
|
|
121
111
|
}
|
|
122
112
|
function resolvePlaywrightLauncher() {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
113
|
+
const cliPath = resolveCliPath();
|
|
114
|
+
if (cliPath) {
|
|
115
|
+
return {
|
|
116
|
+
command: process.execPath,
|
|
117
|
+
args: [cliPath]
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const localBin = path.resolve(process.cwd(), "node_modules/.bin/playwright");
|
|
121
|
+
if (fs.existsSync(localBin)) {
|
|
122
|
+
return {
|
|
123
|
+
command: localBin,
|
|
124
|
+
args: []
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
command: "playwright",
|
|
129
|
+
args: []
|
|
130
|
+
};
|
|
137
131
|
}
|
|
138
132
|
function resolveCliPath() {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
133
|
+
const searchRoots = [process.cwd(), moduleDir];
|
|
134
|
+
for (const base of searchRoots) {
|
|
135
|
+
try {
|
|
136
|
+
const pkgPath = require$1.resolve("@playwright/test/package.json", {
|
|
137
|
+
paths: [base]
|
|
138
|
+
});
|
|
139
|
+
const cliPath = path.join(path.dirname(pkgPath), "cli.js");
|
|
140
|
+
if (fs.existsSync(cliPath)) {
|
|
141
|
+
return cliPath;
|
|
142
|
+
}
|
|
143
|
+
} catch (_error) {
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
146
147
|
}
|
|
147
148
|
function buildPlaywrightWorkerRetryArgs(userArgs, failedFiles, attempt) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
"--workers=1",
|
|
152
|
-
"--reporter=list",
|
|
153
|
-
"--output",
|
|
154
|
-
buildPlaywrightRetryOutputDir(attempt, failedFiles),
|
|
155
|
-
...failedFiles
|
|
156
|
-
];
|
|
149
|
+
const optionArgs = extractPlaywrightOptionArgs(userArgs);
|
|
150
|
+
const sanitizedOptions = removePlaywrightOptions(optionArgs, PLAYWRIGHT_OPTIONS_TO_REMOVE_FOR_WORKER_RETRY);
|
|
151
|
+
return [...sanitizedOptions, "--retries=0", "--workers=1", "--reporter=list", "--output", buildPlaywrightRetryOutputDir(attempt, failedFiles), ...failedFiles];
|
|
157
152
|
}
|
|
158
153
|
function buildPlaywrightInitialFreshWorkerArgs(userArgs) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
154
|
+
const optionArgs = extractPlaywrightOptionArgs(userArgs);
|
|
155
|
+
const sanitizedOptions = removePlaywrightOptions(optionArgs, PLAYWRIGHT_OPTIONS_TO_REMOVE_FOR_INITIAL_FRESH_RUN);
|
|
156
|
+
const positionalArgs = extractPlaywrightPositionalArgs(userArgs);
|
|
157
|
+
return [...sanitizedOptions, "--retries=0", ...positionalArgs];
|
|
158
|
+
}
|
|
159
|
+
async function resolveFreshWorkerRetrySettings({
|
|
160
|
+
launcher,
|
|
161
|
+
configPath,
|
|
162
|
+
hasCustomConfig,
|
|
163
|
+
userArgs,
|
|
164
|
+
env
|
|
165
|
+
}) {
|
|
166
|
+
const configuredSettings = await probeConfiguredPlaywrightSettings({
|
|
167
|
+
launcher,
|
|
168
|
+
configPath,
|
|
169
|
+
hasCustomConfig,
|
|
170
|
+
userArgs,
|
|
171
|
+
env
|
|
172
|
+
});
|
|
173
|
+
return {
|
|
174
|
+
retries: configuredSettings.retries ?? 0,
|
|
175
|
+
workers: configuredSettings.workers
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
async function probeConfiguredPlaywrightSettings({
|
|
179
|
+
launcher,
|
|
180
|
+
configPath,
|
|
181
|
+
hasCustomConfig,
|
|
182
|
+
userArgs,
|
|
183
|
+
env
|
|
184
|
+
}) {
|
|
185
|
+
const probeArgs = ["test"];
|
|
186
|
+
if (!hasCustomConfig) {
|
|
187
|
+
probeArgs.push("--config", configPath);
|
|
188
|
+
}
|
|
189
|
+
probeArgs.push("--list", "--reporter=json", ...extractPlaywrightProjectArgs(userArgs));
|
|
190
|
+
try {
|
|
191
|
+
const stdout = await spawnAndCaptureStdout({
|
|
192
|
+
name: "Playwright retries probe",
|
|
193
|
+
launcher,
|
|
194
|
+
args: probeArgs,
|
|
195
|
+
env
|
|
196
|
+
});
|
|
197
|
+
const json = parseJsonObjectFromText(stdout);
|
|
198
|
+
if (!json) {
|
|
199
|
+
return {
|
|
200
|
+
retries: null,
|
|
201
|
+
workers: null
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
retries: resolvePlaywrightRetriesFromReport(json),
|
|
206
|
+
workers: resolvePlaywrightWorkersFromReport(json)
|
|
207
|
+
};
|
|
208
|
+
} catch (_error) {
|
|
209
|
+
return {
|
|
210
|
+
retries: null,
|
|
211
|
+
workers: null
|
|
212
|
+
};
|
|
213
|
+
}
|
|
205
214
|
}
|
|
206
215
|
function extractPlaywrightProjectArgs(userArgs) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
216
|
+
const output = [];
|
|
217
|
+
for (let i = 0; i < userArgs.length; i += 1) {
|
|
218
|
+
const arg = userArgs[i];
|
|
219
|
+
if (arg === "--") {
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
if (arg === "--project") {
|
|
223
|
+
const next = userArgs[i + 1];
|
|
224
|
+
if (next != null) {
|
|
225
|
+
output.push(arg, next);
|
|
226
|
+
i += 1;
|
|
227
|
+
}
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (arg.startsWith("--project=")) {
|
|
231
|
+
output.push(arg);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return output;
|
|
222
235
|
}
|
|
223
236
|
function parseJsonObjectFromText(input) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
237
|
+
try {
|
|
238
|
+
return JSON.parse(input);
|
|
239
|
+
} catch {
|
|
240
|
+
const start = input.indexOf("{");
|
|
241
|
+
const end = input.lastIndexOf("}");
|
|
242
|
+
if (start === -1 || end === -1 || end <= start) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
try {
|
|
246
|
+
return JSON.parse(input.slice(start, end + 1));
|
|
247
|
+
} catch {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
236
251
|
}
|
|
237
252
|
function extractPlaywrightPositionalArgs(userArgs) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
253
|
+
const output = [];
|
|
254
|
+
for (let i = 0; i < userArgs.length; i += 1) {
|
|
255
|
+
const arg = userArgs[i];
|
|
256
|
+
if (arg === "--") {
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
if (!arg.startsWith("-")) {
|
|
260
|
+
output.push(arg);
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (arg.includes("=")) {
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (PLAYWRIGHT_OPTIONS_WITH_REQUIRED_VALUE.has(arg)) {
|
|
267
|
+
const next = userArgs[i + 1];
|
|
268
|
+
if (next !== void 0) {
|
|
269
|
+
i += 1;
|
|
270
|
+
}
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (PLAYWRIGHT_OPTIONS_WITH_OPTIONAL_VALUE.has(arg)) {
|
|
274
|
+
const next = userArgs[i + 1];
|
|
275
|
+
if (next !== void 0 && !next.startsWith("-")) {
|
|
276
|
+
i += 1;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return output;
|
|
257
281
|
}
|
|
258
282
|
function removePlaywrightOptions(optionArgs, optionsToRemove) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
283
|
+
const output = [];
|
|
284
|
+
for (let i = 0; i < optionArgs.length; i += 1) {
|
|
285
|
+
const arg = optionArgs[i];
|
|
286
|
+
const normalized = arg.split("=")[0];
|
|
287
|
+
if (!arg.startsWith("-") || !optionsToRemove.has(normalized)) {
|
|
288
|
+
output.push(arg);
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
if (arg.includes("=")) {
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if (PLAYWRIGHT_OPTIONS_WITH_REQUIRED_VALUE.has(normalized)) {
|
|
295
|
+
const next = optionArgs[i + 1];
|
|
296
|
+
if (next !== void 0) {
|
|
297
|
+
i += 1;
|
|
298
|
+
}
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
if (PLAYWRIGHT_OPTIONS_WITH_OPTIONAL_VALUE.has(normalized)) {
|
|
302
|
+
const next = optionArgs[i + 1];
|
|
303
|
+
if (next !== void 0 && !next.startsWith("-")) {
|
|
304
|
+
i += 1;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return output;
|
|
278
309
|
}
|
|
279
310
|
function extractPlaywrightOptionArgs(userArgs) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
311
|
+
const output = [];
|
|
312
|
+
for (let i = 0; i < userArgs.length; i += 1) {
|
|
313
|
+
const arg = userArgs[i];
|
|
314
|
+
if (arg === "--") {
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
if (!arg.startsWith("-")) {
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
output.push(arg);
|
|
321
|
+
if (arg.includes("=")) {
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
if (PLAYWRIGHT_OPTIONS_WITH_REQUIRED_VALUE.has(arg)) {
|
|
325
|
+
const next = userArgs[i + 1];
|
|
326
|
+
if (next !== void 0) {
|
|
327
|
+
output.push(next);
|
|
328
|
+
i += 1;
|
|
329
|
+
}
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
if (PLAYWRIGHT_OPTIONS_WITH_OPTIONAL_VALUE.has(arg)) {
|
|
333
|
+
const next = userArgs[i + 1];
|
|
334
|
+
if (next !== void 0 && !next.startsWith("-")) {
|
|
335
|
+
output.push(next);
|
|
336
|
+
i += 1;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return output;
|
|
304
341
|
}
|
|
305
342
|
async function readPlaywrightReportSummary(resultsFilePath, newerThanMtimeMs) {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
343
|
+
const stats = await fs$1.stat(resultsFilePath).catch(() => null);
|
|
344
|
+
if (!stats) {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
if (newerThanMtimeMs != null && stats.mtimeMs <= newerThanMtimeMs) {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
const data = await readJson(resultsFilePath);
|
|
351
|
+
if (!data || typeof data !== "object") {
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
failedFiles: resolveFailedPlaywrightFiles(data)
|
|
356
|
+
};
|
|
312
357
|
}
|
|
313
358
|
function getFileMtimeMs(filePath) {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
359
|
+
try {
|
|
360
|
+
return fs.statSync(filePath).mtimeMs;
|
|
361
|
+
} catch {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
319
364
|
}
|
|
320
365
|
function resolvePlaywrightRetriesFromReport(data) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
366
|
+
const projects = Array.isArray(data?.config?.projects) ? data.config.projects : [];
|
|
367
|
+
const retriesFromProjects = projects.map((project) => Number(project?.retries)).filter((value) => Number.isFinite(value)).map((value) => Math.max(0, Math.floor(value)));
|
|
368
|
+
if (retriesFromProjects.length > 0) {
|
|
369
|
+
return Math.max(...retriesFromProjects);
|
|
370
|
+
}
|
|
371
|
+
const fallback = Number(data?.config?.retries);
|
|
372
|
+
if (Number.isFinite(fallback)) {
|
|
373
|
+
return Math.max(0, Math.floor(fallback));
|
|
374
|
+
}
|
|
375
|
+
return 0;
|
|
326
376
|
}
|
|
327
377
|
function resolvePlaywrightWorkersFromReport(data) {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
378
|
+
const configWorkers = Number(data?.config?.workers);
|
|
379
|
+
if (Number.isFinite(configWorkers)) {
|
|
380
|
+
return Math.max(1, Math.floor(configWorkers));
|
|
381
|
+
}
|
|
382
|
+
const projects = Array.isArray(data?.config?.projects) ? data.config.projects : [];
|
|
383
|
+
const workersFromProjects = projects.map((project) => Number(project?.metadata?.actualWorkers ?? project?.workers)).filter((value) => Number.isFinite(value)).map((value) => Math.max(1, Math.floor(value)));
|
|
384
|
+
if (workersFromProjects.length > 0) {
|
|
385
|
+
return Math.max(...workersFromProjects);
|
|
386
|
+
}
|
|
387
|
+
return null;
|
|
333
388
|
}
|
|
334
389
|
function resolveFailedPlaywrightFiles(data) {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
390
|
+
const suites = Array.isArray(data?.suites) ? data.suites : [];
|
|
391
|
+
const rootDir = typeof data?.config?.rootDir === "string" ? data.config.rootDir : null;
|
|
392
|
+
const failedFiles = /* @__PURE__ */ new Set();
|
|
393
|
+
for (const suite of suites) {
|
|
394
|
+
collectFailedPlaywrightFiles(suite, failedFiles, rootDir);
|
|
395
|
+
}
|
|
396
|
+
return Array.from(failedFiles).sort();
|
|
340
397
|
}
|
|
341
398
|
function collectFailedPlaywrightFiles(suite, output, rootDir) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
399
|
+
const specs = Array.isArray(suite?.specs) ? suite.specs : [];
|
|
400
|
+
for (const spec of specs) {
|
|
401
|
+
if (isPlaywrightSpecSuccessful(spec)) {
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
const file = normalizePlaywrightReportFile(spec?.file ?? suite?.file, rootDir);
|
|
405
|
+
if (file) {
|
|
406
|
+
output.add(file);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
const innerSuites = Array.isArray(suite?.suites) ? suite.suites : [];
|
|
410
|
+
for (const innerSuite of innerSuites) {
|
|
411
|
+
collectFailedPlaywrightFiles(innerSuite, output, rootDir);
|
|
412
|
+
}
|
|
350
413
|
}
|
|
351
414
|
function isPlaywrightSpecSuccessful(spec) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
415
|
+
if (typeof spec?.ok === "boolean") {
|
|
416
|
+
return spec.ok;
|
|
417
|
+
}
|
|
418
|
+
const tests = Array.isArray(spec?.tests) ? spec.tests : [];
|
|
419
|
+
if (tests.length === 0) {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
return tests.every((test) => {
|
|
423
|
+
const testStatus = String(test?.status ?? "");
|
|
424
|
+
if (testStatus === "expected" || testStatus === "skipped" || testStatus === "flaky") {
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
const results = Array.isArray(test?.results) ? test.results : [];
|
|
428
|
+
if (results.length === 0) {
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
return results.every((result) => result?.status === "passed" || result?.status === "skipped");
|
|
432
|
+
});
|
|
362
433
|
}
|
|
363
434
|
function normalizePlaywrightReportFile(filePath, rootDir) {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
435
|
+
const raw = String(filePath ?? "").trim();
|
|
436
|
+
if (!raw) {
|
|
437
|
+
return null;
|
|
438
|
+
}
|
|
439
|
+
let absolutePath;
|
|
440
|
+
if (path.isAbsolute(raw)) {
|
|
441
|
+
absolutePath = path.normalize(raw);
|
|
442
|
+
} else {
|
|
443
|
+
const cwdCandidate = path.resolve(process.cwd(), raw);
|
|
444
|
+
const rootCandidate = rootDir ? path.resolve(rootDir, raw) : null;
|
|
445
|
+
if (rootCandidate && fs.existsSync(rootCandidate)) {
|
|
446
|
+
absolutePath = rootCandidate;
|
|
447
|
+
} else if (fs.existsSync(cwdCandidate)) {
|
|
448
|
+
absolutePath = cwdCandidate;
|
|
449
|
+
} else {
|
|
450
|
+
absolutePath = rootCandidate ?? cwdCandidate;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
const relative = path.relative(process.cwd(), absolutePath);
|
|
454
|
+
if (relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative)) {
|
|
455
|
+
return toPosixPath(relative || raw);
|
|
456
|
+
}
|
|
457
|
+
return toPosixPath(raw);
|
|
378
458
|
}
|
|
379
459
|
function toPosixPath(input) {
|
|
380
|
-
|
|
460
|
+
return String(input ?? "").replaceAll("\\", "/").split(path.sep).join("/");
|
|
381
461
|
}
|
|
382
462
|
function buildPlaywrightRetryOutputDir(attempt, failedFiles) {
|
|
383
|
-
|
|
384
|
-
|
|
463
|
+
const slug = failedFiles.map((file) => toPosixPath(file).replace(/[^a-zA-Z0-9._/-]+/g, "-").replace(/\//g, "__")).join("--");
|
|
464
|
+
return toPosixPath(path.join("build", "playwright", "test-results", "rb-retries", `attempt-${attempt}`, slug));
|
|
385
465
|
}
|
|
386
466
|
function createPlaywrightRetryEnv(baseEnv) {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
467
|
+
return {
|
|
468
|
+
...baseEnv,
|
|
469
|
+
PLAYWRIGHT_FORCE_TTY: "false"
|
|
470
|
+
};
|
|
391
471
|
}
|
|
392
472
|
function truncatePlaywrightRetryLogLabel(label, maxLength = PLAYWRIGHT_RETRY_LOG_LABEL_MAX_LENGTH) {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
473
|
+
if (label.length <= maxLength) {
|
|
474
|
+
return label;
|
|
475
|
+
}
|
|
476
|
+
if (maxLength <= 3) {
|
|
477
|
+
return ".".repeat(Math.max(0, maxLength));
|
|
478
|
+
}
|
|
479
|
+
const visibleChars = maxLength - 3;
|
|
480
|
+
const startLength = Math.ceil(visibleChars / 2);
|
|
481
|
+
const endLength = Math.floor(visibleChars / 2);
|
|
482
|
+
return `${label.slice(0, startLength)}...${label.slice(-endLength)}`;
|
|
399
483
|
}
|
|
400
484
|
function getPlaywrightRetryLogName(failedFile) {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
485
|
+
const normalizedFile = toPosixPath(failedFile);
|
|
486
|
+
const relativeFile = normalizedFile.startsWith("build/spec/") ? normalizedFile.slice("build/spec/".length) : normalizedFile;
|
|
487
|
+
const strippedBaseName = relativeFile.replace(/\.(spec|test)\.[^.]+$/i, "").replace(/\.[^.]+$/i, "").trim();
|
|
488
|
+
return strippedBaseName || relativeFile || normalizedFile;
|
|
489
|
+
}
|
|
490
|
+
function formatPlaywrightRetryBracketLabel({
|
|
491
|
+
failedFile,
|
|
492
|
+
suffix = ""
|
|
493
|
+
}) {
|
|
494
|
+
const label = truncatePlaywrightRetryLogLabel(getPlaywrightRetryLogName(failedFile), Math.max(1, PLAYWRIGHT_RETRY_LOG_LABEL_MAX_LENGTH - suffix.length));
|
|
495
|
+
return `[${label}${suffix}]`;
|
|
496
|
+
}
|
|
497
|
+
function formatPlaywrightRetryLogLabel({
|
|
498
|
+
failedFile
|
|
499
|
+
}) {
|
|
500
|
+
return formatPlaywrightRetryBracketLabel({
|
|
501
|
+
failedFile
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
function formatPlaywrightRetryLinePrefix({
|
|
505
|
+
failedFile,
|
|
506
|
+
stream
|
|
507
|
+
}) {
|
|
508
|
+
return stream === "stderr" ? `${formatPlaywrightRetryBracketLabel({
|
|
509
|
+
failedFile,
|
|
510
|
+
suffix: " stderr"
|
|
511
|
+
})} ` : `${formatPlaywrightRetryLogLabel({
|
|
512
|
+
failedFile
|
|
513
|
+
})} `;
|
|
514
|
+
}
|
|
515
|
+
async function runPlaywrightRetryAttempt({
|
|
516
|
+
launcher,
|
|
517
|
+
userArgs,
|
|
518
|
+
failedFiles,
|
|
519
|
+
retryConcurrency,
|
|
520
|
+
configPath,
|
|
521
|
+
hasCustomConfig,
|
|
522
|
+
env,
|
|
523
|
+
attempt
|
|
524
|
+
}) {
|
|
525
|
+
const failedAgain = /* @__PURE__ */ new Set();
|
|
526
|
+
let lastError = null;
|
|
527
|
+
let nextIndex = 0;
|
|
528
|
+
const retryEnv = createPlaywrightRetryEnv(env);
|
|
529
|
+
async function runNext() {
|
|
530
|
+
while (nextIndex < failedFiles.length) {
|
|
531
|
+
const fileIndex = nextIndex;
|
|
532
|
+
nextIndex += 1;
|
|
533
|
+
const failedFile = failedFiles[fileIndex];
|
|
534
|
+
const retryLogLabel = formatPlaywrightRetryLogLabel({
|
|
535
|
+
failedFile
|
|
536
|
+
});
|
|
537
|
+
const retryArgs = buildPlaywrightWorkerRetryArgs(userArgs, [failedFile], attempt);
|
|
538
|
+
const retryPlaywrightArgs = ["test"];
|
|
539
|
+
if (!hasCustomConfig) {
|
|
540
|
+
retryPlaywrightArgs.push("--config", configPath);
|
|
541
|
+
}
|
|
542
|
+
retryPlaywrightArgs.push(...retryArgs);
|
|
543
|
+
try {
|
|
544
|
+
console.warn(`${retryLogLabel} Starting fresh-worker retry`);
|
|
545
|
+
await runPlaywrightOnce({
|
|
546
|
+
launcher,
|
|
547
|
+
args: retryPlaywrightArgs,
|
|
548
|
+
env: retryEnv,
|
|
549
|
+
outputMode: "line",
|
|
550
|
+
bufferOutput: true,
|
|
551
|
+
successMessage: null,
|
|
552
|
+
failureMessage: `${retryLogLabel} Playwright failed`,
|
|
553
|
+
stdoutLinePrefix: formatPlaywrightRetryLinePrefix({
|
|
554
|
+
failedFile,
|
|
555
|
+
stream: "stdout"
|
|
556
|
+
}),
|
|
557
|
+
stderrLinePrefix: formatPlaywrightRetryLinePrefix({
|
|
558
|
+
failedFile,
|
|
559
|
+
stream: "stderr"
|
|
560
|
+
})
|
|
561
|
+
});
|
|
562
|
+
} catch (retryError) {
|
|
563
|
+
lastError = retryError;
|
|
564
|
+
failedAgain.add(failedFile);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
await Promise.all(Array.from({
|
|
569
|
+
length: Math.max(1, Math.min(retryConcurrency, failedFiles.length))
|
|
570
|
+
}, () => runNext()));
|
|
571
|
+
return {
|
|
572
|
+
failedFiles: failedFiles.filter((failedFile) => failedAgain.has(failedFile)),
|
|
573
|
+
lastError
|
|
574
|
+
};
|
|
461
575
|
}
|
|
462
576
|
async function readJson(filePath) {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
577
|
+
try {
|
|
578
|
+
const raw = await fs$1.readFile(filePath, "utf8");
|
|
579
|
+
return JSON.parse(raw);
|
|
580
|
+
} catch {
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
469
583
|
}
|
|
470
584
|
function ensureJsxRuntimeShim(projectRoot) {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
export
|
|
489
|
-
|
|
490
|
-
|
|
585
|
+
const shimDir = path.join(projectRoot, "node_modules", "playwright");
|
|
586
|
+
fs.mkdirSync(shimDir, {
|
|
587
|
+
recursive: true
|
|
588
|
+
});
|
|
589
|
+
const shims = [{
|
|
590
|
+
file: "jsx-runtime.js",
|
|
591
|
+
target: "react/jsx-runtime"
|
|
592
|
+
}, {
|
|
593
|
+
file: "jsx-dev-runtime.js",
|
|
594
|
+
target: "react/jsx-dev-runtime"
|
|
595
|
+
}];
|
|
596
|
+
for (const {
|
|
597
|
+
file,
|
|
598
|
+
target
|
|
599
|
+
} of shims) {
|
|
600
|
+
const filePath = path.join(shimDir, file);
|
|
601
|
+
if (!fs.existsSync(filePath)) {
|
|
602
|
+
const content = `export * from "${target}";
|
|
603
|
+
export { default } from "${target}";
|
|
604
|
+
`;
|
|
605
|
+
fs.writeFileSync(filePath, content, "utf8");
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
export {
|
|
610
|
+
createPlaywrightRetryEnv,
|
|
611
|
+
formatPlaywrightRetryLinePrefix,
|
|
612
|
+
formatPlaywrightRetryLogLabel,
|
|
613
|
+
runPlaywright
|
|
614
|
+
};
|
|
615
|
+
//# sourceMappingURL=playwright.js.map
|