plum-e2e 1.3.3 → 1.3.5
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 +41 -24
- package/backend/config/scripts/create-step.mjs +15 -14
- package/backend/lib/runnerProcess.js +2 -3
- package/backend/scripts/manage-runners.mjs +6 -17
- package/backend/services/cronService.js +16 -5
- package/backend/websockets/socketHandler.js +23 -5
- package/bin/plum.js +262 -220
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/Badge.DLLowvEA.css +17 -0
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/Button.cBruH0aD.css +17 -0
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_layout.D7eM-6MV.css +17 -0
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.BVnUajEa.css +17 -0
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.CGnCsn5q.css +17 -0
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.DBhBrHFz.css +17 -0
- package/frontend/.svelte-kit/adapter-node/_app/immutable/assets/_page.DOqo0UR4.css +17 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/0.CnXRuPt4.css +17 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/2.CGnCsn5q.css +17 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/3.BVnUajEa.css +17 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/4.DBhBrHFz.css +17 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/5.D93VAB-w.css +17 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/Badge.DLLowvEA.css +17 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/Button.cBruH0aD.css +17 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_layout.D7eM-6MV.css +17 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.BVnUajEa.css +17 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.CGnCsn5q.css +17 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.DBhBrHFz.css +17 -0
- package/frontend/.svelte-kit/output/client/_app/immutable/assets/_page.DOqo0UR4.css +17 -0
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/Badge.DLLowvEA.css +17 -0
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/Button.cBruH0aD.css +17 -0
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_layout.D7eM-6MV.css +17 -0
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.BVnUajEa.css +17 -0
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.CGnCsn5q.css +17 -0
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.DBhBrHFz.css +17 -0
- package/frontend/.svelte-kit/output/server/_app/immutable/assets/_page.DOqo0UR4.css +17 -0
- package/frontend/build/client/_app/immutable/assets/0.CnXRuPt4.css +17 -0
- package/frontend/build/client/_app/immutable/assets/2.CGnCsn5q.css +17 -0
- package/frontend/build/client/_app/immutable/assets/3.BVnUajEa.css +17 -0
- package/frontend/build/client/_app/immutable/assets/4.DBhBrHFz.css +17 -0
- package/frontend/build/client/_app/immutable/assets/5.D93VAB-w.css +17 -0
- package/frontend/build/client/_app/immutable/assets/Badge.DLLowvEA.css +17 -0
- package/frontend/build/client/_app/immutable/assets/Button.cBruH0aD.css +17 -0
- package/frontend/build/client/_app/immutable/assets/_layout.D7eM-6MV.css +17 -0
- package/frontend/build/client/_app/immutable/assets/_page.BVnUajEa.css +17 -0
- package/frontend/build/client/_app/immutable/assets/_page.CGnCsn5q.css +17 -0
- package/frontend/build/client/_app/immutable/assets/_page.DBhBrHFz.css +17 -0
- package/frontend/build/client/_app/immutable/assets/_page.DOqo0UR4.css +17 -0
- package/frontend/src/app.css +0 -16
- package/frontend/src/lib/constants.js +1 -2
- package/frontend/src/lib/stores/theme.js +0 -17
- package/frontend/src/lib/styles/global.css +0 -16
- package/frontend/src/lib/styles/reset.css +0 -16
- package/frontend/src/lib/styles/tokens.css +0 -16
- package/frontend/src/routes/reports/live/+page.svelte +2 -2
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -282,11 +282,11 @@ plum run-test --parallel 4 @suite-login # run a suite in parallel
|
|
|
282
282
|
plum run-test --browser firefox # run in a specific browser
|
|
283
283
|
```
|
|
284
284
|
|
|
285
|
-
| Flag | Description
|
|
286
|
-
| ------------------ |
|
|
287
|
-
| `@tag` | Run only tests matching the tag
|
|
288
|
-
| `--parallel <n>` | Run across `n` parallel workers
|
|
289
|
-
| `--browser <name>` | `chromium` (default)
|
|
285
|
+
| Flag | Description |
|
|
286
|
+
| ------------------ | --------------------------------- |
|
|
287
|
+
| `@tag` | Run only tests matching the tag |
|
|
288
|
+
| `--parallel <n>` | Run across `n` parallel workers |
|
|
289
|
+
| `--browser <name>` | `chromium` (default) or `firefox` |
|
|
290
290
|
|
|
291
291
|
> `plum run-test` syncs your tests, installs dependencies, and runs Cucumber. No Docker needed.
|
|
292
292
|
|
|
@@ -296,6 +296,8 @@ plum run-test --browser firefox # run in a specific browser
|
|
|
296
296
|
|
|
297
297
|
Runners are additional machines that execute tests in parallel alongside the primary server, letting you distribute a large suite across many nodes. A node runs as a plain Node process — **no Docker required**.
|
|
298
298
|
|
|
299
|
+
Each node automatically installs **Chromium and Firefox** — no browser selection needed. When triggering a test run from the UI, you choose which browser to use at run time.
|
|
300
|
+
|
|
299
301
|
### Start a runner node
|
|
300
302
|
|
|
301
303
|
On the machine that will act as a runner, navigate to your Plum project and run:
|
|
@@ -304,7 +306,7 @@ On the machine that will act as a runner, navigate to your Plum project and run:
|
|
|
304
306
|
plum node start
|
|
305
307
|
```
|
|
306
308
|
|
|
307
|
-
Plum asks a few questions
|
|
309
|
+
Plum asks a few questions, **registers the node with your server automatically**, starts it in the background, then opens the runner management menu:
|
|
308
310
|
|
|
309
311
|
| Prompt | Default | What it sets |
|
|
310
312
|
| ------------------ | ------------------------- | -------------------------------------------------------------------- |
|
|
@@ -312,10 +314,11 @@ Plum asks a few questions and then **registers the node with your server automat
|
|
|
312
314
|
| **Local port** | `3001` | The port this node process listens on |
|
|
313
315
|
| **Advertised URL** | `http://<your-ip>:<port>` | The address the **server** uses to reach this node |
|
|
314
316
|
| **Runner name** | `node-<random>` | The name shown in the UI |
|
|
315
|
-
| **Browser** | `chromium` | Default browser for this node |
|
|
316
317
|
| **Auth token** | auto-generated | Shared secret; press Enter to keep the generated one |
|
|
317
318
|
|
|
318
|
-
|
|
319
|
+
The node starts as a **background daemon** — your terminal is free immediately. Logs go to `backend/logs/runner-<id>.log`. Settings are saved to `.plum-node.json`, so re-running reuses them and never creates a duplicate.
|
|
320
|
+
|
|
321
|
+
After setup, the **runner management menu** opens automatically. From there you can start, stop, restart, and ping any runner registered on the primary. Exit the menu any time — the node keeps running.
|
|
319
322
|
|
|
320
323
|
**Skip the prompts** with flags (handy for CI or scripted nodes):
|
|
321
324
|
|
|
@@ -330,7 +333,6 @@ plum node start --primary http://192.168.1.5:3001 --port 3001 --name ci-node-1
|
|
|
330
333
|
| `--port <n>` | Local HTTP port the node listens on (default `3001`). |
|
|
331
334
|
| `--token <secret>` | Auth token. Auto-generated and saved if omitted. |
|
|
332
335
|
| `--name <name>` | Runner name shown in the UI. |
|
|
333
|
-
| `--browser <name>` | `chromium` (default), `firefox`, or `webkit`. |
|
|
334
336
|
|
|
335
337
|
#### Nodes behind a domain or reverse proxy
|
|
336
338
|
|
|
@@ -342,6 +344,20 @@ plum node start --primary https://plum.example.com --url https://node1.example.c
|
|
|
342
344
|
|
|
343
345
|
The server reaches the node at `https://node1.example.com`; the proxy forwards to the node on port `3001`. The advertised `--url` must be reachable from the server.
|
|
344
346
|
|
|
347
|
+
### Manage runners
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
plum manage-runners
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Opens the interactive runner management menu at any time (reads the primary URL from your saved node config). You can also pass a different server:
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
plum manage-runners --primary http://192.168.1.5:3001
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
> In development, the equivalent command is `npm run manage-runners` from the `backend/` directory.
|
|
360
|
+
|
|
345
361
|
### Change settings later
|
|
346
362
|
|
|
347
363
|
```bash
|
|
@@ -364,26 +380,27 @@ If you run `plum node start` without a reachable `--primary`, Plum prints the no
|
|
|
364
380
|
plum node stop
|
|
365
381
|
```
|
|
366
382
|
|
|
367
|
-
Stops the node started from the current folder.
|
|
383
|
+
Stops the node started from the current folder. You can also stop individual runners from the `plum manage-runners` menu.
|
|
368
384
|
|
|
369
385
|
---
|
|
370
386
|
|
|
371
387
|
## Command Reference
|
|
372
388
|
|
|
373
|
-
| Command | Description
|
|
374
|
-
| ----------------------------- |
|
|
375
|
-
| `plum init` | Initialize a new project in the current folder
|
|
376
|
-
| `plum server start` | Start the full UI stack via Docker, interactively (alias: `plum start`)
|
|
377
|
-
| `plum server reconfig` | Re-enter server settings (URL, ports) without starting
|
|
378
|
-
| `plum server stop` | Stop the server and preserve data (alias: `plum stop`)
|
|
379
|
-
| `plum run-test` | Run all tests locally without Docker
|
|
380
|
-
| `plum run-test @tag` | Run tests matching a tag
|
|
381
|
-
| `plum run-test --parallel N` | Run tests across N parallel workers
|
|
382
|
-
| `plum run-test --browser <b>` | Run in a specific browser (chromium
|
|
383
|
-
| `plum create-step` | Interactively scaffold a new step definition
|
|
384
|
-
| `plum node start` |
|
|
385
|
-
| `plum node reconfig` | Re-enter node settings + re-register, without starting
|
|
386
|
-
| `plum node stop` | Stop the runner node started from this folder
|
|
389
|
+
| Command | Description |
|
|
390
|
+
| ----------------------------- | ------------------------------------------------------------------------------ |
|
|
391
|
+
| `plum init` | Initialize a new project in the current folder |
|
|
392
|
+
| `plum server start` | Start the full UI stack via Docker, interactively (alias: `plum start`) |
|
|
393
|
+
| `plum server reconfig` | Re-enter server settings (URL, ports) without starting |
|
|
394
|
+
| `plum server stop` | Stop the server and preserve data (alias: `plum stop`) |
|
|
395
|
+
| `plum run-test` | Run all tests locally without Docker |
|
|
396
|
+
| `plum run-test @tag` | Run tests matching a tag |
|
|
397
|
+
| `plum run-test --parallel N` | Run tests across N parallel workers |
|
|
398
|
+
| `plum run-test --browser <b>` | Run in a specific browser (`chromium` or `firefox`) |
|
|
399
|
+
| `plum create-step` | Interactively scaffold a new step definition |
|
|
400
|
+
| `plum node start` | Configure, register, and start a runner node; opens the runner management menu |
|
|
401
|
+
| `plum node reconfig` | Re-enter node settings + re-register, without starting |
|
|
402
|
+
| `plum node stop` | Stop the runner node started from this folder |
|
|
403
|
+
| `plum manage-runners` | Open the interactive runner management menu |
|
|
387
404
|
|
|
388
405
|
---
|
|
389
406
|
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
/*
|
|
2
|
-
This file is part of Plum.
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
*
|
|
4
|
+
* Plum is free software: you can redistribute it and/or modify
|
|
5
|
+
* it under the terms of the GNU General Public License as published by
|
|
6
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* Plum is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU General Public License
|
|
15
|
+
* along with Plum. If not, see https://www.gnu.org/licenses/.
|
|
16
|
+
*/
|
|
3
17
|
|
|
4
|
-
Plum is free software: you can redistribute it and/or modify
|
|
5
|
-
it under the terms of the GNU General Public License as published by
|
|
6
|
-
the Free Software Foundation, either version 3 of the License, or
|
|
7
|
-
(at your option) any later version.
|
|
8
|
-
|
|
9
|
-
Plum is distributed in the hope that it will be useful,
|
|
10
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
-
GNU General Public License for more details.
|
|
13
|
-
|
|
14
|
-
You should have received a copy of the GNU General Public License
|
|
15
|
-
along with Plum. If not, see https://www.gnu.org/licenses/.
|
|
16
|
-
*/
|
|
17
18
|
import * as clack from '@clack/prompts';
|
|
18
19
|
import pc from 'picocolors';
|
|
19
20
|
import fs from 'fs';
|
|
@@ -109,13 +109,12 @@ function statusOf(id, registry = loadRegistry()) {
|
|
|
109
109
|
* an interactive prompt running in the same terminal (the caller's menu would
|
|
110
110
|
* wedge after the install finishes).
|
|
111
111
|
*/
|
|
112
|
-
function prepareEnv(
|
|
112
|
+
function prepareEnv() {
|
|
113
113
|
const stdio = ['ignore', 'inherit', 'inherit'];
|
|
114
114
|
if (!fs.existsSync(path.join(BACKEND_DIR, 'node_modules'))) {
|
|
115
115
|
execSync('npm install', { cwd: BACKEND_DIR, stdio, shell: true });
|
|
116
116
|
}
|
|
117
|
-
|
|
118
|
-
execSync(`npx playwright install${target}`, { cwd: BACKEND_DIR, stdio, shell: true });
|
|
117
|
+
execSync('npx playwright install chromium firefox', { cwd: BACKEND_DIR, stdio, shell: true });
|
|
119
118
|
}
|
|
120
119
|
|
|
121
120
|
/**
|
|
@@ -103,10 +103,10 @@ function statusBadge(r) {
|
|
|
103
103
|
* npm/playwright progress is visible. A failure is surfaced but non-fatal — the
|
|
104
104
|
* operator can retry or fix it manually.
|
|
105
105
|
*/
|
|
106
|
-
function prepareNodeEnv(
|
|
107
|
-
clack.log.step(
|
|
106
|
+
function prepareNodeEnv() {
|
|
107
|
+
clack.log.step('Preparing node environment (deps + browsers)...');
|
|
108
108
|
try {
|
|
109
|
-
prepareEnv(
|
|
109
|
+
prepareEnv();
|
|
110
110
|
clack.log.success(pc.green('Environment ready.'));
|
|
111
111
|
return true;
|
|
112
112
|
} catch (e) {
|
|
@@ -152,7 +152,7 @@ async function runAction(r) {
|
|
|
152
152
|
const port = parsePort(r.url);
|
|
153
153
|
|
|
154
154
|
if (action === 'start') {
|
|
155
|
-
prepareNodeEnv(
|
|
155
|
+
prepareNodeEnv();
|
|
156
156
|
const entry = startNode({ id: r.id, port, token: r.token });
|
|
157
157
|
clack.log.success(pc.green(`Started "${r.name}" on port ${port} (pid ${entry.pid})`));
|
|
158
158
|
} else if (action === 'stop') {
|
|
@@ -204,17 +204,6 @@ async function addRunner() {
|
|
|
204
204
|
});
|
|
205
205
|
if (cancelled(port)) return;
|
|
206
206
|
|
|
207
|
-
const browser = await clack.select({
|
|
208
|
-
message: 'Default browser',
|
|
209
|
-
options: [
|
|
210
|
-
{ value: 'chromium', label: 'Chrome' },
|
|
211
|
-
{ value: 'firefox', label: 'Firefox' },
|
|
212
|
-
{ value: 'webkit', label: 'WebKit' }
|
|
213
|
-
],
|
|
214
|
-
initialValue: 'chromium'
|
|
215
|
-
});
|
|
216
|
-
if (cancelled(browser)) return;
|
|
217
|
-
|
|
218
207
|
const defToken = process.env.NODE_TOKEN || generateToken();
|
|
219
208
|
const token = await clack.text({ message: 'Auth token', placeholder: defToken, defaultValue: defToken });
|
|
220
209
|
if (cancelled(token)) return;
|
|
@@ -227,7 +216,7 @@ async function addRunner() {
|
|
|
227
216
|
s.start(`Registering "${name}" with the primary...`);
|
|
228
217
|
let id;
|
|
229
218
|
try {
|
|
230
|
-
const res = await registerWithPrimary({ primary: API_URL, name, url, token, browser });
|
|
219
|
+
const res = await registerWithPrimary({ primary: API_URL, name, url, token, browser: 'chromium' });
|
|
231
220
|
id = res.id;
|
|
232
221
|
s.stop(res.reused ? pc.green(`Reusing existing runner "${name}"`) : pc.green(`Registered "${name}" (id ${id})`));
|
|
233
222
|
} catch (e) {
|
|
@@ -235,7 +224,7 @@ async function addRunner() {
|
|
|
235
224
|
return;
|
|
236
225
|
}
|
|
237
226
|
|
|
238
|
-
prepareNodeEnv(
|
|
227
|
+
prepareNodeEnv();
|
|
239
228
|
|
|
240
229
|
const startNow = await clack.confirm({ message: 'Start this runner now?' });
|
|
241
230
|
if (!cancelled(startNow) && startNow) {
|
|
@@ -88,9 +88,20 @@ function runSingleBuiltIn({ taskName, tags, workers, browser }) {
|
|
|
88
88
|
async function runDistributed({ taskName, tags, workers, browser, runnerIds }) {
|
|
89
89
|
const allIds = getTestIdsForTag(tags);
|
|
90
90
|
const chunks = chunkTests(allIds, runnerIds.length);
|
|
91
|
-
const laneInfos = await resolveLaneInfos(runnerIds);
|
|
92
91
|
|
|
93
|
-
|
|
92
|
+
// Surplus runners beyond the number of non-empty chunks would fall back to
|
|
93
|
+
// running the full tag expression, producing duplicate scenarios in the report.
|
|
94
|
+
const activeRunnerIds = runnerIds.slice(0, chunks.length);
|
|
95
|
+
|
|
96
|
+
if (activeRunnerIds.length === 0) {
|
|
97
|
+
console.log(`Task "${taskName}" — no tests found, skipping.`);
|
|
98
|
+
if (_io) _io.emit('cron-done', { taskName, code: 0 });
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const laneInfos = await resolveLaneInfos(activeRunnerIds);
|
|
103
|
+
|
|
104
|
+
const collectedReports = new Array(activeRunnerIds.length).fill(null);
|
|
94
105
|
let doneCount = 0;
|
|
95
106
|
let overallCode = 0;
|
|
96
107
|
|
|
@@ -99,7 +110,7 @@ async function runDistributed({ taskName, tags, workers, browser, runnerIds }) {
|
|
|
99
110
|
collectedReports[idx] = reportContent;
|
|
100
111
|
doneCount++;
|
|
101
112
|
|
|
102
|
-
if (doneCount ===
|
|
113
|
+
if (doneCount === activeRunnerIds.length) {
|
|
103
114
|
console.log(`Task "${taskName}" — all runners done (exit ${overallCode})`);
|
|
104
115
|
if (_io) _io.emit('cron-done', { taskName, code: overallCode });
|
|
105
116
|
|
|
@@ -116,9 +127,9 @@ async function runDistributed({ taskName, tags, workers, browser, runnerIds }) {
|
|
|
116
127
|
}
|
|
117
128
|
}
|
|
118
129
|
|
|
119
|
-
for (let i = 0; i <
|
|
130
|
+
for (let i = 0; i < activeRunnerIds.length; i++) {
|
|
120
131
|
const lane = laneInfos[i];
|
|
121
|
-
const chunkTag =
|
|
132
|
+
const chunkTag = buildTagExpression(chunks[i]);
|
|
122
133
|
|
|
123
134
|
if (lane.id === BUILT_IN_RUNNER_ID) {
|
|
124
135
|
const env = {
|
|
@@ -118,8 +118,26 @@ async function runDistributed(io, socket, activeProcs, tag, workers, browser, ru
|
|
|
118
118
|
const allIds = getTestIdsForTag(tag);
|
|
119
119
|
const chunks = chunkTests(allIds, runnerIds.length);
|
|
120
120
|
|
|
121
|
+
// Surplus runners beyond the number of non-empty chunks would fall back to
|
|
122
|
+
// running the full tag expression, producing duplicate scenarios in the report.
|
|
123
|
+
const activeRunnerIds = runnerIds.slice(0, chunks.length);
|
|
124
|
+
|
|
125
|
+
if (activeRunnerIds.length === 0) {
|
|
126
|
+
socket.emit('log', '\nNo tests found matching the selected tag.\n');
|
|
127
|
+
socket.emit('done', 0);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (activeRunnerIds.length < runnerIds.length) {
|
|
132
|
+
const skipped = runnerIds.length - activeRunnerIds.length;
|
|
133
|
+
socket.emit(
|
|
134
|
+
'log',
|
|
135
|
+
`[INFO] ${skipped} runner(s) not used — fewer tests than runners selected.\n`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
121
139
|
const laneInfos = await Promise.all(
|
|
122
|
-
|
|
140
|
+
activeRunnerIds.map(async (id) => {
|
|
123
141
|
if (id === BUILT_IN_RUNNER_ID) return { id, name: 'Built-in', dbId: null };
|
|
124
142
|
const r = await runnerService.getById(id);
|
|
125
143
|
return { id, name: r?.name ?? id, dbId: r?.id ?? null };
|
|
@@ -128,10 +146,10 @@ async function runDistributed(io, socket, activeProcs, tag, workers, browser, ru
|
|
|
128
146
|
|
|
129
147
|
socket.emit(
|
|
130
148
|
'runner-lanes-init',
|
|
131
|
-
laneInfos.map((l, i) => ({ id: l.id, name: l.name, testCount:
|
|
149
|
+
laneInfos.map((l, i) => ({ id: l.id, name: l.name, testCount: chunks[i].length }))
|
|
132
150
|
);
|
|
133
151
|
|
|
134
|
-
const total =
|
|
152
|
+
const total = activeRunnerIds.length;
|
|
135
153
|
const collectedReports = new Array(total).fill(null);
|
|
136
154
|
let doneCount = 0;
|
|
137
155
|
let overallCode = 0;
|
|
@@ -168,9 +186,9 @@ async function runDistributed(io, socket, activeProcs, tag, workers, browser, ru
|
|
|
168
186
|
}
|
|
169
187
|
}
|
|
170
188
|
|
|
171
|
-
for (let i = 0; i <
|
|
189
|
+
for (let i = 0; i < activeRunnerIds.length; i++) {
|
|
172
190
|
const lane = laneInfos[i];
|
|
173
|
-
const chunkTag =
|
|
191
|
+
const chunkTag = buildTagExpression(chunks[i]);
|
|
174
192
|
|
|
175
193
|
if (lane.id === BUILT_IN_RUNNER_ID) {
|
|
176
194
|
const env = {
|