plum-e2e 2.8.6 → 2.9.1
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 +20 -19
- package/backend/_scaffold/utils/browser.ts +7 -63
- package/backend/_scaffold/utils/hooks.ts +5 -20
- package/backend/app.js +0 -5
- package/backend/config/scripts/generate-report.js +2 -2
- package/backend/config/scripts/run-tests.js +5 -1
- package/backend/constants/socketEvents.js +7 -4
- package/backend/lib/plumTestRuntime.js +262 -0
- package/backend/lib/reportFilename.js +1 -2
- package/backend/lib/{screenshotPoller.js → rrwebPoller.js} +7 -4
- package/backend/lib/serverBootstrap.js +14 -0
- package/backend/logs/runner-cmtbz5b1l0000mr0110b27w5k.log +8 -0
- package/backend/mcp/server.js +3 -47
- package/backend/package-lock.json +199 -1
- package/backend/package.json +3 -1
- package/backend/prisma/migrations/20260828120000_add_recording_and_split_runner_worker_count/migration.sql +39 -0
- package/backend/prisma/migrations/20260828140000_add_recording_started_ended_at/migration.sql +5 -0
- package/backend/prisma/migrations/20260828150000_strip_screenshot_refs_from_reports/migration.sql +34 -0
- package/backend/prisma/migrations/20260828160000_add_backup_include_reports/migration.sql +4 -0
- package/backend/prisma/schema.prisma +97 -70
- package/backend/routes/backup.routes.js +47 -1
- package/backend/routes/reports.routes.js +23 -0
- package/backend/server.js +1 -1
- package/backend/services/backupCronService.js +1 -1
- package/backend/services/backupService.js +134 -44
- package/backend/services/cronService.js +23 -15
- package/backend/services/nodeExecutionService.js +41 -15
- package/backend/services/nodeStreamRegistry.js +24 -0
- package/backend/services/reportService.js +167 -83
- package/backend/services/runnerService.js +19 -7
- package/backend/services/settingsService.js +6 -3
- package/backend/services/triggerService.js +20 -13
- package/backend/websockets/nodeSocketHandler.js +40 -0
- package/backend/websockets/socketHandler.js +9 -7
- package/bin/plum.js +58 -1
- package/frontend/.svelte-kit/ambient.d.ts +28 -28
- package/frontend/.svelte-kit/generated/server/internal.js +1 -1
- package/frontend/package-lock.json +121 -32
- package/frontend/package.json +1 -0
- package/frontend/src/lib/api/reports.js +13 -4
- package/frontend/src/lib/api/settings.js +16 -1
- package/frontend/src/lib/components/layout/RunnerPanel.svelte +9 -24
- package/frontend/src/lib/components/reports/ElementInspector.svelte +141 -0
- package/frontend/src/lib/components/reports/LiveReplayer.svelte +110 -0
- package/frontend/src/lib/components/reports/MultiTabTimeline.svelte +115 -0
- package/frontend/src/lib/components/reports/RecordingPlayer.svelte +786 -0
- package/frontend/src/lib/components/reports/StepsRail.svelte +109 -0
- package/frontend/src/lib/components/ui/CodeViewer.svelte +61 -0
- package/frontend/src/lib/constants.js +0 -1
- package/frontend/src/lib/copy/reports.js +18 -8
- package/frontend/src/lib/copy/settings.js +25 -4
- package/frontend/src/lib/socketEvents.js +7 -4
- package/frontend/src/lib/stores/runner.js +26 -3
- package/frontend/src/lib/styles/tokens.css +7 -0
- package/frontend/src/lib/utils/format.js +108 -2
- package/frontend/src/lib/utils/inspectElement.js +34 -0
- package/frontend/src/routes/reports/+page.svelte +79 -1
- package/frontend/src/routes/reports/[id]/+page.svelte +304 -495
- package/frontend/src/routes/reports/live/+page.svelte +236 -260
- package/frontend/src/routes/settings/+page.svelte +246 -8
- package/package.json +1 -1
- package/backend/playwright.config.js +0 -85
package/backend/mcp/server.js
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
* runs in-process inside the Plum backend, not as a separate subprocess.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
const fs = require('fs');
|
|
16
15
|
const path = require('path');
|
|
17
16
|
|
|
18
17
|
// Use absolute paths to bypass the SDK's wildcard export mapping
|
|
@@ -29,7 +28,6 @@ const { McpServer } = require(path.join(sdkCjs, 'server', 'mcp.js'));
|
|
|
29
28
|
const { z } = require('zod');
|
|
30
29
|
const { TRIGGER_TYPE } = require('../constants/triggers');
|
|
31
30
|
const { JOB_STATUS } = require('../constants/jobStatus');
|
|
32
|
-
const { SCREENSHOTS_DIR } = require('../lib/reportFilename');
|
|
33
31
|
const testSuiteService = require('../services/testSuiteService');
|
|
34
32
|
const testCaseService = require('../services/testCaseService');
|
|
35
33
|
const triggerService = require('../services/triggerService');
|
|
@@ -83,13 +81,6 @@ function summariseReport(report) {
|
|
|
83
81
|
};
|
|
84
82
|
}
|
|
85
83
|
|
|
86
|
-
// ---------------------------------------------------------------------------
|
|
87
|
-
// Screenshots
|
|
88
|
-
// ---------------------------------------------------------------------------
|
|
89
|
-
|
|
90
|
-
const SCREENSHOT_FILENAME_RE = /^[\w.-]+\.(png|jpg|jpeg)$/i;
|
|
91
|
-
const SCREENSHOT_MIME_TYPES = { png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg' };
|
|
92
|
-
|
|
93
84
|
// ---------------------------------------------------------------------------
|
|
94
85
|
// Server setup
|
|
95
86
|
// ---------------------------------------------------------------------------
|
|
@@ -450,11 +441,9 @@ function createMcpServer({ userId }) {
|
|
|
450
441
|
'get_report_scenario_detail',
|
|
451
442
|
[
|
|
452
443
|
'Get full per-scenario, per-step detail for a Plum test report — the data needed to diagnose',
|
|
453
|
-
"and self-heal a failing test: every step's status, duration, full error message
|
|
454
|
-
'screenshot URL (if one was captured for that step).',
|
|
444
|
+
"and self-heal a failing test: every step's status, duration, and full error message.",
|
|
455
445
|
'',
|
|
456
|
-
'Use
|
|
457
|
-
'and get_report_logs for the raw test-run stdout/stderr.'
|
|
446
|
+
'Use get_report_logs for the raw test-run stdout/stderr.'
|
|
458
447
|
].join('\n'),
|
|
459
448
|
{
|
|
460
449
|
reportId: z.number().int().describe('Numeric report ID'),
|
|
@@ -482,8 +471,7 @@ function createMcpServer({ userId }) {
|
|
|
482
471
|
name: st.name,
|
|
483
472
|
status: st.status,
|
|
484
473
|
duration: st.duration,
|
|
485
|
-
error: st.error ?? null
|
|
486
|
-
screenshot: st.screenshot ?? null
|
|
474
|
+
error: st.error ?? null
|
|
487
475
|
}))
|
|
488
476
|
}))
|
|
489
477
|
);
|
|
@@ -494,38 +482,6 @@ function createMcpServer({ userId }) {
|
|
|
494
482
|
}
|
|
495
483
|
);
|
|
496
484
|
|
|
497
|
-
server.tool(
|
|
498
|
-
'get_report_screenshot',
|
|
499
|
-
'Fetch a screenshot captured during a test step and return it as an image, so it can be viewed ' +
|
|
500
|
-
"directly. Get the filename from get_report_scenario_detail's step.screenshot field.",
|
|
501
|
-
{
|
|
502
|
-
filename: z.string().describe('Screenshot filename, e.g. "3f9c1e2a-....png"')
|
|
503
|
-
},
|
|
504
|
-
async ({ filename }) => {
|
|
505
|
-
if (!SCREENSHOT_FILENAME_RE.test(filename)) {
|
|
506
|
-
throw new Error(`Invalid screenshot filename: ${filename}`);
|
|
507
|
-
}
|
|
508
|
-
const filePath = path.join(SCREENSHOTS_DIR, filename);
|
|
509
|
-
let buffer;
|
|
510
|
-
try {
|
|
511
|
-
buffer = await fs.promises.readFile(filePath);
|
|
512
|
-
} catch {
|
|
513
|
-
throw new Error(`Screenshot not found: ${filename}`);
|
|
514
|
-
}
|
|
515
|
-
const ext = filename.split('.').pop().toLowerCase();
|
|
516
|
-
|
|
517
|
-
return {
|
|
518
|
-
content: [
|
|
519
|
-
{
|
|
520
|
-
type: 'image',
|
|
521
|
-
data: buffer.toString('base64'),
|
|
522
|
-
mimeType: SCREENSHOT_MIME_TYPES[ext]
|
|
523
|
-
}
|
|
524
|
-
]
|
|
525
|
-
};
|
|
526
|
-
}
|
|
527
|
-
);
|
|
528
|
-
|
|
529
485
|
server.tool(
|
|
530
486
|
'get_report_logs',
|
|
531
487
|
'Get the raw stdout/stderr log output captured during a Plum test run, tagged per runner. ' +
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"@cucumber/cucumber": "^11.2.0",
|
|
15
15
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
16
16
|
"@prisma/client": "^6.19.3",
|
|
17
|
+
"@rrweb/record": "^2.1.1",
|
|
17
18
|
"bcryptjs": "^2.4.3",
|
|
18
19
|
"chai": "^4.3.6",
|
|
19
20
|
"chai-soft-assert": "^0.0.5",
|
|
@@ -25,7 +26,8 @@
|
|
|
25
26
|
"node-cron": "^3.0.3",
|
|
26
27
|
"picocolors": "^1.1.1",
|
|
27
28
|
"playwright": "1.50.1",
|
|
28
|
-
"socket.io": "^4.8.1"
|
|
29
|
+
"socket.io": "^4.8.1",
|
|
30
|
+
"socket.io-client": "^4.8.3"
|
|
29
31
|
},
|
|
30
32
|
"devDependencies": {
|
|
31
33
|
"@playwright/test": "1.50.1",
|
|
@@ -1391,6 +1393,29 @@
|
|
|
1391
1393
|
"@prisma/debug": "6.19.3"
|
|
1392
1394
|
}
|
|
1393
1395
|
},
|
|
1396
|
+
"node_modules/@rrweb/record": {
|
|
1397
|
+
"version": "2.1.1",
|
|
1398
|
+
"resolved": "https://registry.npmjs.org/@rrweb/record/-/record-2.1.1.tgz",
|
|
1399
|
+
"integrity": "sha512-c2u175R+daC37PhbjaQV0migxtIXsLuD0mRgRstj/x/CRmLGctRlDi373/4miIUclleq4eZIQXAglH9opclFRQ==",
|
|
1400
|
+
"license": "MIT",
|
|
1401
|
+
"dependencies": {
|
|
1402
|
+
"@rrweb/types": "^2.1.1",
|
|
1403
|
+
"@rrweb/utils": "^2.1.1",
|
|
1404
|
+
"rrweb": "^2.1.1"
|
|
1405
|
+
}
|
|
1406
|
+
},
|
|
1407
|
+
"node_modules/@rrweb/types": {
|
|
1408
|
+
"version": "2.1.1",
|
|
1409
|
+
"resolved": "https://registry.npmjs.org/@rrweb/types/-/types-2.1.1.tgz",
|
|
1410
|
+
"integrity": "sha512-g0I3nCNL1S7slDXwhunxOuOoswkY8WVZJQrPFiwixOc6xoD514d1JLTuyAzCKIox8g/PaLKtV5g31Uk42inQSQ==",
|
|
1411
|
+
"license": "MIT"
|
|
1412
|
+
},
|
|
1413
|
+
"node_modules/@rrweb/utils": {
|
|
1414
|
+
"version": "2.1.1",
|
|
1415
|
+
"resolved": "https://registry.npmjs.org/@rrweb/utils/-/utils-2.1.1.tgz",
|
|
1416
|
+
"integrity": "sha512-x2SgJAD3YJ9eVcLZ5l6OqWMtczdA3VfS5XqPeqpZdQgV9oh6Id5GK2cDN4bimnkqryOcIJdz8cTvV0yNvqQ+nA==",
|
|
1417
|
+
"license": "MIT"
|
|
1418
|
+
},
|
|
1394
1419
|
"node_modules/@smithy/core": {
|
|
1395
1420
|
"version": "3.25.1",
|
|
1396
1421
|
"resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.25.1.tgz",
|
|
@@ -1576,6 +1601,12 @@
|
|
|
1576
1601
|
"@types/node": "*"
|
|
1577
1602
|
}
|
|
1578
1603
|
},
|
|
1604
|
+
"node_modules/@types/css-font-loading-module": {
|
|
1605
|
+
"version": "0.0.7",
|
|
1606
|
+
"resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz",
|
|
1607
|
+
"integrity": "sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==",
|
|
1608
|
+
"license": "MIT"
|
|
1609
|
+
},
|
|
1579
1610
|
"node_modules/@types/deep-eql": {
|
|
1580
1611
|
"version": "4.0.2",
|
|
1581
1612
|
"resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
|
|
@@ -1601,6 +1632,12 @@
|
|
|
1601
1632
|
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
|
|
1602
1633
|
"integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ=="
|
|
1603
1634
|
},
|
|
1635
|
+
"node_modules/@xstate/fsm": {
|
|
1636
|
+
"version": "1.6.5",
|
|
1637
|
+
"resolved": "https://registry.npmjs.org/@xstate/fsm/-/fsm-1.6.5.tgz",
|
|
1638
|
+
"integrity": "sha512-b5o1I6aLNeYlU/3CPlj/Z91ybk1gUsKT+5NAJI+2W4UjvS5KLG28K9v5UvNoFVjHV8PajVZ00RH3vnjyQO7ZAw==",
|
|
1639
|
+
"license": "MIT"
|
|
1640
|
+
},
|
|
1604
1641
|
"node_modules/accepts": {
|
|
1605
1642
|
"version": "1.3.8",
|
|
1606
1643
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
|
@@ -1747,6 +1784,15 @@
|
|
|
1747
1784
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
|
1748
1785
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
|
1749
1786
|
},
|
|
1787
|
+
"node_modules/base64-arraybuffer": {
|
|
1788
|
+
"version": "1.0.2",
|
|
1789
|
+
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
|
1790
|
+
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
|
|
1791
|
+
"license": "MIT",
|
|
1792
|
+
"engines": {
|
|
1793
|
+
"node": ">= 0.6.0"
|
|
1794
|
+
}
|
|
1795
|
+
},
|
|
1750
1796
|
"node_modules/base64id": {
|
|
1751
1797
|
"version": "2.0.0",
|
|
1752
1798
|
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
|
|
@@ -2339,6 +2385,40 @@
|
|
|
2339
2385
|
"node": ">=10.2.0"
|
|
2340
2386
|
}
|
|
2341
2387
|
},
|
|
2388
|
+
"node_modules/engine.io-client": {
|
|
2389
|
+
"version": "6.6.6",
|
|
2390
|
+
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.6.tgz",
|
|
2391
|
+
"integrity": "sha512-iY6QdftLQ9pyiPoX082bpf/u1UewnOaJrtJIF9T0++QB34lZrj0uP+Q/bj8AlUsAxqhnkTV2BS8SBZSxOmoV5Q==",
|
|
2392
|
+
"license": "MIT",
|
|
2393
|
+
"dependencies": {
|
|
2394
|
+
"@socket.io/component-emitter": "~3.1.0",
|
|
2395
|
+
"debug": "~4.4.1",
|
|
2396
|
+
"engine.io-parser": "~5.2.1",
|
|
2397
|
+
"ws": "~8.21.0",
|
|
2398
|
+
"xmlhttprequest-ssl": "~2.1.1"
|
|
2399
|
+
}
|
|
2400
|
+
},
|
|
2401
|
+
"node_modules/engine.io-client/node_modules/ws": {
|
|
2402
|
+
"version": "8.21.3",
|
|
2403
|
+
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz",
|
|
2404
|
+
"integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==",
|
|
2405
|
+
"license": "MIT",
|
|
2406
|
+
"engines": {
|
|
2407
|
+
"node": ">=10.0.0"
|
|
2408
|
+
},
|
|
2409
|
+
"peerDependencies": {
|
|
2410
|
+
"bufferutil": "^4.0.1",
|
|
2411
|
+
"utf-8-validate": ">=5.0.2"
|
|
2412
|
+
},
|
|
2413
|
+
"peerDependenciesMeta": {
|
|
2414
|
+
"bufferutil": {
|
|
2415
|
+
"optional": true
|
|
2416
|
+
},
|
|
2417
|
+
"utf-8-validate": {
|
|
2418
|
+
"optional": true
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
},
|
|
2342
2422
|
"node_modules/engine.io-parser": {
|
|
2343
2423
|
"version": "5.2.3",
|
|
2344
2424
|
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
|
@@ -3326,6 +3406,12 @@
|
|
|
3326
3406
|
"node": ">=16 || 14 >=14.17"
|
|
3327
3407
|
}
|
|
3328
3408
|
},
|
|
3409
|
+
"node_modules/mitt": {
|
|
3410
|
+
"version": "3.0.1",
|
|
3411
|
+
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
|
|
3412
|
+
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
|
3413
|
+
"license": "MIT"
|
|
3414
|
+
},
|
|
3329
3415
|
"node_modules/mkdirp": {
|
|
3330
3416
|
"version": "2.1.6",
|
|
3331
3417
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz",
|
|
@@ -3355,6 +3441,24 @@
|
|
|
3355
3441
|
"thenify-all": "^1.0.0"
|
|
3356
3442
|
}
|
|
3357
3443
|
},
|
|
3444
|
+
"node_modules/nanoid": {
|
|
3445
|
+
"version": "3.3.18",
|
|
3446
|
+
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
|
|
3447
|
+
"integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
|
|
3448
|
+
"funding": [
|
|
3449
|
+
{
|
|
3450
|
+
"type": "github",
|
|
3451
|
+
"url": "https://github.com/sponsors/ai"
|
|
3452
|
+
}
|
|
3453
|
+
],
|
|
3454
|
+
"license": "MIT",
|
|
3455
|
+
"bin": {
|
|
3456
|
+
"nanoid": "bin/nanoid.cjs"
|
|
3457
|
+
},
|
|
3458
|
+
"engines": {
|
|
3459
|
+
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
|
3460
|
+
}
|
|
3461
|
+
},
|
|
3358
3462
|
"node_modules/negotiator": {
|
|
3359
3463
|
"version": "0.6.3",
|
|
3360
3464
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
|
@@ -3643,6 +3747,34 @@
|
|
|
3643
3747
|
"node": ">=18"
|
|
3644
3748
|
}
|
|
3645
3749
|
},
|
|
3750
|
+
"node_modules/postcss": {
|
|
3751
|
+
"version": "8.5.26",
|
|
3752
|
+
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz",
|
|
3753
|
+
"integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==",
|
|
3754
|
+
"funding": [
|
|
3755
|
+
{
|
|
3756
|
+
"type": "opencollective",
|
|
3757
|
+
"url": "https://opencollective.com/postcss/"
|
|
3758
|
+
},
|
|
3759
|
+
{
|
|
3760
|
+
"type": "tidelift",
|
|
3761
|
+
"url": "https://tidelift.com/funding/github/npm/postcss"
|
|
3762
|
+
},
|
|
3763
|
+
{
|
|
3764
|
+
"type": "github",
|
|
3765
|
+
"url": "https://github.com/sponsors/ai"
|
|
3766
|
+
}
|
|
3767
|
+
],
|
|
3768
|
+
"license": "MIT",
|
|
3769
|
+
"dependencies": {
|
|
3770
|
+
"nanoid": "^3.3.17",
|
|
3771
|
+
"picocolors": "^1.1.1",
|
|
3772
|
+
"source-map-js": "^1.2.1"
|
|
3773
|
+
},
|
|
3774
|
+
"engines": {
|
|
3775
|
+
"node": "^10 || ^12 || >=14"
|
|
3776
|
+
}
|
|
3777
|
+
},
|
|
3646
3778
|
"node_modules/prisma": {
|
|
3647
3779
|
"version": "6.19.3",
|
|
3648
3780
|
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.3.tgz",
|
|
@@ -3888,6 +4020,40 @@
|
|
|
3888
4020
|
"url": "https://opencollective.com/express"
|
|
3889
4021
|
}
|
|
3890
4022
|
},
|
|
4023
|
+
"node_modules/rrdom": {
|
|
4024
|
+
"version": "2.1.1",
|
|
4025
|
+
"resolved": "https://registry.npmjs.org/rrdom/-/rrdom-2.1.1.tgz",
|
|
4026
|
+
"integrity": "sha512-VBkTF3bGNqcZjqnbo/gKi9GVQPIxiG0dofw7CQdAZQFQ2juJdt2YKIf3oS9QudL8HTpGh9bz5TUN+3amBn1Geg==",
|
|
4027
|
+
"license": "MIT",
|
|
4028
|
+
"dependencies": {
|
|
4029
|
+
"rrweb-snapshot": "^2.1.1"
|
|
4030
|
+
}
|
|
4031
|
+
},
|
|
4032
|
+
"node_modules/rrweb": {
|
|
4033
|
+
"version": "2.1.1",
|
|
4034
|
+
"resolved": "https://registry.npmjs.org/rrweb/-/rrweb-2.1.1.tgz",
|
|
4035
|
+
"integrity": "sha512-ToxhJg3SsrAhw+/DPhI/2iiwZQIrGK5BGkZ0kHn4qUExcvQYRaolkciD1FWX2+r6vf1IxFyhsoRY18h3D+XoCg==",
|
|
4036
|
+
"license": "MIT",
|
|
4037
|
+
"dependencies": {
|
|
4038
|
+
"@rrweb/types": "^2.1.1",
|
|
4039
|
+
"@rrweb/utils": "^2.1.1",
|
|
4040
|
+
"@types/css-font-loading-module": "0.0.7",
|
|
4041
|
+
"@xstate/fsm": "^1.4.0",
|
|
4042
|
+
"base64-arraybuffer": "^1.0.1",
|
|
4043
|
+
"mitt": "^3.0.0",
|
|
4044
|
+
"rrdom": "^2.1.1",
|
|
4045
|
+
"rrweb-snapshot": "^2.1.1"
|
|
4046
|
+
}
|
|
4047
|
+
},
|
|
4048
|
+
"node_modules/rrweb-snapshot": {
|
|
4049
|
+
"version": "2.1.1",
|
|
4050
|
+
"resolved": "https://registry.npmjs.org/rrweb-snapshot/-/rrweb-snapshot-2.1.1.tgz",
|
|
4051
|
+
"integrity": "sha512-al4Kx7Am7YlIn/5Yb2EMr7cdmjGiK+cLhdilJEXqkFXgpnys8t/yMJumXy2Ormgirpg3MBnFha0GTgny+iIL2w==",
|
|
4052
|
+
"license": "MIT",
|
|
4053
|
+
"dependencies": {
|
|
4054
|
+
"postcss": "^8.4.38"
|
|
4055
|
+
}
|
|
4056
|
+
},
|
|
3891
4057
|
"node_modules/safe-buffer": {
|
|
3892
4058
|
"version": "5.2.1",
|
|
3893
4059
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
|
@@ -4162,6 +4328,21 @@
|
|
|
4162
4328
|
}
|
|
4163
4329
|
}
|
|
4164
4330
|
},
|
|
4331
|
+
"node_modules/socket.io-client": {
|
|
4332
|
+
"version": "4.8.3",
|
|
4333
|
+
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz",
|
|
4334
|
+
"integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==",
|
|
4335
|
+
"license": "MIT",
|
|
4336
|
+
"dependencies": {
|
|
4337
|
+
"@socket.io/component-emitter": "~3.1.0",
|
|
4338
|
+
"debug": "~4.4.1",
|
|
4339
|
+
"engine.io-client": "~6.6.1",
|
|
4340
|
+
"socket.io-parser": "~4.2.4"
|
|
4341
|
+
},
|
|
4342
|
+
"engines": {
|
|
4343
|
+
"node": ">=10.0.0"
|
|
4344
|
+
}
|
|
4345
|
+
},
|
|
4165
4346
|
"node_modules/socket.io-parser": {
|
|
4166
4347
|
"version": "4.2.4",
|
|
4167
4348
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
|
@@ -4214,6 +4395,15 @@
|
|
|
4214
4395
|
"node": ">=0.10.0"
|
|
4215
4396
|
}
|
|
4216
4397
|
},
|
|
4398
|
+
"node_modules/source-map-js": {
|
|
4399
|
+
"version": "1.2.1",
|
|
4400
|
+
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
|
4401
|
+
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
|
4402
|
+
"license": "BSD-3-Clause",
|
|
4403
|
+
"engines": {
|
|
4404
|
+
"node": ">=0.10.0"
|
|
4405
|
+
}
|
|
4406
|
+
},
|
|
4217
4407
|
"node_modules/source-map-support": {
|
|
4218
4408
|
"version": "0.5.21",
|
|
4219
4409
|
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
|
@@ -4790,6 +4980,14 @@
|
|
|
4790
4980
|
"node": ">=8.0"
|
|
4791
4981
|
}
|
|
4792
4982
|
},
|
|
4983
|
+
"node_modules/xmlhttprequest-ssl": {
|
|
4984
|
+
"version": "2.1.2",
|
|
4985
|
+
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
|
|
4986
|
+
"integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
|
|
4987
|
+
"engines": {
|
|
4988
|
+
"node": ">=0.4.0"
|
|
4989
|
+
}
|
|
4990
|
+
},
|
|
4793
4991
|
"node_modules/yallist": {
|
|
4794
4992
|
"version": "4.0.0",
|
|
4795
4993
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
package/backend/package.json
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"@cucumber/cucumber": "^11.2.0",
|
|
28
28
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
29
29
|
"@prisma/client": "^6.19.3",
|
|
30
|
+
"@rrweb/record": "^2.1.1",
|
|
30
31
|
"bcryptjs": "^2.4.3",
|
|
31
32
|
"chai": "^4.3.6",
|
|
32
33
|
"chai-soft-assert": "^0.0.5",
|
|
@@ -38,6 +39,7 @@
|
|
|
38
39
|
"node-cron": "^3.0.3",
|
|
39
40
|
"picocolors": "^1.1.1",
|
|
40
41
|
"playwright": "1.50.1",
|
|
41
|
-
"socket.io": "^4.8.1"
|
|
42
|
+
"socket.io": "^4.8.1",
|
|
43
|
+
"socket.io-client": "^4.8.3"
|
|
42
44
|
}
|
|
43
45
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
-- AlterTable: split the overloaded `runners` column into explicit runnerCount
|
|
2
|
+
-- (distinct runner machines/lanes) and workerCount (Cucumber --parallel workers
|
|
3
|
+
-- within a runner) — the old column meant one or the other depending on which
|
|
4
|
+
-- code path wrote it.
|
|
5
|
+
ALTER TABLE "Report" ADD COLUMN "runnerCount" INTEGER NOT NULL DEFAULT 1;
|
|
6
|
+
ALTER TABLE "Report" ADD COLUMN "workerCount" INTEGER NOT NULL DEFAULT 1;
|
|
7
|
+
|
|
8
|
+
-- Backfill: distributed runs stored a comma-joined runnerName (see
|
|
9
|
+
-- reportService.saveCombinedReport) and used `runners` for lane count.
|
|
10
|
+
-- Everything else used `runners` for the Cucumber --parallel worker count.
|
|
11
|
+
UPDATE "Report"
|
|
12
|
+
SET "runnerCount" = "runners", "workerCount" = 1
|
|
13
|
+
WHERE "runnerName" LIKE '%, %';
|
|
14
|
+
|
|
15
|
+
UPDATE "Report"
|
|
16
|
+
SET "runnerCount" = 1, "workerCount" = "runners"
|
|
17
|
+
WHERE "runnerName" IS NULL OR "runnerName" NOT LIKE '%, %';
|
|
18
|
+
|
|
19
|
+
ALTER TABLE "Report" DROP COLUMN "runners";
|
|
20
|
+
|
|
21
|
+
-- CreateTable: one row per (report, scenario, worker, browser tab). `events`
|
|
22
|
+
-- is a gzip-compressed JSON array of rrweb events.
|
|
23
|
+
CREATE TABLE "Recording" (
|
|
24
|
+
"id" SERIAL NOT NULL,
|
|
25
|
+
"reportId" INTEGER NOT NULL,
|
|
26
|
+
"scenarioId" TEXT NOT NULL,
|
|
27
|
+
"workerId" INTEGER NOT NULL DEFAULT 1,
|
|
28
|
+
"tabId" TEXT NOT NULL DEFAULT 'main',
|
|
29
|
+
"tabIndex" INTEGER NOT NULL DEFAULT 0,
|
|
30
|
+
"events" BYTEA NOT NULL,
|
|
31
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
32
|
+
|
|
33
|
+
CONSTRAINT "Recording_pkey" PRIMARY KEY ("id")
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
CREATE INDEX "Recording_reportId_idx" ON "Recording"("reportId");
|
|
37
|
+
CREATE INDEX "Recording_reportId_scenarioId_idx" ON "Recording"("reportId", "scenarioId");
|
|
38
|
+
|
|
39
|
+
ALTER TABLE "Recording" ADD CONSTRAINT "Recording_reportId_fkey" FOREIGN KEY ("reportId") REFERENCES "Report"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
-- AlterTable: epoch-ms boundaries of each tab recording, so the replay UI can
|
|
2
|
+
-- line multiple tabs up on one absolute timeline and auto-switch between them.
|
|
3
|
+
-- Nullable — existing recordings predate this field and have no boundary data.
|
|
4
|
+
ALTER TABLE "Recording" ADD COLUMN "startedAt" BIGINT;
|
|
5
|
+
ALTER TABLE "Recording" ADD COLUMN "endedAt" BIGINT;
|
package/backend/prisma/migrations/20260828150000_strip_screenshot_refs_from_reports/migration.sql
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
-- One-time data cleanup: screenshot capture was removed in favor of rrweb
|
|
2
|
+
-- session recording (see RRWEB_MIGRATION_PLAN.md Phase 4). Historical reports
|
|
3
|
+
-- still carry a `screenshot` filename on each step in the content JSONB —
|
|
4
|
+
-- strip it so old reports stop referencing files that no longer exist on
|
|
5
|
+
-- disk (deleted once, at startup, by serverBootstrap.js). Steps/logs are
|
|
6
|
+
-- otherwise untouched.
|
|
7
|
+
UPDATE "Report"
|
|
8
|
+
SET content = jsonb_set(
|
|
9
|
+
content,
|
|
10
|
+
'{features}',
|
|
11
|
+
COALESCE((
|
|
12
|
+
SELECT jsonb_agg(
|
|
13
|
+
CASE
|
|
14
|
+
WHEN jsonb_typeof(feature->'scenarios') = 'array' THEN
|
|
15
|
+
jsonb_set(feature, '{scenarios}', (
|
|
16
|
+
SELECT jsonb_agg(
|
|
17
|
+
CASE
|
|
18
|
+
WHEN jsonb_typeof(scenario->'steps') = 'array' THEN
|
|
19
|
+
jsonb_set(scenario, '{steps}', (
|
|
20
|
+
SELECT jsonb_agg(step - 'screenshot')
|
|
21
|
+
FROM jsonb_array_elements(scenario->'steps') AS step
|
|
22
|
+
))
|
|
23
|
+
ELSE scenario
|
|
24
|
+
END
|
|
25
|
+
)
|
|
26
|
+
FROM jsonb_array_elements(feature->'scenarios') AS scenario
|
|
27
|
+
))
|
|
28
|
+
ELSE feature
|
|
29
|
+
END
|
|
30
|
+
)
|
|
31
|
+
FROM jsonb_array_elements(content->'features') AS feature
|
|
32
|
+
), content->'features')
|
|
33
|
+
)
|
|
34
|
+
WHERE jsonb_typeof(content->'features') = 'array';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
-- Opt-in toggle for including Report/Recording data in exports (manual and
|
|
2
|
+
-- scheduled S3). Defaults off — reports can be large (rrweb recordings), and
|
|
3
|
+
-- this is new behavior existing installs shouldn't get switched on for silently.
|
|
4
|
+
ALTER TABLE "Project" ADD COLUMN "backupIncludeReports" BOOLEAN NOT NULL DEFAULT false;
|