plum-e2e 2.4.8 → 2.4.10

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 CHANGED
@@ -81,17 +81,20 @@ Full documentation is available at:
81
81
  | Command | Description |
82
82
  | ----------------------------- | ------------------------------------------------------------------ |
83
83
  | `plum init` | Initialize a new project in the current folder |
84
- | `plum server start` | Start the full UI stack via Docker (alias: `plum start`) |
85
- | `plum server stop` | Stop the server and preserve data (alias: `plum stop`) |
84
+ | `plum server start` | Start the full UI stack via Docker |
85
+ | `plum server restart` | Rebuild Docker images and restart the server without prompts |
86
+ | `plum server stop` | Stop the server (data preserved) |
86
87
  | `plum server reconfig` | Re-enter server settings without starting |
88
+ | `plum update` | Update Plum and auto-restart whatever is running (server or node) |
89
+ | `plum node start` | Set up connectivity, start a runner node, and open the runner menu |
90
+ | `plum node restart` | Stop, refresh dependencies, and restart the runner node |
91
+ | `plum node stop` | Stop the runner node started from this folder |
92
+ | `plum node reconfig` | Re-enter node settings and re-register |
87
93
  | `plum run-test` | Run all tests locally without Docker |
88
94
  | `plum run-test @tag` | Run tests matching a tag |
89
95
  | `plum run-test --parallel N` | Run tests across N parallel workers |
90
96
  | `plum run-test --browser <b>` | Run in `chromium` (default) or `firefox` |
91
97
  | `plum create-step` | Interactively scaffold a new step definition |
92
- | `plum node start` | Set up connectivity, start a runner node, and open the runner menu |
93
- | `plum node stop` | Stop the runner node started from this folder |
94
- | `plum node reconfig` | Re-enter node settings and re-register |
95
98
  | `plum manage-runners` | Open the interactive runner management menu |
96
99
 
97
100
  ---
@@ -139,9 +139,26 @@ function statusOf(id, registry = loadRegistry()) {
139
139
  */
140
140
  function prepareEnv() {
141
141
  const stdio = ['ignore', 'inherit', 'inherit'];
142
- if (!fs.existsSync(path.join(BACKEND_DIR, 'node_modules'))) {
142
+
143
+ // Re-run npm install when node_modules is missing OR when the plum package
144
+ // version has changed (npm install -g wipes node_modules on upgrade).
145
+ const markerPath = path.join(BACKEND_DIR, 'node_modules', '.plum-version');
146
+ let installedVersion = null;
147
+ try {
148
+ installedVersion = fs.readFileSync(markerPath, 'utf8').trim();
149
+ } catch {}
150
+ const currentVersion = JSON.parse(
151
+ fs.readFileSync(path.join(BACKEND_DIR, '..', 'package.json'), 'utf8')
152
+ ).version;
153
+
154
+ if (
155
+ !fs.existsSync(path.join(BACKEND_DIR, 'node_modules')) ||
156
+ installedVersion !== currentVersion
157
+ ) {
143
158
  execSync('npm install', { cwd: BACKEND_DIR, stdio, shell: true });
159
+ fs.writeFileSync(markerPath, currentVersion, 'utf8');
144
160
  }
161
+
145
162
  execSync('npx playwright install chromium firefox', { cwd: BACKEND_DIR, stdio, shell: true });
146
163
  }
147
164
 
package/bin/plum.js CHANGED
@@ -324,7 +324,7 @@ async function serverStart() {
324
324
  }
325
325
 
326
326
  async function serverRestart() {
327
- clack.intro(pc.bgMagenta(pc.white(' 🟣 Plum — Restart ')));
327
+ clack.intro(pc.bgMagenta(pc.white(' 🟣 Plum — Server Restart ')));
328
328
  const { loadServerConfig } = serverConfigLib();
329
329
  const cfg = loadServerConfig(process.cwd());
330
330
  applyServerConfig(cfg);
@@ -349,7 +349,7 @@ async function serverRestart() {
349
349
  s.stop(ready ? pc.green('✓ Server is ready') : pc.yellow('Server may still be starting'));
350
350
  clack.log.info(`UI: ${pc.cyan(`http://localhost:${cfg.frontendPort}`)}`);
351
351
  clack.log.info(`API: ${pc.cyan(`http://localhost:${cfg.backendPort}`)}`);
352
- clack.outro(pc.green('Plum restarted.'));
352
+ clack.outro(pc.green('Server restarted.'));
353
353
  }
354
354
 
355
355
  async function serverUpdate() {
@@ -357,8 +357,33 @@ async function serverUpdate() {
357
357
  clack.log.step('Fetching latest Plum version…');
358
358
  execSync('npm install -g plum-e2e@latest', { stdio: 'inherit' });
359
359
  clack.log.success('Plum CLI updated.');
360
- clack.log.step('Rebuilding server with new version…');
361
- await serverRestart();
360
+
361
+ const serverCfgPath = path.join(process.cwd(), '.plum-server.json');
362
+ const hasServerCfg = fs.existsSync(serverCfgPath);
363
+
364
+ const { loadNodeConfig } = nodeRegisterLib();
365
+ const { loadRegistry, isAlive } = runnerProcessLib();
366
+ const nodeCfg = loadNodeConfig(process.cwd());
367
+ const registry = loadRegistry();
368
+ const nodeRunning = !!(
369
+ nodeCfg.id &&
370
+ registry[String(nodeCfg.id)]?.pid &&
371
+ isAlive(registry[String(nodeCfg.id)].pid)
372
+ );
373
+
374
+ if (hasServerCfg) {
375
+ clack.log.step('Rebuilding server with new version…');
376
+ await serverRestart();
377
+ }
378
+
379
+ if (nodeRunning) {
380
+ clack.log.step('Restarting node runner with new version…');
381
+ await nodeRestart();
382
+ }
383
+
384
+ if (!hasServerCfg && !nodeRunning) {
385
+ clack.outro(pc.green('Plum updated. Run `plum server start` or `plum node start` to launch.'));
386
+ }
362
387
  }
363
388
 
364
389
  async function serverReconfig() {
@@ -528,6 +553,46 @@ async function nodeStart({ reconfig }) {
528
553
  clack.outro(`Manage runners anytime: ${pc.cyan('plum manage-runners')}`);
529
554
  }
530
555
 
556
+ async function nodeRestart() {
557
+ clack.intro(pc.bgMagenta(pc.white(' 🟣 Plum — Node Restart ')));
558
+ const { loadNodeConfig } = nodeRegisterLib();
559
+ const { prepareEnv, stopNode, startNode } = runnerProcessLib();
560
+ const cfg = loadNodeConfig(process.cwd());
561
+
562
+ if (!cfg.id) {
563
+ clack.log.warn('No node configured in this folder — run `plum node start` first.');
564
+ clack.outro(pc.dim('Done.'));
565
+ return;
566
+ }
567
+
568
+ const stopped = stopNode(String(cfg.id));
569
+ if (stopped) {
570
+ clack.log.success(`Stopped runner "${cfg.name ?? cfg.id}".`);
571
+ } else {
572
+ clack.log.info('Node was not running — starting fresh.');
573
+ }
574
+
575
+ clack.log.step('Refreshing dependencies…');
576
+ try {
577
+ prepareEnv();
578
+ } catch (e) {
579
+ clack.log.warn(`Dependency refresh failed: ${e.message}`);
580
+ }
581
+
582
+ try {
583
+ const entry = startNode({ id: String(cfg.id), port: cfg.port, token: cfg.token });
584
+ clack.log.success(
585
+ pc.green(
586
+ `Node "${cfg.name}" restarted (pid ${entry.pid}) — logs at backend/logs/runner-${cfg.id}.log`
587
+ )
588
+ );
589
+ } catch (e) {
590
+ clack.log.warn(`Could not restart node: ${e.message}`);
591
+ }
592
+
593
+ clack.outro(pc.green('Node restarted.'));
594
+ }
595
+
531
596
  async function nodeReconfig() {
532
597
  clack.intro(pc.bgMagenta(pc.white(' 🟣 Plum — Reconfigure Node ')));
533
598
  const cfg = await configureNode({ force: true });
@@ -809,11 +874,17 @@ switch (command) {
809
874
  // intentional fall-through
810
875
 
811
876
  case 'start':
812
- await serverStart();
877
+ console.log(
878
+ `\nSpecify what to start:\n ${pc.cyan('plum server start')} — start the web UI stack (Docker)\n ${pc.cyan('plum node start')} — start a runner node\n`
879
+ );
880
+ process.exit(1);
813
881
  break;
814
882
 
815
883
  case 'restart':
816
- await serverRestart();
884
+ console.log(
885
+ `\nSpecify what to restart:\n ${pc.cyan('plum server restart')} — rebuild and restart the server\n ${pc.cyan('plum node restart')} — restart the runner node\n`
886
+ );
887
+ process.exit(1);
817
888
  break;
818
889
 
819
890
  case 'update':
@@ -894,14 +965,10 @@ switch (command) {
894
965
  }
895
966
 
896
967
  case 'stop':
897
- console.log('--------------------------------------\n');
898
- console.log('🛑 Stopping Plum...');
899
- execSync('docker compose down', {
900
- cwd: plumRoot,
901
- stdio: 'inherit'
902
- });
903
- console.log('✅ Plum stopped. Your data is preserved in the database volume.\n');
904
- console.log('--------------------------------------\n');
968
+ console.log(
969
+ `\nSpecify what to stop:\n ${pc.cyan('plum server stop')} — stop the web UI stack\n ${pc.cyan('plum node stop')} — stop the runner node\n`
970
+ );
971
+ process.exit(1);
905
972
  break;
906
973
 
907
974
  case 'node': {
@@ -942,6 +1009,11 @@ switch (command) {
942
1009
  break;
943
1010
  }
944
1011
 
1012
+ if (subcommand === 'restart') {
1013
+ await nodeRestart();
1014
+ break;
1015
+ }
1016
+
945
1017
  if (subcommand === 'reconfig') {
946
1018
  await nodeReconfig();
947
1019
  break;
@@ -1006,16 +1078,16 @@ switch (command) {
1006
1078
  console.log('--------------------------------------\n');
1007
1079
  console.log('Usage: plum <command>\n');
1008
1080
  console.log(' init Set up a new Plum project');
1009
- console.log(' server start Start the full UI stack (interactive; alias: plum start)');
1081
+ console.log(' server start Start the full UI stack (interactive)');
1010
1082
  console.log(' --headless <bool> Run browsers headless (true/false)');
1011
1083
  console.log(' --backend-port <n> Host port for the backend/API (default: 3001)');
1012
1084
  console.log(' --frontend-port <n> Host port for the UI (default: 5173)');
1085
+ console.log(' server restart Rebuild Docker images and restart the server (no prompts)');
1086
+ console.log(' server stop Stop the server (data preserved)');
1013
1087
  console.log(' server reconfig Re-enter server settings without starting');
1014
1088
  console.log(
1015
- ' server restart Rebuild and restart the server (no prompts; alias: plum restart)'
1089
+ ' update Update Plum and restart whichever is running (server/node)'
1016
1090
  );
1017
- console.log(' server stop Stop the server (alias: plum stop)');
1018
- console.log(' update Update Plum to the latest version and restart the server');
1019
1091
  console.log(' node start Start a runner node (interactive), then open runner menu');
1020
1092
  console.log(' --primary <url> Primary Plum server to auto-register with');
1021
1093
  console.log(' --url <url> Address the primary calls back (default: <lan-ip>:<port>;');
@@ -1026,6 +1098,7 @@ switch (command) {
1026
1098
  console.log(' --token <secret> Auth token (auto-generated + saved if omitted)');
1027
1099
  console.log(' --name <name> Runner name shown on the primary (default: node-<rand>)');
1028
1100
  console.log(' --browser <name> chromium | firefox (default: chromium)');
1101
+ console.log(' node restart Stop, refresh deps, and restart the node runner');
1029
1102
  console.log(' node reconfig Re-enter node settings + re-register, without starting');
1030
1103
  console.log(' node stop Stop the runner node started from this folder');
1031
1104
  console.log(' manage-runners Open the runner management menu');
@@ -181,27 +181,14 @@
181
181
  </div>
182
182
 
183
183
  <div>
184
- {#if isScheduled(detail.triggerType)}
185
- <p class="report-task-name">
186
- <svg
187
- width="11"
188
- height="11"
189
- viewBox="0 0 24 24"
190
- fill="none"
191
- stroke="currentColor"
192
- stroke-width="2"
193
- stroke-linecap="round"
194
- stroke-linejoin="round"
195
- >
196
- <rect x="3" y="4" width="18" height="18" rx="2" />
197
- <line x1="16" y1="2" x2="16" y2="6" />
198
- <line x1="8" y1="2" x2="8" y2="6" />
199
- <line x1="3" y1="10" x2="21" y2="10" />
200
- </svg>
201
- {detail.triggerType}
202
- </p>
203
- {/if}
204
- <h1>{overallPass ? 'Passed' : 'Failed'}</h1>
184
+ <div class="h1-row">
185
+ <h1>{overallPass ? 'Passed' : 'Failed'}</h1>
186
+ {#if detail.testRun?.title}
187
+ <span class="run-name-badge">{detail.testRun.title}</span>
188
+ {:else if isScheduled(detail.triggerType)}
189
+ <span class="run-name-badge">{detail.triggerType}</span>
190
+ {/if}
191
+ </div>
205
192
  <div class="header-meta">
206
193
  <span class="mono">{detail.tags}</span>
207
194
  <span class="meta-sep">·</span>
@@ -524,21 +511,32 @@
524
511
  color: var(--fail);
525
512
  }
526
513
 
527
- .report-task-name {
514
+ .h1-row {
515
+ display: flex;
516
+ align-items: center;
517
+ gap: 0.75rem;
518
+ margin-bottom: 0.2rem;
519
+ }
520
+
521
+ .run-name-badge {
528
522
  display: inline-flex;
529
523
  align-items: center;
530
- gap: 0.35rem;
531
- font-size: 0.7rem;
532
- font-weight: 600;
533
- text-transform: uppercase;
534
- letter-spacing: 0.1em;
535
- color: var(--warn);
536
- margin-bottom: 0.35rem;
524
+ font-size: 0.75rem;
525
+ font-weight: 500;
526
+ color: var(--accent);
527
+ background: var(--accent-soft);
528
+ border: 1px solid color-mix(in srgb, var(--accent) 20%, transparent);
529
+ border-radius: 100px;
530
+ padding: 0.25rem 0.7rem;
531
+ white-space: nowrap;
532
+ max-width: 240px;
533
+ overflow: hidden;
534
+ text-overflow: ellipsis;
537
535
  }
538
536
 
539
537
  h1 {
540
538
  font-size: 2rem;
541
- margin-bottom: 0.2rem;
539
+ margin-bottom: 0;
542
540
  }
543
541
 
544
542
  .header-meta {
@@ -166,6 +166,10 @@
166
166
 
167
167
  {#if state.currentRun}
168
168
  <div class="run-info">
169
+ {#if state.currentRun.runTitle}
170
+ <span class="run-title-label">{state.currentRun.runTitle}</span>
171
+ <span class="run-sep">·</span>
172
+ {/if}
169
173
  <span class="run-tag-label">{state.currentRun.tag || 'all tests'}</span>
170
174
  <span class="run-sep">·</span>
171
175
  <span class="run-detail"
@@ -463,6 +467,17 @@
463
467
  flex-wrap: wrap;
464
468
  }
465
469
 
470
+ .run-title-label {
471
+ font-size: 0.875rem;
472
+ font-weight: 500;
473
+ color: var(--accent);
474
+ background: var(--accent-soft);
475
+ border: 1px solid color-mix(in srgb, var(--accent) 20%, transparent);
476
+ border-radius: 100px;
477
+ padding: 0.15rem 0.6rem;
478
+ white-space: nowrap;
479
+ }
480
+
466
481
  .run-tag-label {
467
482
  font-family: 'JetBrains Mono', monospace;
468
483
  font-size: 0.8125rem;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plum-e2e",
3
- "version": "2.4.8",
3
+ "version": "2.4.10",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/silverlunah/plum.git"