@empiricalrun/playwright-utils 0.58.6 → 0.60.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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-case-sync.d.ts","sourceRoot":"","sources":["../../src/reporter/test-case-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,oBAAoB,EAErB,MAAM,6CAA6C,CAAC;AACrD,OAAO,KAAK,EACV,UAAU,EAEV,KAAK,EAEN,MAAM,2BAA2B,CAAC;AAUnC,YAAY,EACV,uBAAuB,EACvB,6BAA6B,EAC7B,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,6CAA6C,CAAC;
|
|
1
|
+
{"version":3,"file":"test-case-sync.d.ts","sourceRoot":"","sources":["../../src/reporter/test-case-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,oBAAoB,EAErB,MAAM,6CAA6C,CAAC;AACrD,OAAO,KAAK,EACV,UAAU,EAEV,KAAK,EAEN,MAAM,2BAA2B,CAAC;AAUnC,YAAY,EACV,uBAAuB,EACvB,6BAA6B,EAC7B,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,6CAA6C,CAAC;AAgUrD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,KAAK,GACf,oBAAoB,CAyBtB"}
|
|
@@ -15,6 +15,24 @@ function getSuiteParallelMode(suite) {
|
|
|
15
15
|
? mode
|
|
16
16
|
: "none";
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* The project's per-project `workers` cap, if it declares one.
|
|
20
|
+
*
|
|
21
|
+
* The public `FullProject` deliberately omits `workers`, so this reads the
|
|
22
|
+
* internal `FullProjectInternal` that Playwright attaches to every project
|
|
23
|
+
* suite (`buildProjectSuite` in `runner/loadUtils.ts` — the same object
|
|
24
|
+
* `suite.project()` resolves through, so it is present wherever projects
|
|
25
|
+
* are). Only explicit per-project caps surface here: the global `workers`
|
|
26
|
+
* setting resolves per machine (a `'50%'` default is a different number on
|
|
27
|
+
* the listing container than on a shard) and is not a per-project signal.
|
|
28
|
+
*/
|
|
29
|
+
function getProjectWorkerCap(suite) {
|
|
30
|
+
const workers = suite
|
|
31
|
+
._fullProject?.workers;
|
|
32
|
+
return typeof workers === "number" && Number.isFinite(workers) && workers >= 1
|
|
33
|
+
? workers
|
|
34
|
+
: undefined;
|
|
35
|
+
}
|
|
18
36
|
function toPosixPath(value) {
|
|
19
37
|
return value.split(path_1.default.sep).join("/");
|
|
20
38
|
}
|
|
@@ -41,7 +59,7 @@ function stableGroupId(input) {
|
|
|
41
59
|
input.column,
|
|
42
60
|
]))
|
|
43
61
|
.digest("hex");
|
|
44
|
-
return
|
|
62
|
+
return `${input.kind}:${hash}`;
|
|
45
63
|
}
|
|
46
64
|
function collectDirectTestCases(rootDir, suite, state) {
|
|
47
65
|
return suite.tests.map((test) => ({
|
|
@@ -53,11 +71,7 @@ function collectDirectTestCases(rootDir, suite, state) {
|
|
|
53
71
|
tags: normalizeTags(test.tags),
|
|
54
72
|
}));
|
|
55
73
|
}
|
|
56
|
-
function
|
|
57
|
-
if (state.serialAncestor || getSuiteParallelMode(suite) !== "serial") {
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
const tests = suite.allTests();
|
|
74
|
+
function buildDependencyGroup(rootDir, suite, state, kind, tests) {
|
|
61
75
|
if (tests.length < 2) {
|
|
62
76
|
return null;
|
|
63
77
|
}
|
|
@@ -71,13 +85,14 @@ function collectOutermostSerialGroup(rootDir, suite, state) {
|
|
|
71
85
|
const location = suite.location ?? firstTest.location;
|
|
72
86
|
return {
|
|
73
87
|
id: stableGroupId({
|
|
88
|
+
kind,
|
|
74
89
|
projectName,
|
|
75
90
|
filePath,
|
|
76
91
|
titlePath,
|
|
77
92
|
line: location.line,
|
|
78
93
|
column: location.column,
|
|
79
94
|
}),
|
|
80
|
-
kind
|
|
95
|
+
kind,
|
|
81
96
|
file_path: filePath,
|
|
82
97
|
title_path: titlePath,
|
|
83
98
|
playwright_project: projectName,
|
|
@@ -87,14 +102,95 @@ function collectOutermostSerialGroup(rootDir, suite, state) {
|
|
|
87
102
|
})),
|
|
88
103
|
};
|
|
89
104
|
}
|
|
105
|
+
function collectOutermostSerialGroup(rootDir, suite, state) {
|
|
106
|
+
if (state.serialAncestor || getSuiteParallelMode(suite) !== "serial") {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
return buildDependencyGroup(rootDir, suite, state, "playwright_serial", suite.allTests());
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Tests under this suite that Playwright would place in its sequential
|
|
113
|
+
* ("general") group: everything except subtrees that opt back into
|
|
114
|
+
* parallelism. Order is declaration order, which is the order the group
|
|
115
|
+
* executes in.
|
|
116
|
+
*/
|
|
117
|
+
function sequentialTests(suite) {
|
|
118
|
+
const tests = [...suite.tests];
|
|
119
|
+
for (const child of suite.suites) {
|
|
120
|
+
if (getSuiteParallelMode(child) !== "parallel") {
|
|
121
|
+
tests.push(...sequentialTests(child));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return tests;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The `playwright_default` counterpart of the serial collector: the outermost
|
|
128
|
+
* scope whose tests Playwright schedules as one ordered worker group without
|
|
129
|
+
* serial semantics.
|
|
130
|
+
*
|
|
131
|
+
* Two shapes qualify (mirroring `createTestGroups` in Playwright's
|
|
132
|
+
* `runner/testGroups.ts`):
|
|
133
|
+
*
|
|
134
|
+
* - a `test.describe.configure({ mode: "default" })` suite — grouped whole,
|
|
135
|
+
* including any nested parallel subtree, because Playwright keys the group
|
|
136
|
+
* on the outermost sequential suite;
|
|
137
|
+
* - a file with no explicit mode in a non-fullyParallel project — Playwright's
|
|
138
|
+
* per-file "general" group, which excludes subtrees that opt back into
|
|
139
|
+
* `parallel`.
|
|
140
|
+
*/
|
|
141
|
+
function collectOutermostDefaultGroup(rootDir, suite, state) {
|
|
142
|
+
if (state.sequentialAncestor) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
const mode = getSuiteParallelMode(suite);
|
|
146
|
+
if (mode === "default") {
|
|
147
|
+
return buildDependencyGroup(rootDir, suite, state, "playwright_default", suite.allTests());
|
|
148
|
+
}
|
|
149
|
+
if (suite.type === "file" && !state.insideParallel && mode === "none") {
|
|
150
|
+
return buildDependencyGroup(rootDir, suite, state, "playwright_default", sequentialTests(suite));
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
90
154
|
function walkSuite(rootDir, suite, state, testCases, dependencyGroups) {
|
|
91
155
|
const isDescribe = suite.type === "describe";
|
|
92
156
|
const suites = isDescribe ? [...state.suites, suite.title] : state.suites;
|
|
93
157
|
const nextState = { ...state, suites };
|
|
158
|
+
const mode = getSuiteParallelMode(suite);
|
|
159
|
+
// A per-project `workers` cap constrains the whole project: the dispatcher
|
|
160
|
+
// enforces it per process (`_workerLimitPerProjectId`), so the guarantee
|
|
161
|
+
// only survives sharding if every test of the project lands on one shard.
|
|
162
|
+
// Emitted alongside any inner groups — it is the outermost scope, so it
|
|
163
|
+
// wins for scheduling and keeps the inner ones whole for free.
|
|
164
|
+
if (suite.type === "project") {
|
|
165
|
+
const workerCap = getProjectWorkerCap(suite);
|
|
166
|
+
if (workerCap !== undefined) {
|
|
167
|
+
const capGroup = buildDependencyGroup(rootDir, suite, nextState, "playwright_worker_cap", suite.allTests());
|
|
168
|
+
if (capGroup) {
|
|
169
|
+
dependencyGroups.push({ ...capGroup, worker_limit: workerCap });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// A serial group is emitted even inside a default scope: the outer default
|
|
174
|
+
// group wins for scheduling, but serial membership carries extra semantics
|
|
175
|
+
// (failure cascade, re-expansion) its consumers still need.
|
|
94
176
|
const serialGroup = collectOutermostSerialGroup(rootDir, suite, nextState);
|
|
95
177
|
if (serialGroup) {
|
|
96
178
|
dependencyGroups.push(serialGroup);
|
|
97
179
|
nextState.serialAncestor = suite;
|
|
180
|
+
if (!nextState.sequentialAncestor) {
|
|
181
|
+
nextState.sequentialAncestor = suite;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const defaultGroup = collectOutermostDefaultGroup(rootDir, suite, nextState);
|
|
185
|
+
if (defaultGroup) {
|
|
186
|
+
dependencyGroups.push(defaultGroup);
|
|
187
|
+
nextState.sequentialAncestor = suite;
|
|
188
|
+
}
|
|
189
|
+
// fullyParallel (project suite) or an explicit `mode: "parallel"` — every
|
|
190
|
+
// descendant is independently schedulable unless a sequential suite below
|
|
191
|
+
// says otherwise.
|
|
192
|
+
if (mode === "parallel") {
|
|
193
|
+
nextState.insideParallel = true;
|
|
98
194
|
}
|
|
99
195
|
testCases.push(...collectDirectTestCases(rootDir, suite, nextState));
|
|
100
196
|
for (const child of suite.suites) {
|
|
@@ -112,6 +208,15 @@ function walkSuite(rootDir, suite, state, testCases, dependencyGroups) {
|
|
|
112
208
|
: "";
|
|
113
209
|
childState.suites = [];
|
|
114
210
|
childState.serialAncestor = null;
|
|
211
|
+
childState.sequentialAncestor = null;
|
|
212
|
+
}
|
|
213
|
+
else if (childState.sequentialAncestor &&
|
|
214
|
+
getSuiteParallelMode(childState.sequentialAncestor) === "none" &&
|
|
215
|
+
getSuiteParallelMode(child) === "parallel") {
|
|
216
|
+
// The scope was a plain file's "general" group, which excludes subtrees
|
|
217
|
+
// that opt back into parallel — so below this child, sequential suites
|
|
218
|
+
// start their own groups again.
|
|
219
|
+
childState.sequentialAncestor = null;
|
|
115
220
|
}
|
|
116
221
|
walkSuite(rootDir, child, childState, testCases, dependencyGroups);
|
|
117
222
|
}
|
|
@@ -125,6 +230,8 @@ function collectTestCaseSyncManifest(config, rootSuite) {
|
|
|
125
230
|
filePath: "",
|
|
126
231
|
suites: [],
|
|
127
232
|
serialAncestor: null,
|
|
233
|
+
sequentialAncestor: null,
|
|
234
|
+
insideParallel: false,
|
|
128
235
|
}, testCases, dependencyGroups);
|
|
129
236
|
return {
|
|
130
237
|
version: 1,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empiricalrun/playwright-utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.60.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/",
|
|
6
6
|
"access": "public"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"openapi-typescript": "7.6.1",
|
|
42
42
|
"playwright-core": "1.62.1",
|
|
43
43
|
"serve-handler": "^6.1.6",
|
|
44
|
-
"@empiricalrun/shared-types": "0.
|
|
44
|
+
"@empiricalrun/shared-types": "0.17.0"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@babel/code-frame": "^7.24.7",
|