plum-e2e 2.8.6 → 2.9.0
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/_scaffold/utils/browser.ts +184 -30
- package/backend/_scaffold/utils/hooks.ts +37 -8
- package/backend/app.js +0 -5
- package/backend/config/scripts/generate-report.js +2 -2
- package/backend/constants/socketEvents.js +7 -4
- 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/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
|
@@ -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;
|
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
This file is part of Plum.
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
*/
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
* Plum is free software: you can redistribute it and/or modify
|
|
4
|
+
* it under the terms of the GNU General Public License as published by
|
|
5
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
6
|
+
* (at your option) any later version.
|
|
7
|
+
* Plum is distributed in the hope that it will be useful,
|
|
8
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10
|
+
* GNU General Public License for more details.
|
|
11
|
+
* You should have received a copy of the GNU General Public License
|
|
12
|
+
* along with Plum. If not, see https://www.gnu.org/licenses/.
|
|
13
|
+
*/
|
|
17
14
|
generator client {
|
|
18
15
|
provider = "prisma-client-js"
|
|
19
16
|
}
|
|
@@ -57,7 +54,12 @@ model Report {
|
|
|
57
54
|
status String
|
|
58
55
|
tags String
|
|
59
56
|
triggerType String @default("manual-trigger")
|
|
60
|
-
|
|
57
|
+
// runnerCount = distinct runner machines/lanes in this run (built-in counts as one).
|
|
58
|
+
// workerCount = Cucumber --parallel workers within each runner. Two independent
|
|
59
|
+
// axes of parallelism — kept as separate columns since one DB column used to mean
|
|
60
|
+
// one or the other depending on which code path wrote it.
|
|
61
|
+
runnerCount Int @default(1)
|
|
62
|
+
workerCount Int @default(1)
|
|
61
63
|
browser String @default("chromium")
|
|
62
64
|
runnerName String?
|
|
63
65
|
runnerId String?
|
|
@@ -71,6 +73,7 @@ model Report {
|
|
|
71
73
|
duration Int?
|
|
72
74
|
createdAt DateTime @default(now())
|
|
73
75
|
testHistory TestCaseHistory[]
|
|
76
|
+
recordings Recording[]
|
|
74
77
|
|
|
75
78
|
@@index([createdAt])
|
|
76
79
|
@@index([status])
|
|
@@ -79,30 +82,54 @@ model Report {
|
|
|
79
82
|
@@index([cronJobId])
|
|
80
83
|
}
|
|
81
84
|
|
|
85
|
+
// One row per (report, scenario, worker, browser tab) — a scenario that opens
|
|
86
|
+
// extra tabs, or runs under a multi-worker runner, produces multiple rows.
|
|
87
|
+
// `events` is a gzip-compressed JSON array of rrweb events.
|
|
88
|
+
model Recording {
|
|
89
|
+
id Int @id @default(autoincrement())
|
|
90
|
+
reportId Int
|
|
91
|
+
report Report @relation(fields: [reportId], references: [id], onDelete: Cascade)
|
|
92
|
+
scenarioId String
|
|
93
|
+
workerId Int @default(1)
|
|
94
|
+
tabId String @default("main")
|
|
95
|
+
tabIndex Int @default(0)
|
|
96
|
+
// Epoch ms of this tab's first/last recorded event — lets the replay UI line
|
|
97
|
+
// multiple tabs up on one absolute timeline and auto-switch between them.
|
|
98
|
+
// Null on recordings written before this field existed.
|
|
99
|
+
startedAt BigInt?
|
|
100
|
+
endedAt BigInt?
|
|
101
|
+
events Bytes
|
|
102
|
+
createdAt DateTime @default(now())
|
|
103
|
+
|
|
104
|
+
@@index([reportId])
|
|
105
|
+
@@index([reportId, scenarioId])
|
|
106
|
+
}
|
|
107
|
+
|
|
82
108
|
model Project {
|
|
83
|
-
id
|
|
84
|
-
name
|
|
85
|
-
logoUrl
|
|
86
|
-
timezone
|
|
87
|
-
testCasePrefix
|
|
88
|
-
testSuitePrefix
|
|
89
|
-
caseSeqNext
|
|
90
|
-
suiteSeqNext
|
|
91
|
-
discordWebhookUrl
|
|
92
|
-
slackWebhookUrl
|
|
93
|
-
notifyPublicUrl
|
|
94
|
-
backupEnabled
|
|
95
|
-
backupCron
|
|
96
|
-
backupS3Endpoint
|
|
97
|
-
backupS3Region
|
|
98
|
-
backupS3Bucket
|
|
99
|
-
backupS3AccessKey
|
|
100
|
-
backupS3SecretKey
|
|
101
|
-
backupS3Prefix
|
|
102
|
-
backupLastRunAt
|
|
103
|
-
backupLastStatus
|
|
104
|
-
|
|
105
|
-
|
|
109
|
+
id Int @id @default(autoincrement())
|
|
110
|
+
name String @default("")
|
|
111
|
+
logoUrl String @default("")
|
|
112
|
+
timezone String @default("UTC")
|
|
113
|
+
testCasePrefix String @default("TC")
|
|
114
|
+
testSuitePrefix String @default("TS")
|
|
115
|
+
caseSeqNext Int @default(0)
|
|
116
|
+
suiteSeqNext Int @default(0)
|
|
117
|
+
discordWebhookUrl String @default("")
|
|
118
|
+
slackWebhookUrl String @default("")
|
|
119
|
+
notifyPublicUrl String @default("")
|
|
120
|
+
backupEnabled Boolean @default(false)
|
|
121
|
+
backupCron String @default("0 2 * * *")
|
|
122
|
+
backupS3Endpoint String @default("")
|
|
123
|
+
backupS3Region String @default("")
|
|
124
|
+
backupS3Bucket String @default("")
|
|
125
|
+
backupS3AccessKey String @default("")
|
|
126
|
+
backupS3SecretKey String @default("")
|
|
127
|
+
backupS3Prefix String @default("")
|
|
128
|
+
backupLastRunAt DateTime?
|
|
129
|
+
backupLastStatus String @default("")
|
|
130
|
+
backupIncludeReports Boolean @default(false)
|
|
131
|
+
mcpKey String @default("")
|
|
132
|
+
maxRetries Int @default(0)
|
|
106
133
|
}
|
|
107
134
|
|
|
108
135
|
model User {
|
|
@@ -117,8 +144,8 @@ model User {
|
|
|
117
144
|
suites TestSuite[]
|
|
118
145
|
cases TestCase[]
|
|
119
146
|
runs TestRun[]
|
|
120
|
-
executedEntries TestRunEntry[]
|
|
121
|
-
assignedEntries TestRunEntry[]
|
|
147
|
+
executedEntries TestRunEntry[] @relation("entryExecutor")
|
|
148
|
+
assignedEntries TestRunEntry[] @relation("entryAssignee")
|
|
122
149
|
caseHistories TestCaseHistory[]
|
|
123
150
|
}
|
|
124
151
|
|
|
@@ -138,21 +165,21 @@ model TestSuite {
|
|
|
138
165
|
}
|
|
139
166
|
|
|
140
167
|
model TestCase {
|
|
141
|
-
id
|
|
142
|
-
displayId
|
|
143
|
-
title
|
|
144
|
-
description
|
|
145
|
-
priority
|
|
146
|
-
isAutomated
|
|
147
|
-
suiteId
|
|
148
|
-
suite
|
|
149
|
-
createdById
|
|
150
|
-
createdBy
|
|
151
|
-
createdAt
|
|
152
|
-
updatedAt
|
|
153
|
-
steps
|
|
154
|
-
runEntries
|
|
155
|
-
history
|
|
168
|
+
id String @id @default(cuid())
|
|
169
|
+
displayId String @unique
|
|
170
|
+
title String
|
|
171
|
+
description String @default("")
|
|
172
|
+
priority String @default("Medium")
|
|
173
|
+
isAutomated Boolean @default(false)
|
|
174
|
+
suiteId String
|
|
175
|
+
suite TestSuite @relation(fields: [suiteId], references: [id], onDelete: Cascade)
|
|
176
|
+
createdById String
|
|
177
|
+
createdBy User @relation(fields: [createdById], references: [id])
|
|
178
|
+
createdAt DateTime @default(now())
|
|
179
|
+
updatedAt DateTime @updatedAt
|
|
180
|
+
steps TestStep[]
|
|
181
|
+
runEntries TestRunEntry[]
|
|
182
|
+
history TestCaseHistory[]
|
|
156
183
|
|
|
157
184
|
@@index([suiteId])
|
|
158
185
|
}
|
|
@@ -169,13 +196,13 @@ model TestStep {
|
|
|
169
196
|
}
|
|
170
197
|
|
|
171
198
|
model TestRun {
|
|
172
|
-
id String
|
|
199
|
+
id String @id @default(cuid())
|
|
173
200
|
title String
|
|
174
|
-
status String
|
|
201
|
+
status String @default("backlog")
|
|
175
202
|
createdById String
|
|
176
|
-
createdBy User
|
|
177
|
-
createdAt DateTime
|
|
178
|
-
updatedAt DateTime
|
|
203
|
+
createdBy User @relation(fields: [createdById], references: [id])
|
|
204
|
+
createdAt DateTime @default(now())
|
|
205
|
+
updatedAt DateTime @updatedAt
|
|
179
206
|
entries TestRunEntry[]
|
|
180
207
|
history TestCaseHistory[]
|
|
181
208
|
reports Report[]
|
|
@@ -203,19 +230,19 @@ model TestRunEntry {
|
|
|
203
230
|
}
|
|
204
231
|
|
|
205
232
|
model TestCaseHistory {
|
|
206
|
-
id String
|
|
233
|
+
id String @id @default(cuid())
|
|
207
234
|
caseId String
|
|
208
|
-
case TestCase
|
|
235
|
+
case TestCase @relation(fields: [caseId], references: [id], onDelete: Cascade)
|
|
209
236
|
runId String?
|
|
210
|
-
run TestRun?
|
|
237
|
+
run TestRun? @relation(fields: [runId], references: [id], onDelete: SetNull)
|
|
211
238
|
reportId Int?
|
|
212
|
-
report Report?
|
|
239
|
+
report Report? @relation(fields: [reportId], references: [id], onDelete: SetNull)
|
|
213
240
|
result String
|
|
214
|
-
source String
|
|
215
|
-
notes String
|
|
241
|
+
source String @default("manual")
|
|
242
|
+
notes String @default("")
|
|
216
243
|
executedById String?
|
|
217
|
-
executedBy User?
|
|
218
|
-
executedAt DateTime
|
|
244
|
+
executedBy User? @relation(fields: [executedById], references: [id])
|
|
245
|
+
executedAt DateTime @default(now())
|
|
219
246
|
|
|
220
247
|
@@index([caseId])
|
|
221
248
|
@@index([runId])
|