@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.
- package/dist/clearDatabase.js +10 -12
- package/dist/clearDatabase.js.map +1 -1
- package/dist/cli.js +439 -556
- package/dist/cli.js.map +1 -1
- package/dist/coverage/collect.js +63 -101
- package/dist/coverage/collect.js.map +1 -1
- package/dist/coverage/config-loader.js +180 -230
- package/dist/coverage/config-loader.js.map +1 -1
- package/dist/coverage/config.js +76 -100
- package/dist/coverage/config.js.map +1 -1
- package/dist/coverage/console-text-report.js +175 -220
- package/dist/coverage/console-text-report.js.map +1 -1
- package/dist/coverage/files.js +45 -58
- package/dist/coverage/files.js.map +1 -1
- package/dist/coverage/fixtures.js +27 -38
- package/dist/coverage/fixtures.js.map +1 -1
- package/dist/coverage/global-setup.js +15 -18
- package/dist/coverage/global-setup.js.map +1 -1
- package/dist/coverage/index.js +38 -55
- package/dist/coverage/index.js.map +1 -1
- package/dist/coverage/report.js +341 -466
- package/dist/coverage/report.js.map +1 -1
- package/dist/coverage/reporter.js +47 -61
- package/dist/coverage/reporter.js.map +1 -1
- package/dist/coverage/v8-tracker.js +115 -147
- package/dist/coverage/v8-tracker.js.map +1 -1
- package/dist/index.js +46 -75
- package/dist/index.js.map +1 -1
- package/dist/runners/playwright.d.ts.map +1 -1
- package/dist/runners/playwright.js +398 -449
- package/dist/runners/playwright.js.map +1 -1
- package/dist/runners/process.d.ts +4 -1
- package/dist/runners/process.d.ts.map +1 -1
- package/dist/runners/process.js +111 -101
- package/dist/runners/process.js.map +1 -1
- package/dist/runners/vitest.js +124 -171
- package/dist/runners/vitest.js.map +1 -1
- package/dist/serverCoverage.js +28 -42
- package/dist/serverCoverage.js.map +1 -1
- package/dist/vitest.config.d.ts +1 -1
- package/dist/vitest.config.js +62 -74
- package/dist/vitest.config.js.map +1 -1
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
|
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
|
-
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
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
310
|
+
try {
|
|
311
|
+
return fs.statSync(filePath).mtimeMs;
|
|
312
|
+
} catch {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
372
315
|
}
|
|
373
316
|
function resolvePlaywrightRetriesFromReport(data) {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
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
|
-
|
|
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
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
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
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
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
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
//# sourceMappingURL=playwright.js.map
|
|
440
|
+
//#endregion
|
|
441
|
+
export { runPlaywright };
|
|
442
|
+
|
|
443
|
+
//# sourceMappingURL=playwright.js.map
|