@rpcbase/test 0.343.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.js +392 -490
- package/dist/runners/playwright.js.map +1 -1
- package/dist/runners/process.js +107 -142
- 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,541 +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
|
-
|
|
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
|
+
}
|
|
86
106
|
}
|
|
87
|
-
function runPlaywrightOnce({
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
launcher,
|
|
98
|
-
args,
|
|
99
|
-
env,
|
|
100
|
-
outputMode,
|
|
101
|
-
successMessage: successMessage === void 0 ? "Playwright suite passed!" : successMessage ?? void 0,
|
|
102
|
-
failureMessage: failureMessage === void 0 ? "Playwright failed" : failureMessage ?? void 0
|
|
103
|
-
});
|
|
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
|
+
});
|
|
104
117
|
}
|
|
105
118
|
function resolvePlaywrightLauncher() {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
command: "playwright",
|
|
122
|
-
args: []
|
|
123
|
-
};
|
|
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
|
+
};
|
|
124
133
|
}
|
|
125
134
|
function resolveCliPath() {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (fs.existsSync(cliPath)) {
|
|
134
|
-
return cliPath;
|
|
135
|
-
}
|
|
136
|
-
} catch (_error) {
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
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;
|
|
140
142
|
}
|
|
141
143
|
function buildPlaywrightWorkerRetryArgs(userArgs, failedFiles, attempt) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
+
];
|
|
145
153
|
}
|
|
146
154
|
function buildPlaywrightInitialFreshWorkerArgs(userArgs) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
+
];
|
|
151
162
|
}
|
|
152
|
-
async function resolveFreshWorkerRetrySettings({
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
env
|
|
165
|
-
});
|
|
166
|
-
return {
|
|
167
|
-
retries: configuredSettings.retries ?? 0,
|
|
168
|
-
workers: configuredSettings.workers
|
|
169
|
-
};
|
|
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
|
+
};
|
|
170
175
|
}
|
|
171
|
-
async function probeConfiguredPlaywrightSettings({
|
|
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
|
-
return {
|
|
198
|
-
retries: resolvePlaywrightRetriesFromReport(json),
|
|
199
|
-
workers: resolvePlaywrightWorkersFromReport(json)
|
|
200
|
-
};
|
|
201
|
-
} catch (_error) {
|
|
202
|
-
return {
|
|
203
|
-
retries: null,
|
|
204
|
-
workers: null
|
|
205
|
-
};
|
|
206
|
-
}
|
|
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
|
+
}
|
|
207
201
|
}
|
|
208
202
|
function extractPlaywrightProjectArgs(userArgs) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
output.push(arg);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
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;
|
|
228
218
|
}
|
|
229
219
|
function parseJsonObjectFromText(input) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
243
|
-
}
|
|
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
|
+
}
|
|
244
232
|
}
|
|
245
233
|
function extractPlaywrightPositionalArgs(userArgs) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
266
|
-
if (PLAYWRIGHT_OPTIONS_WITH_OPTIONAL_VALUE.has(arg)) {
|
|
267
|
-
const next = userArgs[i + 1];
|
|
268
|
-
if (next !== void 0 && !next.startsWith("-")) {
|
|
269
|
-
i += 1;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
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;
|
|
274
253
|
}
|
|
275
254
|
function removePlaywrightOptions(optionArgs, optionsToRemove) {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const next = optionArgs[i + 1];
|
|
296
|
-
if (next !== void 0 && !next.startsWith("-")) {
|
|
297
|
-
i += 1;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
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;
|
|
302
274
|
}
|
|
303
275
|
function extractPlaywrightOptionArgs(userArgs) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
output.push(next);
|
|
329
|
-
i += 1;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
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;
|
|
334
300
|
}
|
|
335
301
|
async function readPlaywrightReportSummary(resultsFilePath, newerThanMtimeMs) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
}
|
|
343
|
-
const data = await readJson(resultsFilePath);
|
|
344
|
-
if (!data || typeof data !== "object") {
|
|
345
|
-
return null;
|
|
346
|
-
}
|
|
347
|
-
return {
|
|
348
|
-
failedFiles: resolveFailedPlaywrightFiles(data)
|
|
349
|
-
};
|
|
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) };
|
|
350
308
|
}
|
|
351
309
|
function getFileMtimeMs(filePath) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
310
|
+
try {
|
|
311
|
+
return fs.statSync(filePath).mtimeMs;
|
|
312
|
+
} catch {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
357
315
|
}
|
|
358
316
|
function resolvePlaywrightRetriesFromReport(data) {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
const fallback = Number(data?.config?.retries);
|
|
365
|
-
if (Number.isFinite(fallback)) {
|
|
366
|
-
return Math.max(0, Math.floor(fallback));
|
|
367
|
-
}
|
|
368
|
-
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;
|
|
369
322
|
}
|
|
370
323
|
function resolvePlaywrightWorkersFromReport(data) {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
const workersFromProjects = projects.map((project) => Number(project?.metadata?.actualWorkers ?? project?.workers)).filter((value) => Number.isFinite(value)).map((value) => Math.max(1, Math.floor(value)));
|
|
377
|
-
if (workersFromProjects.length > 0) {
|
|
378
|
-
return Math.max(...workersFromProjects);
|
|
379
|
-
}
|
|
380
|
-
return null;
|
|
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;
|
|
381
329
|
}
|
|
382
330
|
function resolveFailedPlaywrightFiles(data) {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
}
|
|
389
|
-
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();
|
|
390
336
|
}
|
|
391
337
|
function collectFailedPlaywrightFiles(suite, output, rootDir) {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
const innerSuites = Array.isArray(suite?.suites) ? suite.suites : [];
|
|
403
|
-
for (const innerSuite of innerSuites) {
|
|
404
|
-
collectFailedPlaywrightFiles(innerSuite, output, rootDir);
|
|
405
|
-
}
|
|
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);
|
|
406
346
|
}
|
|
407
347
|
function isPlaywrightSpecSuccessful(spec) {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
return true;
|
|
419
|
-
}
|
|
420
|
-
const results = Array.isArray(test?.results) ? test.results : [];
|
|
421
|
-
if (results.length === 0) {
|
|
422
|
-
return false;
|
|
423
|
-
}
|
|
424
|
-
return results.every((result) => result?.status === "passed" || result?.status === "skipped");
|
|
425
|
-
});
|
|
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
|
+
});
|
|
426
358
|
}
|
|
427
359
|
function normalizePlaywrightReportFile(filePath, rootDir) {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
} else {
|
|
443
|
-
absolutePath = rootCandidate ?? cwdCandidate;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
const relative = path.relative(process.cwd(), absolutePath);
|
|
447
|
-
if (relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative)) {
|
|
448
|
-
return toPosixPath(relative || raw);
|
|
449
|
-
}
|
|
450
|
-
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);
|
|
451
374
|
}
|
|
452
375
|
function toPosixPath(input) {
|
|
453
|
-
|
|
376
|
+
return String(input ?? "").split(path.sep).join("/");
|
|
454
377
|
}
|
|
455
378
|
function buildPlaywrightRetryOutputDir(attempt, failedFiles) {
|
|
456
|
-
|
|
457
|
-
|
|
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));
|
|
458
381
|
}
|
|
459
|
-
async function runPlaywrightRetryAttempt({
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
} catch (retryError) {
|
|
492
|
-
lastError = retryError;
|
|
493
|
-
failedAgain.add(failedFile);
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
await Promise.all(Array.from({
|
|
498
|
-
length: Math.max(1, Math.min(retryConcurrency, failedFiles.length))
|
|
499
|
-
}, () => runNext()));
|
|
500
|
-
return {
|
|
501
|
-
failedFiles: failedFiles.filter((failedFile) => failedAgain.has(failedFile)),
|
|
502
|
-
lastError
|
|
503
|
-
};
|
|
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
|
+
};
|
|
504
414
|
}
|
|
505
415
|
async function readJson(filePath) {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
416
|
+
try {
|
|
417
|
+
const raw = await fsPromises.readFile(filePath, "utf8");
|
|
418
|
+
return JSON.parse(raw);
|
|
419
|
+
} catch {
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
512
422
|
}
|
|
513
423
|
function ensureJsxRuntimeShim(projectRoot) {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
const filePath = path.join(shimDir, file);
|
|
530
|
-
if (!fs.existsSync(filePath)) {
|
|
531
|
-
const content = `export * from "${target}";
|
|
532
|
-
export { default } from "${target}";
|
|
533
|
-
`;
|
|
534
|
-
fs.writeFileSync(filePath, content, "utf8");
|
|
535
|
-
}
|
|
536
|
-
}
|
|
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
|
+
}
|
|
537
439
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
//# sourceMappingURL=playwright.js.map
|
|
440
|
+
//#endregion
|
|
441
|
+
export { runPlaywright };
|
|
442
|
+
|
|
443
|
+
//# sourceMappingURL=playwright.js.map
|