plum-e2e 2.4.13 → 2.5.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/CLAUDE.md +1 -1
- package/backend/_scaffold/utils/browser.ts +47 -2
- package/backend/_scaffold/utils/hooks.ts +25 -2
- package/backend/config/scripts/run-tests.js +35 -13
- package/backend/logs/runner-cmr8o2x3n0000mg01410tpuck.log +20 -0
- package/backend/logs/runner-cmr90hvcx0000n2016edtmrtv.log +43 -0
- package/backend/logs/runner-cmr90i7h20001n201g2wg6v3n.log +43 -0
- package/backend/logs/runner-cmr90iilm0002n201mmisva95.log +43 -0
- package/backend/logs/runner-cmr90j70r0003n201xz8525dz.log +20 -0
- package/backend/logs/runner-cmr90yup70000qp017ianc2gp.log +43 -0
- package/backend/logs/runner-cmr90zbtm0001qp01u9tfdkh1.log +20 -0
- package/backend/logs/runner-cmr91a3mw0000tb01414rd1df.log +20 -0
- package/backend/logs/runner-cmr91dhla0001tb01jp7thjnl.log +20 -0
- package/backend/prisma/migrations/20260706000000_add_report_logs/migration.sql +2 -0
- package/backend/prisma/migrations/20260707032554_add_perf_indexes/migration.sql +44 -0
- package/backend/prisma/schema.prisma +20 -0
- package/backend/routes/node.routes.js +42 -4
- package/backend/routes/reports.routes.js +4 -2
- package/backend/services/reportService.js +58 -20
- package/backend/services/runnerService.js +11 -1
- package/backend/websockets/socketHandler.js +162 -16
- package/bin/plum.js +96 -68
- package/frontend/package.json +1 -1
- package/frontend/src/lib/api/reports.js +16 -5
- package/frontend/src/lib/components/layout/RunnerPanel.svelte +53 -55
- package/frontend/src/lib/components/ui/AutomatedBadge.svelte +56 -0
- package/frontend/src/lib/components/ui/BackLink.svelte +77 -0
- package/frontend/src/lib/components/ui/Badge.svelte +1 -1
- package/frontend/src/lib/components/ui/Button.svelte +1 -1
- package/frontend/src/lib/components/ui/CaseIdChip.svelte +59 -0
- package/frontend/src/lib/components/ui/ConfirmModal.svelte +1 -1
- package/frontend/src/lib/components/ui/Pagination.svelte +1 -1
- package/frontend/src/lib/components/ui/PriorityBadge.svelte +83 -0
- package/frontend/src/lib/components/ui/ResultChip.svelte +92 -0
- package/frontend/src/lib/components/ui/StatusDot.svelte +64 -0
- package/frontend/src/lib/components/ui/StepKeyword.svelte +79 -0
- package/frontend/src/lib/components/ui/StepStatusIcon.svelte +66 -0
- package/frontend/src/lib/components/ui/TagChip.svelte +51 -0
- package/frontend/src/lib/constants.js +14 -0
- package/frontend/src/lib/stores/runner.js +5 -3
- package/frontend/src/lib/styles/tokens.css +26 -0
- package/frontend/src/lib/utils/format.js +74 -0
- package/frontend/src/routes/+page.svelte +3 -3
- package/frontend/src/routes/login/+page.svelte +55 -40
- package/frontend/src/routes/reports/+page.svelte +35 -27
- package/frontend/src/routes/reports/[id]/+page.svelte +703 -215
- package/frontend/src/routes/reports/live/+page.svelte +281 -283
- package/frontend/src/routes/scheduled-tests/+page.svelte +3 -3
- package/frontend/src/routes/settings/+page.svelte +6 -6
- package/frontend/src/routes/setup/+page.svelte +2 -1
- package/frontend/src/routes/test-repository/+page.svelte +5 -5
- package/frontend/src/routes/test-repository/runs/[id]/+page.svelte +33 -152
- package/frontend/src/routes/test-repository/suites/[id]/+page.svelte +4 -4
- package/frontend/vite.config.js +7 -1
- package/package.json +1 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
*
|
|
4
|
+
* Plum is free software: you can redistribute it and/or modify
|
|
5
|
+
* it under the terms of the GNU General Public License as published by
|
|
6
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* Plum is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU General Public License for more details.
|
|
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
|
+
-->
|
|
17
|
+
|
|
18
|
+
<!--
|
|
19
|
+
* This file is part of Plum.
|
|
20
|
+
*
|
|
21
|
+
* Plum is free software: you can redistribute it and/or modify
|
|
22
|
+
* it under the terms of the GNU General Public License as published by
|
|
23
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
24
|
+
* (at your option) any later version.
|
|
25
|
+
*
|
|
26
|
+
* Plum is distributed in the hope that it will be useful,
|
|
27
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
28
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
29
|
+
* GNU General Public License for more details.
|
|
30
|
+
*
|
|
31
|
+
* You should have received a copy of the GNU General Public License
|
|
32
|
+
* along with Plum. If not, see https://www.gnu.org/licenses/.
|
|
33
|
+
-->
|
|
34
|
+
|
|
35
|
+
<script>
|
|
36
|
+
export let id = '';
|
|
37
|
+
export let small = false;
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<span class="id-chip" class:small>{id}</span>
|
|
41
|
+
|
|
42
|
+
<style>
|
|
43
|
+
.id-chip {
|
|
44
|
+
font-family: 'JetBrains Mono', monospace;
|
|
45
|
+
font-weight: 600;
|
|
46
|
+
color: var(--accent);
|
|
47
|
+
background: var(--accent-soft);
|
|
48
|
+
border-radius: 4px;
|
|
49
|
+
letter-spacing: 0.02em;
|
|
50
|
+
font-size: 0.7rem;
|
|
51
|
+
padding: 0.1rem 0.4rem;
|
|
52
|
+
flex-shrink: 0;
|
|
53
|
+
white-space: nowrap;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.id-chip.small {
|
|
57
|
+
font-size: 0.63rem;
|
|
58
|
+
}
|
|
59
|
+
</style>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
*
|
|
4
|
+
* Plum is free software: you can redistribute it and/or modify
|
|
5
|
+
* it under the terms of the GNU General Public License as published by
|
|
6
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* Plum is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU General Public License for more details.
|
|
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
|
+
-->
|
|
17
|
+
|
|
18
|
+
<!--
|
|
19
|
+
* This file is part of Plum.
|
|
20
|
+
*
|
|
21
|
+
* Plum is free software: you can redistribute it and/or modify
|
|
22
|
+
* it under the terms of the GNU General Public License as published by
|
|
23
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
24
|
+
* (at your option) any later version.
|
|
25
|
+
*
|
|
26
|
+
* Plum is distributed in the hope that it will be useful,
|
|
27
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
28
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
29
|
+
* GNU General Public License for more details.
|
|
30
|
+
*
|
|
31
|
+
* You should have received a copy of the GNU General Public License
|
|
32
|
+
* along with Plum. If not, see https://www.gnu.org/licenses/.
|
|
33
|
+
-->
|
|
34
|
+
|
|
35
|
+
<script>
|
|
36
|
+
export let priority = 'Medium';
|
|
37
|
+
export let small = false;
|
|
38
|
+
|
|
39
|
+
$: p = priority?.toLowerCase() ?? 'medium';
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<span
|
|
43
|
+
class="priority-badge"
|
|
44
|
+
class:critical={p === 'critical'}
|
|
45
|
+
class:high={p === 'high'}
|
|
46
|
+
class:medium={p === 'medium'}
|
|
47
|
+
class:low={p === 'low'}
|
|
48
|
+
class:small
|
|
49
|
+
>
|
|
50
|
+
{priority}
|
|
51
|
+
</span>
|
|
52
|
+
|
|
53
|
+
<style>
|
|
54
|
+
.priority-badge {
|
|
55
|
+
font-size: 0.65rem;
|
|
56
|
+
font-weight: 500;
|
|
57
|
+
border-radius: var(--radius-pill);
|
|
58
|
+
padding: 0.12rem 0.45rem;
|
|
59
|
+
flex-shrink: 0;
|
|
60
|
+
white-space: nowrap;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.priority-badge.small {
|
|
64
|
+
font-size: 0.6rem;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.critical {
|
|
68
|
+
background: var(--fail-soft);
|
|
69
|
+
color: var(--fail);
|
|
70
|
+
}
|
|
71
|
+
.high {
|
|
72
|
+
background: var(--warn-soft);
|
|
73
|
+
color: var(--warn);
|
|
74
|
+
}
|
|
75
|
+
.medium {
|
|
76
|
+
background: var(--node-soft);
|
|
77
|
+
color: var(--node);
|
|
78
|
+
}
|
|
79
|
+
.low {
|
|
80
|
+
background: var(--bg-subtle);
|
|
81
|
+
color: var(--text-muted);
|
|
82
|
+
}
|
|
83
|
+
</style>
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
*
|
|
4
|
+
* Plum is free software: you can redistribute it and/or modify
|
|
5
|
+
* it under the terms of the GNU General Public License as published by
|
|
6
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* Plum is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU General Public License for more details.
|
|
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
|
+
-->
|
|
17
|
+
|
|
18
|
+
<!--
|
|
19
|
+
* This file is part of Plum.
|
|
20
|
+
*
|
|
21
|
+
* Plum is free software: you can redistribute it and/or modify
|
|
22
|
+
* it under the terms of the GNU General Public License as published by
|
|
23
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
24
|
+
* (at your option) any later version.
|
|
25
|
+
*
|
|
26
|
+
* Plum is distributed in the hope that it will be useful,
|
|
27
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
28
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
29
|
+
* GNU General Public License for more details.
|
|
30
|
+
*
|
|
31
|
+
* You should have received a copy of the GNU General Public License
|
|
32
|
+
* along with Plum. If not, see https://www.gnu.org/licenses/.
|
|
33
|
+
-->
|
|
34
|
+
|
|
35
|
+
<script>
|
|
36
|
+
export let status = 'pending';
|
|
37
|
+
|
|
38
|
+
$: v =
|
|
39
|
+
status === 'pass'
|
|
40
|
+
? 'pass'
|
|
41
|
+
: status === 'fail'
|
|
42
|
+
? 'fail'
|
|
43
|
+
: status === 'blocked'
|
|
44
|
+
? 'warn'
|
|
45
|
+
: status === 'skip'
|
|
46
|
+
? 'muted'
|
|
47
|
+
: 'pending';
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<span
|
|
51
|
+
class="result-chip"
|
|
52
|
+
class:pass={v === 'pass'}
|
|
53
|
+
class:fail={v === 'fail'}
|
|
54
|
+
class:warn={v === 'warn'}
|
|
55
|
+
class:muted={v === 'muted'}
|
|
56
|
+
class:pending={v === 'pending'}
|
|
57
|
+
>
|
|
58
|
+
{status}
|
|
59
|
+
</span>
|
|
60
|
+
|
|
61
|
+
<style>
|
|
62
|
+
.result-chip {
|
|
63
|
+
font-size: 0.7rem;
|
|
64
|
+
font-weight: 600;
|
|
65
|
+
text-transform: uppercase;
|
|
66
|
+
letter-spacing: 0.05em;
|
|
67
|
+
padding: 0.2rem 0.6rem;
|
|
68
|
+
border-radius: var(--radius-pill);
|
|
69
|
+
white-space: nowrap;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.pass {
|
|
73
|
+
background: var(--pass-soft);
|
|
74
|
+
color: var(--pass);
|
|
75
|
+
}
|
|
76
|
+
.fail {
|
|
77
|
+
background: var(--fail-soft);
|
|
78
|
+
color: var(--fail);
|
|
79
|
+
}
|
|
80
|
+
.warn {
|
|
81
|
+
background: var(--warn-soft);
|
|
82
|
+
color: var(--warn);
|
|
83
|
+
}
|
|
84
|
+
.muted {
|
|
85
|
+
background: var(--bg-subtle);
|
|
86
|
+
color: var(--text-muted);
|
|
87
|
+
}
|
|
88
|
+
.pending {
|
|
89
|
+
background: var(--bg-subtle);
|
|
90
|
+
color: var(--text-muted);
|
|
91
|
+
}
|
|
92
|
+
</style>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
*
|
|
4
|
+
* Plum is free software: you can redistribute it and/or modify
|
|
5
|
+
* it under the terms of the GNU General Public License as published by
|
|
6
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* Plum is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU General Public License for more details.
|
|
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
|
+
-->
|
|
17
|
+
|
|
18
|
+
<!--
|
|
19
|
+
* This file is part of Plum.
|
|
20
|
+
*
|
|
21
|
+
* Plum is free software: you can redistribute it and/or modify
|
|
22
|
+
* it under the terms of the GNU General Public License as published by
|
|
23
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
24
|
+
* (at your option) any later version.
|
|
25
|
+
*
|
|
26
|
+
* Plum is distributed in the hope that it will be useful,
|
|
27
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
28
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
29
|
+
* GNU General Public License for more details.
|
|
30
|
+
*
|
|
31
|
+
* You should have received a copy of the GNU General Public License
|
|
32
|
+
* along with Plum. If not, see https://www.gnu.org/licenses/.
|
|
33
|
+
-->
|
|
34
|
+
|
|
35
|
+
<script>
|
|
36
|
+
export let status = 'passed';
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<span
|
|
40
|
+
class="status-dot"
|
|
41
|
+
class:pass={status === 'passed'}
|
|
42
|
+
class:fail={status === 'failed'}
|
|
43
|
+
class:skip={status !== 'passed' && status !== 'failed'}
|
|
44
|
+
></span>
|
|
45
|
+
|
|
46
|
+
<style>
|
|
47
|
+
.status-dot {
|
|
48
|
+
display: inline-block;
|
|
49
|
+
width: 8px;
|
|
50
|
+
height: 8px;
|
|
51
|
+
border-radius: 50%;
|
|
52
|
+
flex-shrink: 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.pass {
|
|
56
|
+
background: var(--pass);
|
|
57
|
+
}
|
|
58
|
+
.fail {
|
|
59
|
+
background: var(--fail);
|
|
60
|
+
}
|
|
61
|
+
.skip {
|
|
62
|
+
background: var(--text-muted);
|
|
63
|
+
}
|
|
64
|
+
</style>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
*
|
|
4
|
+
* Plum is free software: you can redistribute it and/or modify
|
|
5
|
+
* it under the terms of the GNU General Public License as published by
|
|
6
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* Plum is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU General Public License for more details.
|
|
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
|
+
-->
|
|
17
|
+
|
|
18
|
+
<!--
|
|
19
|
+
* This file is part of Plum.
|
|
20
|
+
*
|
|
21
|
+
* Plum is free software: you can redistribute it and/or modify
|
|
22
|
+
* it under the terms of the GNU General Public License as published by
|
|
23
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
24
|
+
* (at your option) any later version.
|
|
25
|
+
*
|
|
26
|
+
* Plum is distributed in the hope that it will be useful,
|
|
27
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
28
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
29
|
+
* GNU General Public License for more details.
|
|
30
|
+
*
|
|
31
|
+
* You should have received a copy of the GNU General Public License
|
|
32
|
+
* along with Plum. If not, see https://www.gnu.org/licenses/.
|
|
33
|
+
-->
|
|
34
|
+
|
|
35
|
+
<script>
|
|
36
|
+
import { keywordClass } from '$lib/utils/format';
|
|
37
|
+
|
|
38
|
+
export let keyword = '';
|
|
39
|
+
|
|
40
|
+
$: klass = keywordClass(keyword);
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<span
|
|
44
|
+
class="kw"
|
|
45
|
+
class:kw-given={klass === 'kw-given'}
|
|
46
|
+
class:kw-when={klass === 'kw-when'}
|
|
47
|
+
class:kw-then={klass === 'kw-then'}
|
|
48
|
+
class:kw-and={klass === 'kw-and'}>{keyword}</span
|
|
49
|
+
>
|
|
50
|
+
|
|
51
|
+
<style>
|
|
52
|
+
.kw {
|
|
53
|
+
font-size: 0.72rem;
|
|
54
|
+
font-weight: 500;
|
|
55
|
+
letter-spacing: 0.04em;
|
|
56
|
+
flex-shrink: 0;
|
|
57
|
+
padding: 0.05rem 0.4rem;
|
|
58
|
+
border-radius: 3px;
|
|
59
|
+
font-family: 'JetBrains Mono', monospace;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.kw-given {
|
|
63
|
+
background: var(--accent-soft);
|
|
64
|
+
color: var(--accent);
|
|
65
|
+
}
|
|
66
|
+
.kw-when {
|
|
67
|
+
background: var(--warn-soft);
|
|
68
|
+
color: var(--warn);
|
|
69
|
+
}
|
|
70
|
+
.kw-then {
|
|
71
|
+
background: var(--pass-soft);
|
|
72
|
+
color: var(--pass);
|
|
73
|
+
}
|
|
74
|
+
.kw-and {
|
|
75
|
+
background: var(--bg-elevated);
|
|
76
|
+
color: var(--text-muted);
|
|
77
|
+
border: 1px solid var(--border);
|
|
78
|
+
}
|
|
79
|
+
</style>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
*
|
|
4
|
+
* Plum is free software: you can redistribute it and/or modify
|
|
5
|
+
* it under the terms of the GNU General Public License as published by
|
|
6
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* Plum is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU General Public License for more details.
|
|
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
|
+
-->
|
|
17
|
+
|
|
18
|
+
<!--
|
|
19
|
+
* This file is part of Plum.
|
|
20
|
+
*
|
|
21
|
+
* Plum is free software: you can redistribute it and/or modify
|
|
22
|
+
* it under the terms of the GNU General Public License as published by
|
|
23
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
24
|
+
* (at your option) any later version.
|
|
25
|
+
*
|
|
26
|
+
* Plum is distributed in the hope that it will be useful,
|
|
27
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
28
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
29
|
+
* GNU General Public License for more details.
|
|
30
|
+
*
|
|
31
|
+
* You should have received a copy of the GNU General Public License
|
|
32
|
+
* along with Plum. If not, see https://www.gnu.org/licenses/.
|
|
33
|
+
-->
|
|
34
|
+
|
|
35
|
+
<script>
|
|
36
|
+
export let status = 'passed';
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<span
|
|
40
|
+
class="step-status-icon"
|
|
41
|
+
class:icon-pass={status === 'passed'}
|
|
42
|
+
class:icon-fail={status === 'failed'}
|
|
43
|
+
class:icon-skip={status !== 'passed' && status !== 'failed'}
|
|
44
|
+
>
|
|
45
|
+
{#if status === 'passed'}✓{:else if status === 'failed'}✗{:else}−{/if}
|
|
46
|
+
</span>
|
|
47
|
+
|
|
48
|
+
<style>
|
|
49
|
+
.step-status-icon {
|
|
50
|
+
font-size: 0.75rem;
|
|
51
|
+
font-weight: 600;
|
|
52
|
+
width: 14px;
|
|
53
|
+
flex-shrink: 0;
|
|
54
|
+
text-align: center;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.icon-pass {
|
|
58
|
+
color: var(--pass);
|
|
59
|
+
}
|
|
60
|
+
.icon-fail {
|
|
61
|
+
color: var(--fail);
|
|
62
|
+
}
|
|
63
|
+
.icon-skip {
|
|
64
|
+
color: var(--text-muted);
|
|
65
|
+
}
|
|
66
|
+
</style>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
*
|
|
4
|
+
* Plum is free software: you can redistribute it and/or modify
|
|
5
|
+
* it under the terms of the GNU General Public License as published by
|
|
6
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* Plum is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU General Public License for more details.
|
|
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
|
+
-->
|
|
17
|
+
|
|
18
|
+
<!--
|
|
19
|
+
* This file is part of Plum.
|
|
20
|
+
*
|
|
21
|
+
* Plum is free software: you can redistribute it and/or modify
|
|
22
|
+
* it under the terms of the GNU General Public License as published by
|
|
23
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
24
|
+
* (at your option) any later version.
|
|
25
|
+
*
|
|
26
|
+
* Plum is distributed in the hope that it will be useful,
|
|
27
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
28
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
29
|
+
* GNU General Public License for more details.
|
|
30
|
+
*
|
|
31
|
+
* You should have received a copy of the GNU General Public License
|
|
32
|
+
* along with Plum. If not, see https://www.gnu.org/licenses/.
|
|
33
|
+
-->
|
|
34
|
+
|
|
35
|
+
<script>
|
|
36
|
+
export let tag = '';
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<span class="tag-chip">{tag}</span>
|
|
40
|
+
|
|
41
|
+
<style>
|
|
42
|
+
.tag-chip {
|
|
43
|
+
font-size: 0.65rem;
|
|
44
|
+
font-family: 'JetBrains Mono', monospace;
|
|
45
|
+
background: var(--accent-soft);
|
|
46
|
+
color: var(--accent);
|
|
47
|
+
padding: 0.1rem 0.4rem;
|
|
48
|
+
border-radius: var(--radius-pill);
|
|
49
|
+
white-space: nowrap;
|
|
50
|
+
}
|
|
51
|
+
</style>
|
|
@@ -34,3 +34,17 @@ export const SUITE_CASES_PER_PAGE = 20;
|
|
|
34
34
|
|
|
35
35
|
export const COPY_TIMEOUT_MS = 1400;
|
|
36
36
|
export const TOAST_TIMEOUT_MS = 4000;
|
|
37
|
+
|
|
38
|
+
export const REPLAY_STEP_MS = 900;
|
|
39
|
+
export const REDIRECT_DELAY_MS = 3000;
|
|
40
|
+
export const RUN_REFRESH_MS = 15000;
|
|
41
|
+
|
|
42
|
+
export const WORKERS_MIN = 1;
|
|
43
|
+
export const WORKERS_MAX = 10;
|
|
44
|
+
export const RUN_PICKER_LIMIT = 200;
|
|
45
|
+
export const RUN_TAG_DISPLAY_LIMIT = 5;
|
|
46
|
+
|
|
47
|
+
export const BUILTIN_RUNNER_ID = 'built-in';
|
|
48
|
+
export const BUILTIN_RUNNER_LABEL = 'Built-in';
|
|
49
|
+
export const ALL_TESTS_LABEL = 'all tests';
|
|
50
|
+
export const AUTOMATED_LABEL = 'Automated';
|
|
@@ -26,8 +26,9 @@ export const runnerState = writable({
|
|
|
26
26
|
latestReportId: null, // number | null — set after test finishes
|
|
27
27
|
status: 'idle', // 'idle' | 'running' | 'pass' | 'fail'
|
|
28
28
|
lastRunId: '',
|
|
29
|
-
lanes: [], // [{ id, name, testCount, status, logs }] multi-runner only
|
|
30
|
-
currentRun: null // { tag, workers, browser, runners } — set while running
|
|
29
|
+
lanes: [], // [{ id, name, testCount, status, logs, latestScreenshot }] multi-runner only
|
|
30
|
+
currentRun: null, // { tag, workers, browser, runners } — set while running
|
|
31
|
+
latestScreenshot: null // { stepName, data: base64 } for single built-in runner
|
|
31
32
|
});
|
|
32
33
|
|
|
33
34
|
export const runnerConfig = writable({
|
|
@@ -63,7 +64,8 @@ export function triggerRun(id, testRunId, notify = {}, runTitle = null) {
|
|
|
63
64
|
status: 'running',
|
|
64
65
|
lastRunId: runId,
|
|
65
66
|
lanes: [],
|
|
66
|
-
currentRun: { tag: runId, workers, browser, runners: selectedRunners, runTitle }
|
|
67
|
+
currentRun: { tag: runId, workers, browser, runners: selectedRunners, runTitle },
|
|
68
|
+
latestScreenshot: null
|
|
67
69
|
});
|
|
68
70
|
panelExpanded.set(true);
|
|
69
71
|
|
|
@@ -48,6 +48,32 @@
|
|
|
48
48
|
--radius-sm: 6px;
|
|
49
49
|
--radius-md: 10px;
|
|
50
50
|
--radius-lg: 16px;
|
|
51
|
+
--radius-pill: 100px;
|
|
52
|
+
|
|
53
|
+
--white: #ffffff;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@keyframes fadeUp {
|
|
57
|
+
from {
|
|
58
|
+
opacity: 0;
|
|
59
|
+
transform: translateY(6px);
|
|
60
|
+
}
|
|
61
|
+
to {
|
|
62
|
+
opacity: 1;
|
|
63
|
+
transform: translateY(0);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@keyframes dotPulse {
|
|
68
|
+
0%,
|
|
69
|
+
100% {
|
|
70
|
+
opacity: 1;
|
|
71
|
+
transform: scale(1);
|
|
72
|
+
}
|
|
73
|
+
50% {
|
|
74
|
+
opacity: 0.4;
|
|
75
|
+
transform: scale(0.65);
|
|
76
|
+
}
|
|
51
77
|
}
|
|
52
78
|
|
|
53
79
|
/* ── Design tokens (dark) ──────────────────────────────────────────────── */
|
|
@@ -64,3 +64,77 @@ export function featureFile(uri) {
|
|
|
64
64
|
if (idx !== -1) return normalized.slice(idx + 1);
|
|
65
65
|
return normalized.split('/').pop();
|
|
66
66
|
}
|
|
67
|
+
|
|
68
|
+
export function keywordClass(kw) {
|
|
69
|
+
const k = (kw ?? '').toLowerCase().replace(/:$/, '');
|
|
70
|
+
if (k === 'given') return 'kw-given';
|
|
71
|
+
if (k === 'when') return 'kw-when';
|
|
72
|
+
if (k === 'then') return 'kw-then';
|
|
73
|
+
return 'kw-and';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function scenarioTestTag(scenario) {
|
|
77
|
+
return scenario.tags?.find((tag) => /^@test[\w-]*/i.test(tag)) ?? null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function featureSuiteTag(feature) {
|
|
81
|
+
for (const scenario of feature.scenarios ?? []) {
|
|
82
|
+
const suiteTag = scenario.tags?.find((tag) => /suite/i.test(tag));
|
|
83
|
+
if (suiteTag) return suiteTag;
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function visibleTags(scenario) {
|
|
89
|
+
const testTag = scenarioTestTag(scenario);
|
|
90
|
+
if (testTag) return [testTag];
|
|
91
|
+
return (scenario.tags ?? []).filter((tag) => !/suite/i.test(tag));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const STATUS_RANK = { failed: 3, pending: 2, skipped: 1, passed: 0 };
|
|
95
|
+
|
|
96
|
+
export function worstStatus(scenarios) {
|
|
97
|
+
return scenarios.reduce(
|
|
98
|
+
(s, scenario) =>
|
|
99
|
+
(STATUS_RANK[scenario.status] ?? 0) > (STATUS_RANK[s] ?? 0) ? scenario.status : s,
|
|
100
|
+
'passed'
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function groupedScenarios(scenarios) {
|
|
105
|
+
const groups = new Map();
|
|
106
|
+
for (const scenario of scenarios) {
|
|
107
|
+
const testTag = scenarioTestTag(scenario);
|
|
108
|
+
const key = testTag ?? `${scenario.keyword}:${scenario.name}`;
|
|
109
|
+
if (!groups.has(key)) {
|
|
110
|
+
groups.set(key, {
|
|
111
|
+
key,
|
|
112
|
+
name: scenario.name,
|
|
113
|
+
tags: visibleTags(scenario),
|
|
114
|
+
scenarios: [],
|
|
115
|
+
duration: 0,
|
|
116
|
+
status: scenario.status
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const group = groups.get(key);
|
|
120
|
+
group.scenarios.push(scenario);
|
|
121
|
+
group.duration += scenario.duration ?? 0;
|
|
122
|
+
group.status = worstStatus(group.scenarios);
|
|
123
|
+
}
|
|
124
|
+
return Array.from(groups.values());
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function parseRunnerLogs(logs) {
|
|
128
|
+
if (!logs) return [];
|
|
129
|
+
if (!/^=== .+ ===/m.test(logs)) return [{ name: 'Logs', content: logs }];
|
|
130
|
+
const sections = [];
|
|
131
|
+
for (const chunk of logs.split(/\n\n(?==== )/)) {
|
|
132
|
+
const m = chunk.match(/^=== (.+?) ===\n?([\s\S]*)/);
|
|
133
|
+
if (m) sections.push({ name: m[1].trim(), content: m[2].trim() });
|
|
134
|
+
}
|
|
135
|
+
return sections;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function scenarioHasScreenshots(scenario) {
|
|
139
|
+
return scenario.steps?.some((step) => step.screenshot) ?? false;
|
|
140
|
+
}
|
|
@@ -516,7 +516,7 @@
|
|
|
516
516
|
|
|
517
517
|
.run-icon-btn:hover {
|
|
518
518
|
background: var(--accent);
|
|
519
|
-
color:
|
|
519
|
+
color: var(--white);
|
|
520
520
|
border-color: var(--accent);
|
|
521
521
|
}
|
|
522
522
|
|
|
@@ -534,7 +534,7 @@
|
|
|
534
534
|
color: var(--accent);
|
|
535
535
|
background: var(--accent-soft);
|
|
536
536
|
border: none;
|
|
537
|
-
border-radius:
|
|
537
|
+
border-radius: var(--radius-pill);
|
|
538
538
|
padding: 0.15rem 0.55rem;
|
|
539
539
|
cursor: pointer;
|
|
540
540
|
font-family: 'JetBrains Mono', monospace;
|
|
@@ -589,7 +589,7 @@
|
|
|
589
589
|
background: var(--warn-soft);
|
|
590
590
|
color: var(--warn);
|
|
591
591
|
padding: 0.1rem 0.4rem;
|
|
592
|
-
border-radius:
|
|
592
|
+
border-radius: var(--radius-pill);
|
|
593
593
|
margin-left: 0.4rem;
|
|
594
594
|
vertical-align: middle;
|
|
595
595
|
}
|