plum-e2e 2.6.2 → 2.6.4

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
@@ -69,6 +69,7 @@ Full documentation is available at:
69
69
  | [Initializing the Project](https://outline.silverlunah.com/s/12bf21d1-02ba-49e9-b0df-908976407afd/doc/initializing-the-project-ilfc8LUyO7) | What `plum init` generates, config files explained |
70
70
  | [Writing Tests](https://outline.silverlunah.com/s/12bf21d1-02ba-49e9-b0df-908976407afd/doc/writing-tests-XeHJQdtH49) | Feature files, page objects, step definitions, best practices |
71
71
  | [Running Tests Locally](https://outline.silverlunah.com/s/12bf21d1-02ba-49e9-b0df-908976407afd/doc/running-tests-locally-GGhFcqaAQ8) | `plum run-test` flags, parallel runs, debugging tips |
72
+ | [Retrying Flaky Tests](https://outline.silverlunah.com/s/12bf21d1-02ba-49e9-b0df-908976407afd/doc/retrying-flaky-tests-NXwRF5SXru) | Auto-retry failed scenarios, global setting, report badges |
72
73
  | [Setting Up the Server](https://outline.silverlunah.com/s/12bf21d1-02ba-49e9-b0df-908976407afd/doc/setting-up-the-server-vj0Ab1kJVs) | Production server setup, reverse proxy (Nginx/Caddy), Docker |
73
74
  | [Setting Up Nodes](https://outline.silverlunah.com/s/12bf21d1-02ba-49e9-b0df-908976407afd/doc/setting-up-nodes-dtmekJGJia) | Runner nodes, systemd service, managing nodes |
74
75
  | [Integrations](https://outline.silverlunah.com/s/12bf21d1-02ba-49e9-b0df-908976407afd/doc/integrations-qfiqfmdP0j) | Discord & Slack webhook notifications, CI/external triggers |
@@ -94,6 +95,7 @@ Full documentation is available at:
94
95
  | `plum run-test @tag` | Run tests matching a tag |
95
96
  | `plum run-test --parallel N` | Run tests across N parallel workers |
96
97
  | `plum run-test --browser <b>` | Run in `chromium` (default) or `firefox` |
98
+ | `plum run-test --help` | Show usage for `run-test` |
97
99
  | `plum create-step` | Interactively scaffold a new step definition |
98
100
  | `plum manage-runners` | Open the interactive runner management menu |
99
101
 
@@ -27,6 +27,7 @@
27
27
  */
28
28
 
29
29
  const fs = require('fs');
30
+ const os = require('os');
30
31
  const path = require('path');
31
32
  const { spawn, execSync } = require('child_process');
32
33
 
@@ -37,6 +38,23 @@ const LOGS_DIR = path.join(BACKEND_DIR, 'logs');
37
38
 
38
39
  const LOCAL_HOSTS = new Set(['localhost', '127.0.0.1', '::1', 'host.docker.internal']);
39
40
 
41
+ /**
42
+ * Runners default to this machine's own LAN IP (see nodeRegister's
43
+ * detectLanIp), not a loopback hostname — so isLocalUrl must also recognise
44
+ * this machine's actual interface addresses, or a runner started and later
45
+ * stopped on this same box gets misclassified as remote and loses its
46
+ * "Start" option (nothing else can restart a fully-stopped process for it).
47
+ */
48
+ function localAddresses() {
49
+ const addrs = new Set(LOCAL_HOSTS);
50
+ for (const ifaces of Object.values(os.networkInterfaces())) {
51
+ for (const iface of ifaces ?? []) {
52
+ if (iface.family === 'IPv4' || iface.family === 4) addrs.add(iface.address);
53
+ }
54
+ }
55
+ return addrs;
56
+ }
57
+
40
58
  function loadRegistry() {
41
59
  try {
42
60
  return JSON.parse(fs.readFileSync(REGISTRY_PATH, 'utf8'));
@@ -90,7 +108,7 @@ function isAlive(pid) {
90
108
 
91
109
  function isLocalUrl(url) {
92
110
  try {
93
- return LOCAL_HOSTS.has(new URL(url).hostname);
111
+ return localAddresses().has(new URL(url).hostname);
94
112
  } catch {
95
113
  return false;
96
114
  }
@@ -191,6 +191,7 @@ async function runAction(r) {
191
191
  }
192
192
 
193
193
  options.push(
194
+ { value: 'token', label: 'Show token' },
194
195
  { value: 'delete', label: pc.red('Delete') },
195
196
  { value: 'back', label: pc.dim('← Back') }
196
197
  );
@@ -241,6 +242,8 @@ async function runAction(r) {
241
242
  } else if (action === 'log') {
242
243
  const entry = runnerProcess.loadRegistry()[r.id];
243
244
  clack.note(entry?.logFile ?? '(no log file)', 'Log file');
245
+ } else if (action === 'token') {
246
+ clack.note(r.token, 'Auth token');
244
247
  } else if (action === 'ping') {
245
248
  const s = clack.spinner();
246
249
  s.start(`Pinging "${r.name}"...`);
package/bin/plum.js CHANGED
@@ -1096,6 +1096,23 @@ switch (command) {
1096
1096
  break;
1097
1097
 
1098
1098
  case 'run-test': {
1099
+ const runHelpArgs = process.argv.slice(3);
1100
+ if (anyFlags(runHelpArgs, ['--help', '-h'])) {
1101
+ console.log(
1102
+ [
1103
+ '',
1104
+ `${pc.bold('Usage:')} plum run-test [tag] [options]`,
1105
+ '',
1106
+ ` ${pc.cyan('plum run-test')} run all tests`,
1107
+ ` ${pc.cyan('plum run-test @tag')} run tests matching a tag`,
1108
+ ` ${pc.cyan('plum run-test --parallel N')} run tests across N parallel workers`,
1109
+ ` ${pc.cyan('plum run-test --browser firefox')} run in a specific browser (chromium/firefox)`,
1110
+ ''
1111
+ ].join('\n')
1112
+ );
1113
+ break;
1114
+ }
1115
+
1099
1116
  console.log('--------------------------------------\n');
1100
1117
  console.log('🚀 Running tests locally...');
1101
1118
 
@@ -30,6 +30,7 @@
30
30
  import { runsVersion } from '$lib/stores/runner';
31
31
  import { auth } from '$lib/stores/auth';
32
32
  import Button from '$lib/components/ui/Button.svelte';
33
+ import Modal from '$lib/components/ui/Modal.svelte';
33
34
  import Toast from '$lib/components/ui/Toast.svelte';
34
35
  import EmptyState from '$lib/components/ui/EmptyState.svelte';
35
36
  import AutomatedBadge from '$lib/components/ui/AutomatedBadge.svelte';
@@ -106,6 +107,10 @@
106
107
  let entryNote = '';
107
108
  let expandedExecEntries = new Set();
108
109
 
110
+ let editRunOpen = false;
111
+ let editRunForm = {};
112
+ let editRunSaving = false;
113
+
109
114
  function toggleExecSteps(entryId) {
110
115
  if (expandedExecEntries.has(entryId)) expandedExecEntries.delete(entryId);
111
116
  else expandedExecEntries.add(entryId);
@@ -203,6 +208,20 @@
203
208
  run = { ...run, entries: run.entries.filter((e) => e.id !== entryId) };
204
209
  }
205
210
 
211
+ async function handleUpdateRun() {
212
+ editRunSaving = true;
213
+ try {
214
+ const updated = await updateRun(runId, editRunForm);
215
+ run = { ...run, ...updated };
216
+ editRunOpen = false;
217
+ showToast('success', 'Run updated.');
218
+ } catch (e) {
219
+ showToast('error', e.message);
220
+ } finally {
221
+ editRunSaving = false;
222
+ }
223
+ }
224
+
206
225
  async function handleSaveRun() {
207
226
  saving = true;
208
227
  try {
@@ -309,6 +328,21 @@
309
328
 
310
329
  <Toast {toast} />
311
330
 
331
+ <Modal bind:open={editRunOpen} title="Edit Run">
332
+ <div class="form-fields">
333
+ <div class="field">
334
+ <label class="field-label" for="er-title">Title</label>
335
+ <input id="er-title" type="text" class="field-input" bind:value={editRunForm.title} />
336
+ </div>
337
+ <div class="modal-actions">
338
+ <Button on:click={handleUpdateRun} disabled={editRunSaving}>
339
+ {editRunSaving ? 'Saving…' : 'Save'}
340
+ </Button>
341
+ <Button variant="ghost" on:click={() => (editRunOpen = false)}>Cancel</Button>
342
+ </div>
343
+ </div>
344
+ </Modal>
345
+
312
346
  <div class="breadcrumb">
313
347
  <a href="/test-repository" class="bc-link">Test Repository</a>
314
348
  <span class="bc-sep">›</span>
@@ -322,6 +356,28 @@
322
356
  <div class="run-header-left">
323
357
  <h1 class="run-title">{run.title}</h1>
324
358
  <span class="run-status-badge {run.status}">{run.status}</span>
359
+ <button
360
+ class="icon-btn"
361
+ title="Edit run"
362
+ on:click={() => {
363
+ editRunForm = { title: run.title };
364
+ editRunOpen = true;
365
+ }}
366
+ >
367
+ <svg
368
+ width="14"
369
+ height="14"
370
+ viewBox="0 0 24 24"
371
+ fill="none"
372
+ stroke="currentColor"
373
+ stroke-width="2"
374
+ stroke-linecap="round"
375
+ stroke-linejoin="round"
376
+ ><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" /><path
377
+ d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"
378
+ /></svg
379
+ >
380
+ </button>
325
381
  </div>
326
382
  <div class="run-header-actions">
327
383
  {#if isLocked}
@@ -1427,4 +1483,15 @@
1427
1483
  display: none;
1428
1484
  }
1429
1485
  }
1486
+
1487
+ .form-fields {
1488
+ display: flex;
1489
+ flex-direction: column;
1490
+ gap: 0.875rem;
1491
+ }
1492
+ .modal-actions {
1493
+ display: flex;
1494
+ gap: 0.5rem;
1495
+ padding-top: 0.25rem;
1496
+ }
1430
1497
  </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plum-e2e",
3
- "version": "2.6.2",
3
+ "version": "2.6.4",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/silverlunah/plum.git"