@molecule/api-ai-tools 1.9.18 → 1.10.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/README.md +170 -19
- package/dist/command-position.d.ts +3 -1
- package/dist/command-position.d.ts.map +1 -1
- package/dist/command-position.js +95 -6
- package/dist/output-filter.d.ts +13 -6
- package/dist/output-filter.d.ts.map +1 -1
- package/dist/output-filter.js +35 -16
- package/dist/suite-overlap.d.ts +30 -1
- package/dist/suite-overlap.d.ts.map +1 -1
- package/dist/suite-overlap.js +171 -34
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +214 -24
- package/dist/trace-artifacts.d.ts +71 -0
- package/dist/trace-artifacts.d.ts.map +1 -1
- package/dist/trace-artifacts.js +398 -12
- package/dist/types.d.ts +19 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utilities.d.ts +32 -12
- package/dist/utilities.d.ts.map +1 -1
- package/dist/utilities.js +101 -33
- package/package.json +1 -1
package/dist/suite-overlap.d.ts
CHANGED
|
@@ -83,6 +83,10 @@ export declare function withSuiteConfig(command: string, config: string | null |
|
|
|
83
83
|
* suite, `--last-failed` dropped (it re-runs the tests that never ran too, X1 flash-m60) and
|
|
84
84
|
* `--retries=0` set (a re-run of a known failure does not need to pay for retries).
|
|
85
85
|
*
|
|
86
|
+
* The re-run keeps the run's `> <file>` redirect only in the sense of keeping its OUTPUT there:
|
|
87
|
+
* the target is rewritten to a fresh sibling ({@link redirectToFreshFile}), so a re-run never
|
|
88
|
+
* overwrites the log the run itself is the evidence for (xr-molecule-flash-m173).
|
|
89
|
+
*
|
|
86
90
|
* `config` is the config the run was measured under ({@link withSuiteConfig}): the narrowed re-run
|
|
87
91
|
* carries it whenever the command itself does not, so a run that dropped `--config` cannot be
|
|
88
92
|
* handed back a command with the same defect (xr-molecule-flash-m113: every failing test was
|
|
@@ -101,9 +105,34 @@ export declare function withSuiteConfig(command: string, config: string | null |
|
|
|
101
105
|
* does not name one.
|
|
102
106
|
* @param cwd - The directory `exec_command` was given, when it was; the base a location is
|
|
103
107
|
* resolved against when the command does not `cd` anywhere.
|
|
108
|
+
* @param env - The settings the REQUEST requires of the suite and this run left out
|
|
109
|
+
* ({@link withRunEnv}): the re-run carries them, so a re-run of a run measured in the wrong
|
|
110
|
+
* mode is not measured in that mode again (xr-molecule-flash-m182 +12:06).
|
|
104
111
|
* @returns The scoped command, or the command unchanged when it runs no Playwright suite.
|
|
105
112
|
*/
|
|
106
|
-
export declare function scopedRerunCommand(command: string, locations: readonly string[], excluded?: readonly string[], config?: string | null, cwd?: string | null): string;
|
|
113
|
+
export declare function scopedRerunCommand(command: string, locations: readonly string[], excluded?: readonly string[], config?: string | null, cwd?: string | null, env?: readonly RunEnvSetting[]): string;
|
|
114
|
+
/** One `NAME=value` the request requires of a suite run. */
|
|
115
|
+
export interface RunEnvSetting {
|
|
116
|
+
name: string;
|
|
117
|
+
value: string;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The command with the request's required settings put in front of its RUN statement — the ONE
|
|
121
|
+
* place every composed re-run gets them (xr-molecule-flash-m182).
|
|
122
|
+
*
|
|
123
|
+
* The settings are written at the statement's own assignment offset, never in front of the whole
|
|
124
|
+
* command: a shell binds an assignment there to the statement's program, while one in front of the
|
|
125
|
+
* whole command is the environment of its FIRST statement alone (`APP_BASE=… cd app && npx
|
|
126
|
+
* playwright test` sets the variable for `cd`; xr-molecule-flash-m103 +28:08). A name the command
|
|
127
|
+
* gives a different value is replaced, and one it already gives the required value is left as
|
|
128
|
+
* written. When the parse cannot place the statement (a run inside a nested `sh -c '…'`), the
|
|
129
|
+
* settings become `export`s, which no statement boundary can swallow.
|
|
130
|
+
*
|
|
131
|
+
* @param command - The command to carry them.
|
|
132
|
+
* @param settings - The `NAME`/`value` pairs the request requires.
|
|
133
|
+
* @returns The command, or the command unchanged when it already carries every setting.
|
|
134
|
+
*/
|
|
135
|
+
export declare function withRunEnv(command: string, settings?: readonly RunEnvSetting[]): string;
|
|
107
136
|
/**
|
|
108
137
|
* One suite run, as the rule that decides whether a second may start beside it reads it.
|
|
109
138
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"suite-overlap.d.ts","sourceRoot":"","sources":["../src/suite-overlap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAQH;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoB1D;AAKD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"suite-overlap.d.ts","sourceRoot":"","sources":["../src/suite-overlap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAQH;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoB1D;AAKD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAS1F;AAqCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,QAAQ,GAAE,SAAS,MAAM,EAAO,EAChC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,EACtB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,EACnB,GAAG,GAAE,SAAS,aAAa,EAAO,GACjC,MAAM,CA8CR;AAED,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CACd;AAmCD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,SAAS,aAAa,EAAO,GAAG,MAAM,CAe3F;AAkHD;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,SAAS,EAAE,OAAO,CAAA;IAClB,oFAAoF;IACpF,MAAM,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,gBAAgB,EACzD,SAAS,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EAC7B,QAAQ,EAAE,SAAS,CAAC,EAAE,GACrB,CAAC,GAAG,IAAI,CAMV;AA2FD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACvC;AAmCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAYtF"}
|
package/dist/suite-overlap.js
CHANGED
|
@@ -108,17 +108,48 @@ export function namesSuiteConfig(command) {
|
|
|
108
108
|
export function withSuiteConfig(command, config) {
|
|
109
109
|
if (!config)
|
|
110
110
|
return command;
|
|
111
|
-
const
|
|
112
|
-
if (
|
|
111
|
+
const run = playwrightStatement(command);
|
|
112
|
+
if (run === null)
|
|
113
113
|
return command;
|
|
114
|
-
|
|
115
|
-
const rest = command.slice(at);
|
|
116
|
-
const end = rest.search(/&&|\|\||;|\|/);
|
|
117
|
-
const statement = end === -1 ? rest : rest.slice(0, end);
|
|
118
|
-
const tail = end === -1 ? '' : rest.slice(end);
|
|
119
|
-
if (namesSuiteConfig(statement))
|
|
114
|
+
if (namesSuiteConfig(command.slice(run.start, run.end)))
|
|
120
115
|
return command;
|
|
121
|
-
|
|
116
|
+
// Right after the runner's own words (`playwright test <here>`), so a launcher in front of
|
|
117
|
+
// them — `npx --no-install`, `node <path>/playwright` — is untouched and the flag is the
|
|
118
|
+
// runner's own argument wherever it sits.
|
|
119
|
+
return `${command.slice(0, run.afterRunner)} --config ${shellQuote(config)}${command.slice(run.afterRunner)}`;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* A redirect that names a FILE for a stream whose target a re-run would destroy: `> f`, `>> f`,
|
|
123
|
+
* `1> f`, `&> f`. `2>` is stderr and deliberately not matched; a descriptor duplication
|
|
124
|
+
* (`2>&1`, `>&2`) has no file target and does not match either, because `&` cannot start one.
|
|
125
|
+
*/
|
|
126
|
+
const FILE_REDIRECT = /(^|\s)(&>>?|1?>>?)(\s*)(?:'([^']+)'|"([^"]+)"|([^\s;&|]+))/g;
|
|
127
|
+
/** A target that is not a log anyone could lose: rewriting it would CREATE a file. */
|
|
128
|
+
const DEVICE_TARGET = /^\/dev\//;
|
|
129
|
+
/**
|
|
130
|
+
* The command with every file redirect pointed at a fresh file beside its original target
|
|
131
|
+
* (`/tmp/pw-full.log` → `/tmp/pw-full.rerun.log`), and nothing else changed.
|
|
132
|
+
*
|
|
133
|
+
* A re-run composed from a run's own command keeps the run's redirect by construction, and the
|
|
134
|
+
* run wrote the walk's only report to that file: xr-molecule-flash-m173 handed over *"Send this
|
|
135
|
+
* once the run ends: … npx playwright test …/articles.spec.ts:32 --retries=0 … > /tmp/pw-full.log
|
|
136
|
+
* 2>&1"* — the ONE narrowed re-run that was about to check the fixes would have overwritten the
|
|
137
|
+
* full walk's log, destroying the other 11 failures' error reports. Following the platform's own
|
|
138
|
+
* advice must never destroy the evidence it was based on.
|
|
139
|
+
*
|
|
140
|
+
* @param command - A shell command.
|
|
141
|
+
* @returns The command whose file redirects write to a fresh target.
|
|
142
|
+
*/
|
|
143
|
+
function redirectToFreshFile(command) {
|
|
144
|
+
return command.replace(FILE_REDIRECT, (whole, before, op, space, single, double, bare) => {
|
|
145
|
+
const target = single ?? double ?? bare ?? '';
|
|
146
|
+
// `/dev/null` and friends are not a report to preserve; leaving them keeps the re-run silent
|
|
147
|
+
// the way the run was, where rewriting them would create a file nobody asked for.
|
|
148
|
+
if (!target || DEVICE_TARGET.test(target))
|
|
149
|
+
return whole;
|
|
150
|
+
const quote = single !== undefined ? "'" : double !== undefined ? '"' : '';
|
|
151
|
+
return `${before}${op}${space}${quote}${target}.rerun.log${quote}`;
|
|
152
|
+
});
|
|
122
153
|
}
|
|
123
154
|
/**
|
|
124
155
|
* The whole-suite command made scoped to the tests it should re-run instead: the same
|
|
@@ -126,6 +157,10 @@ export function withSuiteConfig(command, config) {
|
|
|
126
157
|
* suite, `--last-failed` dropped (it re-runs the tests that never ran too, X1 flash-m60) and
|
|
127
158
|
* `--retries=0` set (a re-run of a known failure does not need to pay for retries).
|
|
128
159
|
*
|
|
160
|
+
* The re-run keeps the run's `> <file>` redirect only in the sense of keeping its OUTPUT there:
|
|
161
|
+
* the target is rewritten to a fresh sibling ({@link redirectToFreshFile}), so a re-run never
|
|
162
|
+
* overwrites the log the run itself is the evidence for (xr-molecule-flash-m173).
|
|
163
|
+
*
|
|
129
164
|
* `config` is the config the run was measured under ({@link withSuiteConfig}): the narrowed re-run
|
|
130
165
|
* carries it whenever the command itself does not, so a run that dropped `--config` cannot be
|
|
131
166
|
* handed back a command with the same defect (xr-molecule-flash-m113: every failing test was
|
|
@@ -144,19 +179,23 @@ export function withSuiteConfig(command, config) {
|
|
|
144
179
|
* does not name one.
|
|
145
180
|
* @param cwd - The directory `exec_command` was given, when it was; the base a location is
|
|
146
181
|
* resolved against when the command does not `cd` anywhere.
|
|
182
|
+
* @param env - The settings the REQUEST requires of the suite and this run left out
|
|
183
|
+
* ({@link withRunEnv}): the re-run carries them, so a re-run of a run measured in the wrong
|
|
184
|
+
* mode is not measured in that mode again (xr-molecule-flash-m182 +12:06).
|
|
147
185
|
* @returns The scoped command, or the command unchanged when it runs no Playwright suite.
|
|
148
186
|
*/
|
|
149
|
-
export function scopedRerunCommand(command, locations, excluded = [], config, cwd) {
|
|
150
|
-
const
|
|
151
|
-
if (
|
|
187
|
+
export function scopedRerunCommand(command, locations, excluded = [], config, cwd, env = []) {
|
|
188
|
+
const run = playwrightStatement(command);
|
|
189
|
+
if (run === null)
|
|
152
190
|
return command;
|
|
153
|
-
const head = command.slice(0, at);
|
|
154
|
-
const rest = command.slice(at);
|
|
155
191
|
// Only the first statement runs the suite; the rest (`| tail -50`, `&& echo done`)
|
|
156
192
|
// is left exactly as it was.
|
|
157
|
-
const
|
|
158
|
-
const
|
|
159
|
-
|
|
193
|
+
const statement = command.slice(run.start, run.end);
|
|
194
|
+
const tail = command.slice(run.end);
|
|
195
|
+
// The arguments after the runner's own words (`playwright test`), where the narrowed re-run
|
|
196
|
+
// writes its locations — the launcher in front of them (`npx --no-install`, `node
|
|
197
|
+
// <path>/playwright`) is not part of the arguments.
|
|
198
|
+
const args = command.slice(run.afterRunner, run.end);
|
|
160
199
|
// One literal alternation, regex-escaped and shell-quoted: a title carries parentheses and
|
|
161
200
|
// quotes (`should only allow comment author to delete`) that are regex and shell syntax alike.
|
|
162
201
|
const invert = excluded.length > 0 && !/--grep-invert\b/.test(command)
|
|
@@ -176,16 +215,79 @@ export function scopedRerunCommand(command, locations, excluded = [], config, cw
|
|
|
176
215
|
// nothing — "No tests found" — so every location is rendered in a form Playwright can match
|
|
177
216
|
// ({@link playwrightLocations}).
|
|
178
217
|
const narrowed = playwrightLocations(locations, command, cwd);
|
|
179
|
-
const
|
|
218
|
+
const dropped = dropSupersededSpecFiles(args, narrowed)
|
|
180
219
|
.replace(/\s*--last-failed\b/g, '')
|
|
181
|
-
.replace(/\s*--retries(?:[= ]\d+)?/g, '')
|
|
182
|
-
|
|
220
|
+
.replace(/\s*--retries(?:[= ]\d+)?/g, '');
|
|
221
|
+
const scoped = command.slice(0, run.afterRunner) +
|
|
222
|
+
` ${narrowed.join(' ')} --retries=0${carry}${invert}` +
|
|
223
|
+
dropped;
|
|
183
224
|
// A command that starts the suite AGAIN after this statement is a chain of walks, not a re-run:
|
|
184
225
|
// everything from the next suite runner on is dropped, so the composed command carries ONE walk.
|
|
185
226
|
// X1 xr-molecule-flash-m157 (+14:02) was handed a re-run that still carried the chain's second
|
|
186
227
|
// whole walk — 4.5 minutes measuring code it had not changed.
|
|
187
228
|
const rerunTail = suiteRunStatements(tail).length > 0 ? '' : tail;
|
|
188
|
-
return `${
|
|
229
|
+
return withRunEnv(`${redirectToFreshFile(scoped)}${redirectToFreshFile(rerunTail)}`, env);
|
|
230
|
+
}
|
|
231
|
+
/** A `NAME=value` word in command position, optionally behind `export`/`env`. */
|
|
232
|
+
const ENV_WORD = /(^|[\s;&|(])(?:export[ \t]+|env[ \t]+)?([A-Za-z_][A-Za-z0-9_]*)=('[^']*'|"[^"]*"|[^\s;&|]+)/g;
|
|
233
|
+
/** The value a command gives a name, unquoted, or null when it sets none. */
|
|
234
|
+
function envValueOf(command, name) {
|
|
235
|
+
for (const m of command.matchAll(ENV_WORD)) {
|
|
236
|
+
if (m[2] !== name)
|
|
237
|
+
continue;
|
|
238
|
+
return (m[3] ?? '').replace(/^(['"])(.*)\1$/, '$2');
|
|
239
|
+
}
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
/** The command with one `NAME=value` word taken out, whitespace and all. */
|
|
243
|
+
function withoutEnvWord(command, name) {
|
|
244
|
+
const pattern = new RegExp(`(^|[\\s;&|(])(?:export[ \\t]+|env[ \\t]+)?${name}=('[^']*'|"[^"]*"|[^\\s;&|]+)`);
|
|
245
|
+
const m = pattern.exec(command);
|
|
246
|
+
if (!m)
|
|
247
|
+
return command;
|
|
248
|
+
const start = m.index + m[1].length;
|
|
249
|
+
const end = m.index + m[0].length;
|
|
250
|
+
const left = /[ \t]*$/.exec(command.slice(0, start))?.[0] ?? '';
|
|
251
|
+
const chain = /^[ \t]*(?:&&|\|\||;)[ \t]*/.exec(command.slice(end));
|
|
252
|
+
// A word between two chained statements takes the following operator with it, so the result is
|
|
253
|
+
// `a && b` and not `a && && b`.
|
|
254
|
+
if (chain && left.length > 0)
|
|
255
|
+
return command.slice(0, start) + command.slice(end + chain[0].length);
|
|
256
|
+
if (left.length > 0)
|
|
257
|
+
return command.slice(0, start - left.length) + command.slice(end);
|
|
258
|
+
const right = /^[ \t]*/.exec(command.slice(end))?.[0] ?? '';
|
|
259
|
+
return command.slice(0, start) + command.slice(end + right.length);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* The command with the request's required settings put in front of its RUN statement — the ONE
|
|
263
|
+
* place every composed re-run gets them (xr-molecule-flash-m182).
|
|
264
|
+
*
|
|
265
|
+
* The settings are written at the statement's own assignment offset, never in front of the whole
|
|
266
|
+
* command: a shell binds an assignment there to the statement's program, while one in front of the
|
|
267
|
+
* whole command is the environment of its FIRST statement alone (`APP_BASE=… cd app && npx
|
|
268
|
+
* playwright test` sets the variable for `cd`; xr-molecule-flash-m103 +28:08). A name the command
|
|
269
|
+
* gives a different value is replaced, and one it already gives the required value is left as
|
|
270
|
+
* written. When the parse cannot place the statement (a run inside a nested `sh -c '…'`), the
|
|
271
|
+
* settings become `export`s, which no statement boundary can swallow.
|
|
272
|
+
*
|
|
273
|
+
* @param command - The command to carry them.
|
|
274
|
+
* @param settings - The `NAME`/`value` pairs the request requires.
|
|
275
|
+
* @returns The command, or the command unchanged when it already carries every setting.
|
|
276
|
+
*/
|
|
277
|
+
export function withRunEnv(command, settings = []) {
|
|
278
|
+
const added = [];
|
|
279
|
+
let out = command;
|
|
280
|
+
for (const setting of settings) {
|
|
281
|
+
if (envValueOf(out, setting.name) === setting.value)
|
|
282
|
+
continue;
|
|
283
|
+
out = withoutEnvWord(out, setting.name);
|
|
284
|
+
added.push(`${setting.name}=${setting.value}`);
|
|
285
|
+
}
|
|
286
|
+
if (added.length === 0)
|
|
287
|
+
return command;
|
|
288
|
+
const at = locatedCommandSegments(out).find((entry) => entry.segment !== null && PLAYWRIGHT_RUN.test(entry.segment.text))?.assignmentOffset ?? null;
|
|
289
|
+
const words = added.join(' ');
|
|
290
|
+
return at === null ? `export ${words} && ${out}` : `${out.slice(0, at)}${words} ${out.slice(at)}`;
|
|
189
291
|
}
|
|
190
292
|
/** A directory a `cd` can name literally: no `~`, no variable, no glob. */
|
|
191
293
|
const LITERAL_DIR = /^[A-Za-z0-9_./+-]+$/;
|
|
@@ -267,7 +369,8 @@ function playwrightLocations(locations, command, cwd) {
|
|
|
267
369
|
/** A spec-file argument Playwright accepts as its positional filter — never a flag. */
|
|
268
370
|
const SPEC_FILE_ARGUMENT = /^(?!-)[^\s]*\.(?:spec|test)\.[cm]?[jt]sx?(?::\d+(?::\d+)?)?$/;
|
|
269
371
|
/**
|
|
270
|
-
* The
|
|
372
|
+
* The runner's own arguments with every bare spec-file argument taken out once the re-run names
|
|
373
|
+
* failure locations.
|
|
271
374
|
*
|
|
272
375
|
* The re-run is "just these": Playwright matches each positional argument against every collected
|
|
273
376
|
* file's path, so a bare file left beside the `file:line`s re-admits ALL of that file's tests.
|
|
@@ -276,23 +379,20 @@ const SPEC_FILE_ARGUMENT = /^(?!-)[^\s]*\.(?:spec|test)\.[cm]?[jt]sx?(?::\d+(?::
|
|
|
276
379
|
* failures sat in two of them — 32 tests, ~3 min, instead of the 8 failures' ~30 s. A file with no
|
|
277
380
|
* failure in it has nothing to re-run, so it goes too.
|
|
278
381
|
*
|
|
279
|
-
*
|
|
382
|
+
* The launcher's words are not arguments and are not here: the caller passes what follows
|
|
383
|
+
* `playwright test` ({@link playwrightStatement}'s `afterRunner`), so `node <path>/playwright
|
|
384
|
+
* test` and `npx playwright test` lose the same spec files.
|
|
385
|
+
*
|
|
386
|
+
* @param args - The run's own arguments, after `playwright test`.
|
|
280
387
|
* @param locations - The failing `file:line`s the re-run names.
|
|
281
|
-
* @returns The
|
|
388
|
+
* @returns The arguments without their bare spec-file arguments.
|
|
282
389
|
*/
|
|
283
|
-
function dropSupersededSpecFiles(
|
|
390
|
+
function dropSupersededSpecFiles(args, locations) {
|
|
284
391
|
if (locations.length === 0)
|
|
285
|
-
return
|
|
286
|
-
|
|
287
|
-
if (runner === null)
|
|
288
|
-
return statement;
|
|
289
|
-
const head = runner[1];
|
|
290
|
-
const kept = statement
|
|
291
|
-
.slice(head.length)
|
|
292
|
-
.replace(/(^|\s+)([^\s]+)(?=\s|$)/g, (whole, _space, token) => {
|
|
392
|
+
return args;
|
|
393
|
+
return args.replace(/(^|\s+)([^\s]+)(?=\s|$)/g, (whole, _space, token) => {
|
|
293
394
|
return SPEC_FILE_ARGUMENT.test(token) ? '' : whole;
|
|
294
395
|
});
|
|
295
|
-
return `${head}${kept}`;
|
|
296
396
|
}
|
|
297
397
|
/**
|
|
298
398
|
* The run in flight that makes a second run a hazard, or null when nothing is in the way.
|
|
@@ -320,6 +420,43 @@ export function suiteRunInTheWay(candidate, inFlight) {
|
|
|
320
420
|
* closing quote is that echo. A version-pinned `playwright@1.44.0 test` is the same run.
|
|
321
421
|
*/
|
|
322
422
|
const PLAYWRIGHT_RUN = /^(?:@playwright\/test|playwright)(?:@\S+)?\s+test(?=\s|$)/;
|
|
423
|
+
/**
|
|
424
|
+
* The statement of a command that runs `playwright test`, and where its own arguments begin.
|
|
425
|
+
*
|
|
426
|
+
* Both rewrites that compose a command ({@link withSuiteConfig}, {@link scopedRerunCommand}) read
|
|
427
|
+
* the run through the shared shell parse — the same answer every classifier gets — instead of
|
|
428
|
+
* searching the text for `playwright test`. The search missed the runner reached by file (`node
|
|
429
|
+
* <path>/@playwright/test/cli.js test`, xr-molecule-flash-m179), so a narrowed re-run of that run
|
|
430
|
+
* silently stayed the whole suite.
|
|
431
|
+
*
|
|
432
|
+
* `afterRunner` is the offset just past the runner's own words (`playwright test`): the
|
|
433
|
+
* sub-command is the word after the program word, wherever the program word is (the script path
|
|
434
|
+
* behind `node`, the name itself otherwise), so an option written there is the run's own.
|
|
435
|
+
*
|
|
436
|
+
* @param command - The shell command.
|
|
437
|
+
* @returns The statement's span and its argument offset, or null when no statement runs the suite.
|
|
438
|
+
*/
|
|
439
|
+
function playwrightStatement(command) {
|
|
440
|
+
for (const located of locatedCommandSegments(command)) {
|
|
441
|
+
const segment = located.segment;
|
|
442
|
+
if (!segment || !PLAYWRIGHT_RUN.test(segment.text))
|
|
443
|
+
continue;
|
|
444
|
+
// A statement inside a nested `sh -c '<command>'` has no offsets here: its positions are
|
|
445
|
+
// positions in that string, so nothing may be spliced at them.
|
|
446
|
+
if (located.start === null || located.end === null || located.programOffset === null)
|
|
447
|
+
return null;
|
|
448
|
+
const words = [...command.slice(located.programOffset).matchAll(/\S+/g)];
|
|
449
|
+
const subcommand = words[1];
|
|
450
|
+
if (subcommand === undefined)
|
|
451
|
+
return null;
|
|
452
|
+
return {
|
|
453
|
+
start: located.start,
|
|
454
|
+
end: located.end,
|
|
455
|
+
afterRunner: located.programOffset + (subcommand.index ?? 0) + subcommand[0].length,
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
323
460
|
/**
|
|
324
461
|
* What narrows a Playwright run to a part of the suite: a named file or line, a title filter, or
|
|
325
462
|
* the tests the run before it printed (`--last-failed`, `--only-changed`).
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AA4K9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAahG;AA6ZD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAqFnE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAEA,CAAA;AAE9B;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,QAI6C,CAAA;AAmBlF;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUhE;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,8FAA8F;IAC9F,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAC5B;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAA;AAoBpF;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,CAAC,GACxE,qBAAqB,CAkBvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,qBAAqB,GAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiCxB;AA2GD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CAguFxF"}
|