@elench/testkit 0.1.30 → 0.1.31

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.
@@ -631,7 +631,7 @@ async function runPlaywrightBatch(targetConfig, batch) {
631
631
  }
632
632
 
633
633
  console.log(
634
- `\n── ${targetConfig.workerLabel} playwright:${targetConfig.name} (${batch.tasks.length} file${batch.tasks.length === 1 ? "" : "s"}) ──`
634
+ `\n── ${targetConfig.workerLabel} playwright:${targetConfig.name} (${batch.tasks.length} file${batch.tasks.length === 1 ? "" : "s"})${formatPlaywrightBatchFiles(batch)} ──`
635
635
  );
636
636
 
637
637
  const cwd = resolveServiceCwd(targetConfig.productDir, local.cwd);
@@ -917,6 +917,15 @@ function formatBatchDescriptor(batch) {
917
917
  return frameworkLabel ? ` (${frameworkLabel}, ${fileLabel})` : ` (${fileLabel})`;
918
918
  }
919
919
 
920
+ function formatPlaywrightBatchFiles(batch) {
921
+ if (!batch?.tasks?.length) return "";
922
+ const files = batch.tasks.map((task) => task.file);
923
+ if (files.length === 1) return ` · ${files[0]}`;
924
+ const preview = files.slice(0, 3).join(", ");
925
+ const suffix = files.length > 3 ? `, +${files.length - 3} more` : "";
926
+ return ` · ${preview}${suffix}`;
927
+ }
928
+
920
929
  function formatFrameworkLabel(framework) {
921
930
  if (!framework || framework === "k6") return "";
922
931
  return framework;
@@ -69,7 +69,9 @@ export function collectSuites(config, suiteType, suiteNames, frameworkFilter, fi
69
69
  ? Math.max(2, files.length)
70
70
  : Math.max(1, files.length)),
71
71
  maxFileConcurrency:
72
- framework === "k6" ? suite.testkit?.maxFileConcurrency || 1 : 1,
72
+ framework === "k6" || framework === "playwright"
73
+ ? suite.testkit?.maxFileConcurrency || 1
74
+ : 1,
73
75
  });
74
76
  orderIndex += 1;
75
77
  }
@@ -183,9 +185,7 @@ export function buildTaskQueue(servicePlans, graphs, timings) {
183
185
  timingKey,
184
186
  estimatedDurationMs: estimateTaskDuration(timings, timingKey, suite),
185
187
  maxBatchSize:
186
- suite.framework === "playwright"
187
- ? Number.POSITIVE_INFINITY
188
- : suite.maxFileConcurrency || 1,
188
+ suite.maxFileConcurrency || 1,
189
189
  });
190
190
  nextId += 1;
191
191
  }
@@ -213,20 +213,9 @@ export function claimNextBatch(queue, preferredGraphKey) {
213
213
  const seed = queue.splice(index, 1)[0];
214
214
  const tasks = [seed];
215
215
 
216
- if (seed.framework === "playwright") {
217
- for (let cursor = queue.length - 1; cursor >= 0; cursor -= 1) {
218
- const candidate = queue[cursor];
219
- if (
220
- candidate.framework === "playwright" &&
221
- candidate.graphKey === seed.graphKey &&
222
- candidate.targetName === seed.targetName
223
- ) {
224
- tasks.push(candidate);
225
- queue.splice(cursor, 1);
226
- }
227
- }
228
- } else if (seed.maxBatchSize > 1) {
229
- for (let cursor = queue.length - 1; cursor >= 0 && tasks.length < seed.maxBatchSize; cursor -= 1) {
216
+ if (seed.maxBatchSize > 1) {
217
+ for (let cursor = 0; cursor < queue.length; cursor += 1) {
218
+ if (tasks.length >= seed.maxBatchSize) break;
230
219
  const candidate = queue[cursor];
231
220
  if (
232
221
  candidate.framework === seed.framework &&
@@ -237,6 +226,7 @@ export function claimNextBatch(queue, preferredGraphKey) {
237
226
  ) {
238
227
  tasks.push(candidate);
239
228
  queue.splice(cursor, 1);
229
+ cursor -= 1;
240
230
  }
241
231
  }
242
232
  }
@@ -137,11 +137,51 @@ describe("runner-planning", () => {
137
137
  expect(queue).toHaveLength(4);
138
138
 
139
139
  const firstBatch = claimNextBatch(queue, "api|frontend");
140
- expect(firstBatch.tasks).toHaveLength(2);
140
+ expect(firstBatch.tasks).toHaveLength(1);
141
141
  expect(firstBatch.framework).toBe("playwright");
142
142
 
143
143
  const secondBatch = claimNextBatch(queue, "api|frontend");
144
- expect(secondBatch.tasks).toHaveLength(2);
145
- expect(secondBatch.framework).toBe("k6");
144
+ expect(secondBatch.tasks).toHaveLength(1);
145
+ expect(secondBatch.framework).toBe("playwright");
146
+
147
+ const thirdBatch = claimNextBatch(queue, "api|frontend");
148
+ expect(thirdBatch.tasks).toHaveLength(2);
149
+ expect(thirdBatch.framework).toBe("k6");
150
+ });
151
+
152
+ it("allows Playwright suites to opt into bounded multi-file batches", () => {
153
+ const frontend = makeConfig("frontend");
154
+ const plans = [
155
+ {
156
+ config: frontend,
157
+ skipped: false,
158
+ runtimeConfigs: [frontend],
159
+ runtimeNames: ["frontend"],
160
+ runtimeKey: "frontend",
161
+ suites: [
162
+ {
163
+ name: "auth",
164
+ type: "e2e",
165
+ framework: "playwright",
166
+ files: ["a.spec.js", "b.spec.js", "c.spec.js"],
167
+ orderIndex: 0,
168
+ weight: 3,
169
+ maxFileConcurrency: 2,
170
+ },
171
+ ],
172
+ },
173
+ ];
174
+
175
+ const graphs = buildRuntimeGraphs(plans);
176
+ const queue = buildTaskQueue(plans, graphs, { files: {} });
177
+
178
+ const firstBatch = claimNextBatch(queue, "frontend");
179
+ expect(firstBatch.tasks.map((task) => task.file)).toEqual(["a.spec.js", "b.spec.js"]);
180
+ expect(firstBatch.tasks).toHaveLength(2);
181
+ expect(firstBatch.framework).toBe("playwright");
182
+
183
+ const secondBatch = claimNextBatch(queue, "frontend");
184
+ expect(secondBatch.tasks).toHaveLength(1);
185
+ expect(secondBatch.framework).toBe("playwright");
146
186
  });
147
187
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "description": "CLI for discovering and running local HTTP, DAL, and Playwright test suites",
5
5
  "type": "module",
6
6
  "types": "./lib/index.d.ts",