plum-e2e 2.4.1 → 2.4.2
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/backend/app.js
CHANGED
|
@@ -47,4 +47,11 @@ if (process.env.PLUM_MODE !== 'node') {
|
|
|
47
47
|
app.use('/trigger', require('./routes/trigger.routes'));
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
// Global JSON error handler — Express's default sends HTML, which breaks JSON clients
|
|
51
|
+
// eslint-disable-next-line no-unused-vars
|
|
52
|
+
app.use((err, req, res, next) => {
|
|
53
|
+
console.error(err);
|
|
54
|
+
res.status(err.status || 500).json({ error: err.message || 'Internal server error' });
|
|
55
|
+
});
|
|
56
|
+
|
|
50
57
|
module.exports = app;
|
|
@@ -57,10 +57,10 @@ function resolveNodeUrl(url) {
|
|
|
57
57
|
const u = new URL(url);
|
|
58
58
|
if (u.hostname === 'localhost' || u.hostname === '127.0.0.1') {
|
|
59
59
|
u.hostname = 'host.docker.internal';
|
|
60
|
-
return u.toString();
|
|
61
60
|
}
|
|
61
|
+
return u.toString().replace(/\/+$/, '');
|
|
62
62
|
} catch {}
|
|
63
|
-
return url;
|
|
63
|
+
return url.replace(/\/+$/, '');
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
async function fetchRunners() {
|
|
@@ -297,10 +297,12 @@ const saveReport = async ({
|
|
|
297
297
|
browser,
|
|
298
298
|
runnerName,
|
|
299
299
|
runnerId,
|
|
300
|
-
testRunId
|
|
300
|
+
testRunId,
|
|
301
|
+
forceFail = false
|
|
301
302
|
}) => {
|
|
302
303
|
const normTrigger = normaliseTrigger(triggerType);
|
|
303
|
-
const { features, status } = processCucumberJson(rawCucumberJson);
|
|
304
|
+
const { features, status: derivedStatus } = processCucumberJson(rawCucumberJson);
|
|
305
|
+
const status = forceFail ? 'FAIL' : derivedStatus;
|
|
304
306
|
const cronJobId = await resolveCronJobId(normTrigger);
|
|
305
307
|
|
|
306
308
|
const report = await prisma.report.create({
|
|
@@ -374,7 +376,8 @@ const saveCombinedReport = async ({
|
|
|
374
376
|
browser,
|
|
375
377
|
runnerName: runners.map((r) => r.name).join(', '),
|
|
376
378
|
runnerId: null,
|
|
377
|
-
testRunId: testRunId ?? null
|
|
379
|
+
testRunId: testRunId ?? null,
|
|
380
|
+
forceFail: reports.some((r) => r === null)
|
|
378
381
|
});
|
|
379
382
|
};
|
|
380
383
|
|
|
@@ -25,8 +25,10 @@ const prisma = require('./prisma');
|
|
|
25
25
|
|
|
26
26
|
const getAll = () => prisma.runner.findMany({ orderBy: { createdAt: 'asc' } });
|
|
27
27
|
|
|
28
|
+
const normaliseUrl = (url) => (url ?? '').replace(/\/+$/, '');
|
|
29
|
+
|
|
28
30
|
const create = ({ name, url, token, browser = 'chromium' }) =>
|
|
29
|
-
prisma.runner.create({ data: { name, url, token, browser } });
|
|
31
|
+
prisma.runner.create({ data: { name, url: normaliseUrl(url), token, browser } });
|
|
30
32
|
|
|
31
33
|
async function remove(id) {
|
|
32
34
|
// Scrub the deleted runner from any cron job's runnerIds string before
|
|
@@ -45,7 +47,11 @@ async function remove(id) {
|
|
|
45
47
|
return prisma.runner.delete({ where: { id } });
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
const update = (id, data) =>
|
|
50
|
+
const update = (id, data) =>
|
|
51
|
+
prisma.runner.update({
|
|
52
|
+
where: { id },
|
|
53
|
+
data: { ...data, ...(data.url && { url: normaliseUrl(data.url) }) }
|
|
54
|
+
});
|
|
49
55
|
|
|
50
56
|
const getById = (id) => prisma.runner.findUnique({ where: { id } });
|
|
51
57
|
|
|
@@ -61,7 +67,12 @@ async function probe({ url, token }) {
|
|
|
61
67
|
headers: { Authorization: `Bearer ${token}` },
|
|
62
68
|
signal: AbortSignal.timeout(5000)
|
|
63
69
|
});
|
|
64
|
-
return { ok:
|
|
70
|
+
if (!res.ok) return { ok: false, error: `HTTP ${res.status}` };
|
|
71
|
+
const body = await res.json();
|
|
72
|
+
if (!body.ok || body.mode !== 'node') {
|
|
73
|
+
return { ok: false, error: 'URL does not point to a Plum runner node' };
|
|
74
|
+
}
|
|
75
|
+
return { ok: true, latency: Date.now() - start };
|
|
65
76
|
} catch (e) {
|
|
66
77
|
return { ok: false, error: e.message };
|
|
67
78
|
}
|
|
@@ -714,14 +714,30 @@
|
|
|
714
714
|
{/if}
|
|
715
715
|
|
|
716
716
|
{#each cronJobs as name}
|
|
717
|
-
<
|
|
717
|
+
<a
|
|
718
|
+
href="/scheduled-tests"
|
|
719
|
+
class="run-card cron-run"
|
|
720
|
+
transition:fly={{ x: -4, duration: 160 }}
|
|
721
|
+
>
|
|
718
722
|
<span class="run-card-dot pulse-pass"></span>
|
|
719
723
|
<div class="run-card-info">
|
|
720
724
|
<span class="run-card-label">{name}</span>
|
|
721
725
|
<span class="run-card-meta">Scheduled run</span>
|
|
722
726
|
</div>
|
|
723
727
|
<span class="run-card-badge cron-badge">Scheduled</span>
|
|
724
|
-
|
|
728
|
+
<svg
|
|
729
|
+
width="13"
|
|
730
|
+
height="13"
|
|
731
|
+
viewBox="0 0 24 24"
|
|
732
|
+
fill="none"
|
|
733
|
+
stroke="currentColor"
|
|
734
|
+
stroke-width="2"
|
|
735
|
+
stroke-linecap="round"
|
|
736
|
+
class="run-card-arrow"
|
|
737
|
+
>
|
|
738
|
+
<line x1="5" y1="12" x2="19" y2="12" /><polyline points="12 5 19 12 12 19" />
|
|
739
|
+
</svg>
|
|
740
|
+
</a>
|
|
725
741
|
{/each}
|
|
726
742
|
|
|
727
743
|
{#if !anyRunning}
|