@shipfox/client-workflows 12.0.2 → 13.0.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/.storybook/preview.tsx +3 -3
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +56 -0
- package/dist/components/job-graph/job-duration-format.d.ts +2 -2
- package/dist/components/job-graph/job-duration-format.d.ts.map +1 -1
- package/dist/components/job-graph/job-duration-format.js +4 -2
- package/dist/components/job-graph/job-duration-format.js.map +1 -1
- package/dist/components/job-graph/job-node.d.ts +1 -1
- package/dist/components/job-graph/job-node.d.ts.map +1 -1
- package/dist/components/job-graph/job-node.js +5 -4
- package/dist/components/job-graph/job-node.js.map +1 -1
- package/dist/components/step-list/step-list.d.ts +2 -2
- package/dist/components/step-list/step-list.d.ts.map +1 -1
- package/dist/components/step-list/step-list.js.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.d.ts +21 -0
- package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -0
- package/dist/components/workflow-run-list/job-status-strip.js +185 -0
- package/dist/components/workflow-run-list/job-status-strip.js.map +1 -0
- package/dist/components/workflow-run-list/run-display.d.ts +27 -3
- package/dist/components/workflow-run-list/run-display.d.ts.map +1 -1
- package/dist/components/workflow-run-list/run-display.js +78 -7
- package/dist/components/workflow-run-list/run-display.js.map +1 -1
- package/dist/components/workflow-run-list/types.d.ts +23 -24
- package/dist/components/workflow-run-list/types.d.ts.map +1 -1
- package/dist/components/workflow-run-list/types.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filter-menu.d.ts +22 -0
- package/dist/components/workflow-run-list/workflow-run-filter-menu.d.ts.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js +68 -0
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-filters.d.ts +18 -0
- package/dist/components/workflow-run-list/workflow-run-filters.d.ts.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-filters.js +236 -0
- package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-list-content.d.ts +8 -4
- package/dist/components/workflow-run-list/workflow-run-list-content.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-content.js +40 -12
- package/dist/components/workflow-run-list/workflow-run-list-content.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.d.ts +29 -2
- package/dist/components/workflow-run-list/workflow-run-list-states.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.js +103 -44
- package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.d.ts +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js +60 -43
- package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.d.ts +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.js +20 -8
- package/dist/components/workflow-run-list/workflow-run-list.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.d.ts +19 -9
- package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.js +179 -112
- package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
- package/dist/components/workflow-run-number-label.d.ts +15 -0
- package/dist/components/workflow-run-number-label.d.ts.map +1 -0
- package/dist/components/workflow-run-number-label.js +41 -0
- package/dist/components/workflow-run-number-label.js.map +1 -0
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts +3 -3
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +8 -7
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +3 -3
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +16 -7
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts +2 -2
- package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts.map +1 -1
- package/dist/components/workflow-run-view/agent-config-failure-callout.js +4 -4
- package/dist/components/workflow-run-view/agent-config-failure-callout.js.map +1 -1
- package/dist/components/workflow-run-view/job-card.d.ts +2 -2
- package/dist/components/workflow-run-view/job-card.d.ts.map +1 -1
- package/dist/components/workflow-run-view/job-card.js +51 -32
- package/dist/components/workflow-run-view/job-card.js.map +1 -1
- package/dist/components/workflow-run-view/job-execution-switcher.d.ts +1 -1
- package/dist/components/workflow-run-view/job-execution-switcher.d.ts.map +1 -1
- package/dist/components/workflow-run-view/job-execution-switcher.js +20 -8
- package/dist/components/workflow-run-view/job-execution-switcher.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.d.ts +3 -2
- package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +15 -11
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/components/workflow-status/status-visuals.d.ts +4 -4
- package/dist/components/workflow-status/status-visuals.d.ts.map +1 -1
- package/dist/components/workflow-status/status-visuals.js +7 -0
- package/dist/components/workflow-status/status-visuals.js.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.d.ts +3 -4
- package/dist/components/workflow-status/workflow-status-icon.d.ts.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.js +6 -4
- package/dist/components/workflow-status/workflow-status-icon.js.map +1 -1
- package/dist/core/entities/job-execution.d.ts +10 -0
- package/dist/core/entities/job-execution.d.ts.map +1 -1
- package/dist/core/entities/job-execution.js +11 -0
- package/dist/core/entities/job-execution.js.map +1 -1
- package/dist/core/entities/job.d.ts +4 -2
- package/dist/core/entities/job.d.ts.map +1 -1
- package/dist/core/entities/job.js +8 -1
- package/dist/core/entities/job.js.map +1 -1
- package/dist/core/entities/workflow-run.d.ts +52 -4
- package/dist/core/entities/workflow-run.d.ts.map +1 -1
- package/dist/core/entities/workflow-run.js +22 -3
- package/dist/core/entities/workflow-run.js.map +1 -1
- package/dist/core/workflow-run.d.ts +6 -6
- package/dist/core/workflow-run.d.ts.map +1 -1
- package/dist/core/workflow-run.js +3 -3
- package/dist/core/workflow-run.js.map +1 -1
- package/dist/feature.d.ts +7 -7
- package/dist/feature.js +5 -5
- package/dist/feature.js.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.d.ts +4 -3
- package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.js +31 -3
- package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
- package/dist/hooks/api/workflow-runs.d.ts +4 -4
- package/dist/hooks/api/workflow-runs.d.ts.map +1 -1
- package/dist/hooks/api/workflow-runs.js +12 -4
- package/dist/hooks/api/workflow-runs.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/pages/project-workflows-page.js +46 -1
- package/dist/pages/project-workflows-page.js.map +1 -1
- package/dist/pages/workflow-run-detail-page.d.ts +11 -0
- package/dist/pages/workflow-run-detail-page.d.ts.map +1 -0
- package/dist/pages/workflow-run-detail-page.js +30 -0
- package/dist/pages/workflow-run-detail-page.js.map +1 -0
- package/dist/pages/workflow-run-list-page.d.ts +10 -0
- package/dist/pages/workflow-run-list-page.d.ts.map +1 -0
- package/dist/pages/workflow-run-list-page.js +43 -0
- package/dist/pages/workflow-run-list-page.js.map +1 -0
- package/dist/routes/inputs.d.ts +61 -5
- package/dist/routes/inputs.d.ts.map +1 -1
- package/dist/routes/inputs.js +150 -13
- package/dist/routes/inputs.js.map +1 -1
- package/dist/routes/project-route.d.ts +6 -0
- package/dist/routes/project-route.d.ts.map +1 -0
- package/dist/routes/project-route.js +25 -0
- package/dist/routes/project-route.js.map +1 -0
- package/dist/routes/run-detail.d.ts.map +1 -1
- package/dist/routes/run-detail.js +12 -7
- package/dist/routes/run-detail.js.map +1 -1
- package/dist/routes/runs.d.ts.map +1 -1
- package/dist/routes/runs.js +11 -6
- package/dist/routes/runs.js.map +1 -1
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/routes/workflows.js +6 -5
- package/dist/routes/workflows.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/components/job-graph/job-duration-format.ts +11 -2
- package/src/components/job-graph/job-graph-view.test.tsx +19 -8
- package/src/components/job-graph/job-graph.stories.tsx +23 -19
- package/src/components/job-graph/job-node.stories.tsx +10 -5
- package/src/components/job-graph/job-node.test.tsx +55 -10
- package/src/components/job-graph/job-node.tsx +16 -9
- package/src/components/step-list/step-list.stories.tsx +2 -3
- package/src/components/step-list/step-list.tsx +3 -2
- package/src/components/workflow-run-list/job-status-strip.tsx +189 -0
- package/src/components/workflow-run-list/run-display.test.ts +169 -30
- package/src/components/workflow-run-list/run-display.ts +116 -9
- package/src/components/workflow-run-list/types.ts +21 -17
- package/src/components/workflow-run-list/workflow-run-filter-menu.tsx +100 -0
- package/src/components/workflow-run-list/workflow-run-filters.tsx +243 -0
- package/src/components/workflow-run-list/workflow-run-list-content.tsx +44 -16
- package/src/components/workflow-run-list/workflow-run-list-states.tsx +128 -27
- package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +441 -104
- package/src/components/workflow-run-list/workflow-run-list-view.tsx +73 -43
- package/src/components/workflow-run-list/workflow-run-list.stories.tsx +140 -20
- package/src/components/workflow-run-list/workflow-run-list.tsx +20 -10
- package/src/components/workflow-run-list/workflow-run-row.tsx +173 -106
- package/src/components/workflow-run-number-label.test.tsx +45 -0
- package/src/components/workflow-run-number-label.tsx +47 -0
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.test.tsx +7 -8
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +13 -12
- package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +6 -8
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +27 -2
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +19 -9
- package/src/components/workflow-run-view/agent-config-failure-callout.stories.tsx +2 -3
- package/src/components/workflow-run-view/agent-config-failure-callout.tsx +4 -4
- package/src/components/workflow-run-view/job-card.stories.tsx +11 -10
- package/src/components/workflow-run-view/job-card.test.tsx +115 -0
- package/src/components/workflow-run-view/job-card.tsx +74 -35
- package/src/components/workflow-run-view/job-execution-switcher.tsx +31 -9
- package/src/components/workflow-run-view/workflow-run-view.test.tsx +88 -30
- package/src/components/workflow-run-view/workflow-run-view.tsx +24 -14
- package/src/components/workflow-status/status-visuals.test.ts +9 -0
- package/src/components/workflow-status/status-visuals.ts +6 -4
- package/src/components/workflow-status/workflow-status-icon.stories.tsx +10 -2
- package/src/components/workflow-status/workflow-status-icon.test.tsx +18 -0
- package/src/components/workflow-status/workflow-status-icon.tsx +17 -24
- package/src/core/entities/job-execution.ts +22 -0
- package/src/core/entities/job-status.test.ts +104 -0
- package/src/core/entities/job.ts +18 -3
- package/src/core/entities/workflow-run.ts +76 -7
- package/src/core/workflow-run.test.ts +24 -9
- package/src/core/workflow-run.ts +17 -2
- package/src/feature.ts +5 -5
- package/src/hooks/api/workflow-run-mapper.ts +30 -3
- package/src/hooks/api/workflow-runs.test.tsx +2 -0
- package/src/hooks/api/workflow-runs.ts +12 -7
- package/src/index.ts +7 -2
- package/src/page-harness-budget.test.ts +1 -1
- package/src/pages/project-workflows-page.test.tsx +59 -5
- package/src/pages/project-workflows-page.tsx +34 -1
- package/src/pages/workflow-run-detail-page.tsx +45 -0
- package/src/pages/workflow-run-list-page.tsx +65 -0
- package/src/pages/{workflow-run-page.test.tsx → workflow-run-pages.test.tsx} +211 -47
- package/src/routes/inputs.test.ts +193 -12
- package/src/routes/inputs.ts +170 -11
- package/src/routes/project-route.tsx +25 -0
- package/src/routes/run-detail.tsx +15 -8
- package/src/routes/runs.tsx +14 -7
- package/src/routes/workflows.tsx +5 -4
- package/test/fixtures/workflow-run.ts +76 -7
- package/test/pages.tsx +18 -8
- package/test/render.tsx +8 -2
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-header.d.ts +0 -10
- package/dist/components/workflow-run-list/workflow-run-list-header.d.ts.map +0 -1
- package/dist/components/workflow-run-list/workflow-run-list-header.js +0 -55
- package/dist/components/workflow-run-list/workflow-run-list-header.js.map +0 -1
- package/dist/pages/workflow-run-first-time-use.d.ts +0 -6
- package/dist/pages/workflow-run-first-time-use.d.ts.map +0 -1
- package/dist/pages/workflow-run-first-time-use.js +0 -17
- package/dist/pages/workflow-run-first-time-use.js.map +0 -1
- package/dist/pages/workflow-run-page.d.ts +0 -10
- package/dist/pages/workflow-run-page.d.ts.map +0 -1
- package/dist/pages/workflow-run-page.js +0 -90
- package/dist/pages/workflow-run-page.js.map +0 -1
- package/src/components/workflow-run-list/workflow-run-list-header.tsx +0 -53
- package/src/pages/workflow-run-first-time-use.tsx +0 -17
- package/src/pages/workflow-run-page.tsx +0 -94
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {TriggerSourceIcon} from '@shipfox/client-triggers';
|
|
2
|
+
import {Icon, type IconName} from '@shipfox/react-ui/icon';
|
|
2
3
|
import {RelativeTime} from '@shipfox/react-ui/relative-time';
|
|
3
4
|
import {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';
|
|
4
5
|
import {Code, Text} from '@shipfox/react-ui/typography';
|
|
@@ -8,169 +9,235 @@ import {
|
|
|
8
9
|
useWorkflowRunDurationAccessibleLabel,
|
|
9
10
|
WorkflowRunDurationLabel,
|
|
10
11
|
} from '#components/workflow-run-duration-label.js';
|
|
12
|
+
import {
|
|
13
|
+
formatWorkflowRunNumberLabel,
|
|
14
|
+
WorkflowRunNumberLabel,
|
|
15
|
+
} from '#components/workflow-run-number-label.js';
|
|
11
16
|
import {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';
|
|
12
17
|
import {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';
|
|
13
|
-
import
|
|
18
|
+
import {
|
|
19
|
+
type WorkflowRunListItem,
|
|
20
|
+
workflowRunActor,
|
|
21
|
+
workflowRunBranchLabel,
|
|
22
|
+
workflowRunCommitLabel,
|
|
23
|
+
} from '#core/workflow-run.js';
|
|
14
24
|
import {withoutWorkflowRunSelectionSearch} from '#core/workflow-run-url-state.js';
|
|
25
|
+
import {JobStatusStrip, jobStatusSummary} from './job-status-strip.js';
|
|
15
26
|
|
|
16
27
|
export function WorkflowRunRowList({
|
|
17
28
|
runs,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
selectedWorkflowRunId,
|
|
29
|
+
workspaceSlug,
|
|
30
|
+
projectSlug,
|
|
21
31
|
}: {
|
|
22
32
|
runs: WorkflowRunListItem[];
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
selectedWorkflowRunId?: string | undefined;
|
|
33
|
+
workspaceSlug?: string | undefined;
|
|
34
|
+
projectSlug?: string | undefined;
|
|
26
35
|
}) {
|
|
27
36
|
return (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
</li>
|
|
39
|
-
))}
|
|
40
|
-
</ul>
|
|
41
|
-
</nav>
|
|
37
|
+
// A container, not the viewport, drives the row's breakpoints. The list is capped at
|
|
38
|
+
// 1120px inside a wider page, so what a row can afford is its own width; keying off the
|
|
39
|
+
// viewport would keep the job strip hidden on a wide screen or crush it on a narrow one.
|
|
40
|
+
<ul className="@container divide-y divide-border-neutral-base border-t border-border-neutral-base">
|
|
41
|
+
{runs.map((run) => (
|
|
42
|
+
<li key={run.id}>
|
|
43
|
+
<WorkflowRunRow run={run} workspaceSlug={workspaceSlug} projectSlug={projectSlug} />
|
|
44
|
+
</li>
|
|
45
|
+
))}
|
|
46
|
+
</ul>
|
|
42
47
|
);
|
|
43
48
|
}
|
|
44
49
|
|
|
50
|
+
/**
|
|
51
|
+
* One run at full width.
|
|
52
|
+
*
|
|
53
|
+
* A single 44px line once the list is at least 976px wide, reflowing to two lines below that:
|
|
54
|
+
* the identity and the numerics keep their places on line one, and the provenance metadata
|
|
55
|
+
* drops beneath. The numeric columns are fixed-width so duration and time form real columns
|
|
56
|
+
* down the list rather than tracking each row's name length.
|
|
57
|
+
*
|
|
58
|
+
* The thresholds are the container widths the spec's viewport breakpoints produce, given a
|
|
59
|
+
* column capped at 1120px with 24px of page padding on each side: a 1280px viewport leaves
|
|
60
|
+
* the row 1072px, and a 1024px viewport leaves it 976px.
|
|
61
|
+
*/
|
|
45
62
|
export function WorkflowRunRow({
|
|
46
63
|
run,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
selected,
|
|
64
|
+
workspaceSlug,
|
|
65
|
+
projectSlug,
|
|
50
66
|
}: {
|
|
51
67
|
run: WorkflowRunListItem;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
selected: boolean;
|
|
68
|
+
workspaceSlug?: string | undefined;
|
|
69
|
+
projectSlug?: string | undefined;
|
|
55
70
|
}) {
|
|
56
71
|
const durationLabel = useWorkflowRunDurationAccessibleLabel(run.runAttempt.displayDuration);
|
|
57
72
|
const statusLabel = getWorkflowStatusVisual(run.status).label;
|
|
73
|
+
const runNumberLabel = formatWorkflowRunNumberLabel(run);
|
|
74
|
+
const branch = workflowRunBranchLabel(run);
|
|
75
|
+
const commit = workflowRunCommitLabel(run);
|
|
76
|
+
const actor = workflowRunActor(run);
|
|
77
|
+
|
|
58
78
|
const body = (
|
|
59
79
|
<>
|
|
60
|
-
{
|
|
61
|
-
<span
|
|
62
|
-
aria-hidden="true"
|
|
63
|
-
className="absolute inset-y-8 left-0 w-3 rounded-full bg-border-highlights-interactive"
|
|
64
|
-
/>
|
|
65
|
-
) : null}
|
|
66
|
-
|
|
67
|
-
<div className="flex min-w-0 items-center gap-8">
|
|
68
|
-
<WorkflowStatusIcon status={run.status} size={14} />
|
|
69
|
-
<Code variant="label" bold className="truncate text-foreground-neutral-base">
|
|
70
|
-
{run.name}
|
|
71
|
-
</Code>
|
|
72
|
-
</div>
|
|
80
|
+
<WorkflowStatusIcon status={run.status} size={14} className="shrink-0" />
|
|
73
81
|
|
|
74
|
-
<div className="flex min-w-0 items-center gap-
|
|
75
|
-
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
-
<span className="flex min-w-0 flex-1 items-center gap-8 truncate text-foreground-neutral-muted">
|
|
79
|
-
<span aria-hidden="true" className="size-14 shrink-0" />
|
|
80
|
-
<span className="sr-only">Run updated </span>
|
|
81
|
-
</span>
|
|
82
|
-
)}
|
|
83
|
-
<span className="ml-auto flex shrink-0 items-center gap-6">
|
|
84
|
-
<WorkflowRunDurationLabel
|
|
85
|
-
duration={run.runAttempt.displayDuration}
|
|
86
|
-
className="text-foreground-neutral-disabled"
|
|
87
|
-
/>
|
|
88
|
-
{durationLabel ? <RunMetadataSeparator /> : null}
|
|
89
|
-
<Code variant="label" className="shrink-0 text-foreground-neutral-disabled">
|
|
90
|
-
<RelativeTime value={run.updatedAt} />
|
|
82
|
+
<div className="flex min-w-0 flex-1 flex-col gap-2 @min-[976px]:flex-row @min-[976px]:items-center @min-[976px]:gap-12">
|
|
83
|
+
<span className="flex min-w-0 items-center gap-8 @min-[976px]:flex-1">
|
|
84
|
+
<Code variant="label" bold className="truncate text-foreground-neutral-base">
|
|
85
|
+
{run.name}
|
|
91
86
|
</Code>
|
|
87
|
+
{run.number !== null ? <WorkflowRunNumberLabel run={run} /> : null}
|
|
88
|
+
</span>
|
|
89
|
+
|
|
90
|
+
<span className="flex min-w-0 flex-wrap items-center gap-10 text-foreground-neutral-subtle @min-[976px]:flex-nowrap @min-[976px]:shrink-0">
|
|
91
|
+
{run.triggerDisplayLabel ? <TriggerLabel run={run} /> : null}
|
|
92
|
+
{branch ? <BranchLabel branch={branch} isPullRequest={branch.startsWith('#')} /> : null}
|
|
93
|
+
{commit ? <CommitLabel commit={commit} /> : null}
|
|
94
|
+
{actor ? <ActorLabel actor={actor} /> : null}
|
|
92
95
|
</span>
|
|
93
96
|
</div>
|
|
97
|
+
|
|
98
|
+
<span className="flex shrink-0 items-center gap-12">
|
|
99
|
+
{/* Fixed at the strip's worst case (see MAX_VISIBLE_JOBS) so the overflow count cannot
|
|
100
|
+
paint over the duration, and kept even when a run has no jobs planned yet, so the
|
|
101
|
+
numerics stay in line down the list instead of stepping in and out. */}
|
|
102
|
+
<span className="hidden w-160 @min-[1040px]:flex">
|
|
103
|
+
<JobStatusStrip jobs={run.jobs} />
|
|
104
|
+
</span>
|
|
105
|
+
<span className="flex w-64 justify-end">
|
|
106
|
+
<WorkflowRunDurationLabel duration={run.runAttempt.displayDuration} />
|
|
107
|
+
</span>
|
|
108
|
+
<Code
|
|
109
|
+
variant="label"
|
|
110
|
+
className="w-64 shrink-0 text-right tabular-nums text-foreground-neutral-muted"
|
|
111
|
+
>
|
|
112
|
+
<RelativeTime value={run.createdAt} />
|
|
113
|
+
</Code>
|
|
114
|
+
</span>
|
|
94
115
|
</>
|
|
95
116
|
);
|
|
96
117
|
|
|
118
|
+
// Rows run edge to edge inside the list's scroll container, which would clip the standard
|
|
119
|
+
// outset focus ring, so this one is inset per the design system's focus-ring rule.
|
|
120
|
+
const rowClassName =
|
|
121
|
+
'flex w-full min-w-0 items-center gap-10 px-12 py-8 text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-[inset_0_0_0_2px_var(--color-primary-500)] focus-visible:outline-none @min-[976px]:h-44 @min-[976px]:py-0';
|
|
122
|
+
|
|
97
123
|
// Optimistic manual runs (temp-<uuid>) have no detail page until the canonical row
|
|
98
124
|
// replaces them on the next poll, so they render non-interactively instead of as a link
|
|
99
125
|
// that would navigate to a workflow run id the detail route rejects.
|
|
100
|
-
if (run.isTemporary) {
|
|
101
|
-
return
|
|
102
|
-
<div className="relative flex w-full flex-col gap-4 rounded-8 border border-transparent px-10 py-8 text-left">
|
|
103
|
-
{body}
|
|
104
|
-
</div>
|
|
105
|
-
);
|
|
126
|
+
if (run.isTemporary || !workspaceSlug || !projectSlug) {
|
|
127
|
+
return <div className={rowClassName}>{body}</div>;
|
|
106
128
|
}
|
|
107
129
|
|
|
108
|
-
|
|
130
|
+
return (
|
|
109
131
|
<Link
|
|
110
|
-
to="/
|
|
111
|
-
params={{
|
|
132
|
+
to="/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId"
|
|
133
|
+
params={{workspaceSlug, projectSlug, workflowRunId: run.id}}
|
|
112
134
|
search={
|
|
113
135
|
((previous: Record<string, unknown>) =>
|
|
114
136
|
withoutWorkflowRunSelectionSearch(previous)) as never
|
|
115
137
|
}
|
|
116
|
-
aria-
|
|
117
|
-
|
|
138
|
+
// An aria-label is the link's whole accessible name, so anything inside the row that
|
|
139
|
+
// carries its own label (the status glyph, the job strip) goes unspoken unless it is
|
|
140
|
+
// curated in here. The job breakdown is the reason this list is worth its width, so it
|
|
141
|
+
// belongs in what a tabbing user hears, not only in browse mode.
|
|
142
|
+
aria-label={[
|
|
143
|
+
run.name,
|
|
144
|
+
runNumberLabel,
|
|
145
|
+
statusLabel,
|
|
146
|
+
durationLabel,
|
|
147
|
+
run.triggerLabel,
|
|
148
|
+
branch ? `branch ${branch}` : undefined,
|
|
149
|
+
actor ? `by ${actor}` : undefined,
|
|
150
|
+
run.jobs.total > 0 ? jobStatusSummary(run.jobs) : undefined,
|
|
151
|
+
]
|
|
118
152
|
.filter((part): part is string => Boolean(part))
|
|
119
153
|
.join(', ')}
|
|
120
|
-
className={
|
|
121
|
-
'group relative flex w-full flex-col gap-4 rounded-8 border border-transparent px-10 py-8 text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-border-interactive-with-active focus-visible:outline-none',
|
|
122
|
-
selected && 'bg-background-components-hover',
|
|
123
|
-
)}
|
|
154
|
+
className={rowClassName}
|
|
124
155
|
>
|
|
125
156
|
{body}
|
|
126
157
|
</Link>
|
|
127
158
|
);
|
|
128
|
-
|
|
129
|
-
return runLink;
|
|
130
159
|
}
|
|
131
160
|
|
|
132
161
|
function TriggerLabel({run}: {run: WorkflowRunListItem}) {
|
|
133
162
|
return (
|
|
134
|
-
<
|
|
135
|
-
<
|
|
136
|
-
<
|
|
137
|
-
<
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
<
|
|
150
|
-
|
|
151
|
-
|
|
163
|
+
<Tooltip>
|
|
164
|
+
<TooltipTrigger asChild>
|
|
165
|
+
<span className="flex min-w-0 max-w-[140px] items-center gap-6 outline-none">
|
|
166
|
+
<TriggerSourceIcon
|
|
167
|
+
provider={run.triggerProvider}
|
|
168
|
+
source={run.triggerSource}
|
|
169
|
+
aria-hidden="true"
|
|
170
|
+
className="size-12 shrink-0 text-foreground-neutral-muted"
|
|
171
|
+
/>
|
|
172
|
+
<Text as="span" size="xs" className="min-w-0 truncate">
|
|
173
|
+
{run.triggerDisplayLabel}
|
|
174
|
+
</Text>
|
|
175
|
+
</span>
|
|
176
|
+
</TooltipTrigger>
|
|
177
|
+
<TooltipContent>
|
|
178
|
+
<Text as="span" size="xs" className="block max-w-[360px] break-words">
|
|
179
|
+
{run.triggerLabel}
|
|
180
|
+
</Text>
|
|
181
|
+
</TooltipContent>
|
|
182
|
+
</Tooltip>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function BranchLabel({branch, isPullRequest}: {branch: string; isPullRequest: boolean}) {
|
|
187
|
+
return (
|
|
188
|
+
<MetadataChip
|
|
189
|
+
icon={isPullRequest ? 'gitPullRequestLine' : 'gitBranchLine'}
|
|
190
|
+
title={isPullRequest ? `Pull request ${branch}` : `Branch ${branch}`}
|
|
191
|
+
className="max-w-[180px]"
|
|
192
|
+
>
|
|
193
|
+
{branch}
|
|
194
|
+
</MetadataChip>
|
|
152
195
|
);
|
|
153
196
|
}
|
|
154
197
|
|
|
155
|
-
function
|
|
198
|
+
function CommitLabel({commit}: {commit: string}) {
|
|
156
199
|
return (
|
|
157
|
-
<
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
</Text>
|
|
161
|
-
</TooltipContent>
|
|
200
|
+
<MetadataChip icon="gitCommitLine" title={`Commit ${commit}`}>
|
|
201
|
+
{commit}
|
|
202
|
+
</MetadataChip>
|
|
162
203
|
);
|
|
163
204
|
}
|
|
164
205
|
|
|
165
|
-
function
|
|
206
|
+
function ActorLabel({actor}: {actor: string}) {
|
|
166
207
|
return (
|
|
167
|
-
<
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
208
|
+
<MetadataChip
|
|
209
|
+
icon="userLine"
|
|
210
|
+
title={`Triggered by ${actor}`}
|
|
211
|
+
// Dropped once the row is two lines, where the provenance line has to stay readable on
|
|
212
|
+
// a phone.
|
|
213
|
+
className="hidden max-w-[120px] @min-[976px]:flex"
|
|
172
214
|
>
|
|
173
|
-
|
|
174
|
-
</
|
|
215
|
+
{actor}
|
|
216
|
+
</MetadataChip>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* A ref, SHA, or handle: monospace because it is content a user copies or pattern-matches,
|
|
222
|
+
* with the icon carrying the kind so the value itself never needs a written label.
|
|
223
|
+
*/
|
|
224
|
+
function MetadataChip({
|
|
225
|
+
icon,
|
|
226
|
+
title,
|
|
227
|
+
className,
|
|
228
|
+
children,
|
|
229
|
+
}: {
|
|
230
|
+
icon: IconName;
|
|
231
|
+
title: string;
|
|
232
|
+
className?: string | undefined;
|
|
233
|
+
children: string;
|
|
234
|
+
}) {
|
|
235
|
+
return (
|
|
236
|
+
<span className={cn('flex min-w-0 shrink-0 items-center gap-4', className)} title={title}>
|
|
237
|
+
<Icon name={icon} className="size-12 shrink-0 text-foreground-neutral-muted" aria-hidden />
|
|
238
|
+
<Code as="span" variant="label" className="min-w-0 truncate">
|
|
239
|
+
{children}
|
|
240
|
+
</Code>
|
|
241
|
+
</span>
|
|
175
242
|
);
|
|
176
243
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {render, screen} from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import {formatWorkflowRunNumberLabel, WorkflowRunNumberLabel} from './workflow-run-number-label.js';
|
|
4
|
+
|
|
5
|
+
describe('WorkflowRunNumberLabel', () => {
|
|
6
|
+
test('formats the workflow name and run number together', () => {
|
|
7
|
+
expect(formatWorkflowRunNumberLabel({workflowName: 'CI', number: 5184})).toBe('CI #5184');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test('shows the bare number when the run name already says the workflow name', () => {
|
|
11
|
+
render(<WorkflowRunNumberLabel run={{name: 'CI', workflowName: 'CI', number: 5184}} />);
|
|
12
|
+
|
|
13
|
+
expect(screen.getByText('#5184')).toBeInTheDocument();
|
|
14
|
+
expect(screen.queryByText('CI #5184')).not.toBeInTheDocument();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('keeps the workflow name when the run carries a different one', () => {
|
|
18
|
+
render(
|
|
19
|
+
<WorkflowRunNumberLabel
|
|
20
|
+
run={{name: 'Deploy production', workflowName: 'CI', number: 5184}}
|
|
21
|
+
/>,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
expect(screen.getByText('CI #5184')).toBeInTheDocument();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('does not render before a server run number is available', () => {
|
|
28
|
+
const {container} = render(<WorkflowRunNumberLabel run={{workflowName: 'CI', number: null}} />);
|
|
29
|
+
|
|
30
|
+
expect(container.firstChild).toBeNull();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('provides a tooltip for long workflow labels', async () => {
|
|
34
|
+
const user = userEvent.setup();
|
|
35
|
+
const label = 'release-production-multi-region-with-canary-and-smoke-tests #5184';
|
|
36
|
+
render(<WorkflowRunNumberLabel run={{workflowName: label.slice(0, -6), number: 5184}} />);
|
|
37
|
+
|
|
38
|
+
const visibleLabel = screen.getByText(label);
|
|
39
|
+
expect(visibleLabel).toHaveClass('max-w-[240px]', 'truncate');
|
|
40
|
+
|
|
41
|
+
await user.hover(visibleLabel);
|
|
42
|
+
|
|
43
|
+
expect(await screen.findByRole('tooltip')).toHaveTextContent(label);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';
|
|
2
|
+
import {Code} from '@shipfox/react-ui/typography';
|
|
3
|
+
import type {WorkflowRun} from '#core/workflow-run.js';
|
|
4
|
+
|
|
5
|
+
type WorkflowRunNumberLabelRun = Pick<WorkflowRun, 'number' | 'workflowName'> &
|
|
6
|
+
Partial<Pick<WorkflowRun, 'name'>>;
|
|
7
|
+
|
|
8
|
+
export function formatWorkflowRunNumberLabel({
|
|
9
|
+
number,
|
|
10
|
+
workflowName,
|
|
11
|
+
}: WorkflowRunNumberLabelRun): string | undefined {
|
|
12
|
+
return number === null ? undefined : `${workflowName} #${number}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The run's identity beside its name.
|
|
17
|
+
*
|
|
18
|
+
* A run whose name was never overridden carries its workflow name twice on one line, so the
|
|
19
|
+
* visible label drops to the bare number in that case. The full label stays in the tooltip
|
|
20
|
+
* and in `formatWorkflowRunNumberLabel`, which feeds accessible names.
|
|
21
|
+
*/
|
|
22
|
+
export function WorkflowRunNumberLabel({run}: {run: WorkflowRunNumberLabelRun}) {
|
|
23
|
+
const label = formatWorkflowRunNumberLabel(run);
|
|
24
|
+
if (label === undefined) return null;
|
|
25
|
+
|
|
26
|
+
const namesTheWorkflowTwice = run.name === run.workflowName;
|
|
27
|
+
const visibleLabel = namesTheWorkflowTwice ? `#${run.number}` : label;
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Tooltip>
|
|
31
|
+
<TooltipTrigger asChild>
|
|
32
|
+
<Code
|
|
33
|
+
as="span"
|
|
34
|
+
variant="label"
|
|
35
|
+
className="min-w-0 max-w-[240px] truncate text-foreground-neutral-muted"
|
|
36
|
+
>
|
|
37
|
+
{visibleLabel}
|
|
38
|
+
</Code>
|
|
39
|
+
</TooltipTrigger>
|
|
40
|
+
<TooltipContent>
|
|
41
|
+
<Code as="span" variant="label" className="block max-w-[360px] break-words">
|
|
42
|
+
{label}
|
|
43
|
+
</Code>
|
|
44
|
+
</TooltipContent>
|
|
45
|
+
</Tooltip>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -8,13 +8,12 @@ import {
|
|
|
8
8
|
workflowRunAttemptDto,
|
|
9
9
|
workflowRunDetail,
|
|
10
10
|
} from '#test/fixtures/workflow-run.js';
|
|
11
|
-
import {jsonResponse,
|
|
11
|
+
import {jsonResponse, PROJECT_TEST_WSLUG, renderProjectPage} from '#test/pages.js';
|
|
12
12
|
import {WorkflowRunAttemptSwitcher} from './workflow-run-attempt-switcher.js';
|
|
13
13
|
|
|
14
14
|
const ROOT_RUN_ID = '11111111-1111-4111-8111-111111111111';
|
|
15
15
|
const CURRENT_RUN_ID = '22222222-2222-4222-8222-222222222222';
|
|
16
16
|
const THIRD_RUN_ID = '33333333-3333-4333-8333-333333333333';
|
|
17
|
-
const PROJECT_ID = '44444444-4444-4444-8444-444444444444';
|
|
18
17
|
const SWITCH_ATTEMPT_PATTERN = /Switch attempt/;
|
|
19
18
|
const ATTEMPT_1_PATTERN = /Attempt 1/;
|
|
20
19
|
const ATTEMPT_2_PATTERN = /Attempt 2/;
|
|
@@ -176,7 +175,7 @@ describe('WorkflowRunAttemptSwitcher', () => {
|
|
|
176
175
|
),
|
|
177
176
|
});
|
|
178
177
|
const {router} = renderSwitcher({
|
|
179
|
-
path: `/
|
|
178
|
+
path: `/w/${PROJECT_TEST_WSLUG}/p/project/runs/${CURRENT_RUN_ID}?job=job-1&step=step-1&stepAttempt=attempt-1`,
|
|
180
179
|
});
|
|
181
180
|
|
|
182
181
|
await user.click(await screen.findByRole('button', {name: 'Switch attempt, currently 2 of 4'}));
|
|
@@ -184,14 +183,14 @@ describe('WorkflowRunAttemptSwitcher', () => {
|
|
|
184
183
|
|
|
185
184
|
expect(attemptLink).toHaveAttribute(
|
|
186
185
|
'href',
|
|
187
|
-
`/
|
|
186
|
+
`/w/${PROJECT_TEST_WSLUG}/p/project/runs/${CURRENT_RUN_ID}?runAttempt=1`,
|
|
188
187
|
);
|
|
189
188
|
|
|
190
189
|
await user.click(attemptLink);
|
|
191
190
|
|
|
192
191
|
await waitFor(() =>
|
|
193
192
|
expect(router.state.location.pathname).toBe(
|
|
194
|
-
`/
|
|
193
|
+
`/w/${PROJECT_TEST_WSLUG}/p/project/runs/${CURRENT_RUN_ID}`,
|
|
195
194
|
),
|
|
196
195
|
);
|
|
197
196
|
expect(router.state.location.search).toEqual({runAttempt: 1});
|
|
@@ -200,7 +199,7 @@ describe('WorkflowRunAttemptSwitcher', () => {
|
|
|
200
199
|
|
|
201
200
|
function renderSwitcher({
|
|
202
201
|
latestAttempt = 4,
|
|
203
|
-
path = `/
|
|
202
|
+
path = `/w/${PROJECT_TEST_WSLUG}/p/project/runs/${CURRENT_RUN_ID}`,
|
|
204
203
|
}: {
|
|
205
204
|
latestAttempt?: number | undefined;
|
|
206
205
|
path?: string | undefined;
|
|
@@ -219,8 +218,8 @@ function renderSwitcher({
|
|
|
219
218
|
return renderProjectPage(path, () => (
|
|
220
219
|
<div data-testid="attempt-switcher-test-mount">
|
|
221
220
|
<WorkflowRunAttemptSwitcher
|
|
222
|
-
|
|
223
|
-
|
|
221
|
+
workspaceSlug={PROJECT_TEST_WSLUG}
|
|
222
|
+
projectSlug="project"
|
|
224
223
|
run={run}
|
|
225
224
|
latestAttempt={latestAttempt}
|
|
226
225
|
/>
|
|
@@ -16,15 +16,15 @@ import {useWorkflowRunAttemptsQuery} from '#hooks/api/workflow-runs.js';
|
|
|
16
16
|
import {WorkflowStatusIcon} from '../workflow-status/workflow-status-icon.js';
|
|
17
17
|
|
|
18
18
|
export interface WorkflowRunAttemptSwitcherProps {
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
workspaceSlug?: string | undefined;
|
|
20
|
+
projectSlug?: string | undefined;
|
|
21
21
|
run: WorkflowRunDetail;
|
|
22
22
|
latestAttempt: number;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export function WorkflowRunAttemptSwitcher({
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
workspaceSlug,
|
|
27
|
+
projectSlug,
|
|
28
28
|
run,
|
|
29
29
|
latestAttempt,
|
|
30
30
|
}: WorkflowRunAttemptSwitcherProps) {
|
|
@@ -35,6 +35,7 @@ export function WorkflowRunAttemptSwitcher({
|
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
if (latestAttempt <= 1) return null;
|
|
38
|
+
if (!workspaceSlug || !projectSlug) return null;
|
|
38
39
|
|
|
39
40
|
const attempts = attemptsQuery.data ?? [];
|
|
40
41
|
const latestLoadedAttempt = Math.max(0, ...attempts.map((attempt) => attempt.attempt));
|
|
@@ -73,8 +74,8 @@ export function WorkflowRunAttemptSwitcher({
|
|
|
73
74
|
attempt={attempt}
|
|
74
75
|
current={attempt.id === run.runAttempt.id}
|
|
75
76
|
workflowRunId={run.id}
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
workspaceSlug={workspaceSlug}
|
|
78
|
+
projectSlug={projectSlug}
|
|
78
79
|
/>
|
|
79
80
|
))
|
|
80
81
|
: null}
|
|
@@ -107,20 +108,20 @@ function AttemptItem({
|
|
|
107
108
|
attempt,
|
|
108
109
|
current,
|
|
109
110
|
workflowRunId,
|
|
110
|
-
|
|
111
|
-
|
|
111
|
+
workspaceSlug,
|
|
112
|
+
projectSlug,
|
|
112
113
|
}: {
|
|
113
114
|
attempt: WorkflowRunAttempt;
|
|
114
115
|
current: boolean;
|
|
115
116
|
workflowRunId: string;
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
workspaceSlug?: string | undefined;
|
|
118
|
+
projectSlug?: string | undefined;
|
|
118
119
|
}) {
|
|
119
120
|
return (
|
|
120
121
|
<DropdownMenuItem asChild className={cn(current && 'text-foreground-neutral-base')}>
|
|
121
122
|
<Link
|
|
122
|
-
to="/
|
|
123
|
-
params={{
|
|
123
|
+
to="/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId"
|
|
124
|
+
params={{workspaceSlug, projectSlug, workflowRunId}}
|
|
124
125
|
search={
|
|
125
126
|
((previous: Record<string, unknown>) => {
|
|
126
127
|
if (current) return previous;
|
|
@@ -26,8 +26,6 @@ import {WorkflowRunSummary} from './workflow-run-summary.js';
|
|
|
26
26
|
const ROOT_RUN_ID = '11111111-1111-4111-8111-111111111111';
|
|
27
27
|
const CURRENT_RUN_ID = '22222222-2222-4222-8222-222222222222';
|
|
28
28
|
const NEXT_RUN_ID = '33333333-3333-4333-8333-333333333333';
|
|
29
|
-
const WORKSPACE_ID = '44444444-4444-4444-8444-444444444444';
|
|
30
|
-
const PROJECT_ID = '55555555-5555-4555-8555-555555555555';
|
|
31
29
|
const SWITCH_ATTEMPT_PATTERN = /Switch attempt/;
|
|
32
30
|
const ATTEMPT_3_PATTERN = /Attempt 3/;
|
|
33
31
|
const STORYBOOK_NOW = '2026-06-26T12:00:00.000Z';
|
|
@@ -71,12 +69,12 @@ const withAttemptApi: Decorator = (Story) => {
|
|
|
71
69
|
const rootRoute = createRootRoute({component: Outlet});
|
|
72
70
|
const runRoute = createRoute({
|
|
73
71
|
getParentRoute: () => rootRoute,
|
|
74
|
-
path: '/
|
|
72
|
+
path: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',
|
|
75
73
|
component: () => <Story />,
|
|
76
74
|
});
|
|
77
75
|
const router = createRouter({
|
|
78
76
|
history: createMemoryHistory({
|
|
79
|
-
initialEntries: [`/
|
|
77
|
+
initialEntries: [`/w/acme/p/project/runs/${CURRENT_RUN_ID}`],
|
|
80
78
|
}),
|
|
81
79
|
routeTree: rootRoute.addChildren([runRoute]),
|
|
82
80
|
});
|
|
@@ -155,8 +153,8 @@ const ATTEMPT_SUMMARY_ARGS = {
|
|
|
155
153
|
}),
|
|
156
154
|
status: 'failed',
|
|
157
155
|
}),
|
|
158
|
-
|
|
159
|
-
|
|
156
|
+
workspaceSlug: 'acme',
|
|
157
|
+
projectSlug: 'project',
|
|
160
158
|
latestAttempt: 3,
|
|
161
159
|
};
|
|
162
160
|
|
|
@@ -352,8 +350,6 @@ export const ActionVariantsWithAttempts: Story = {
|
|
|
352
350
|
{ACTION_VARIANTS.map(({label, run, props}, index) => (
|
|
353
351
|
<WorkflowRunSummary
|
|
354
352
|
key={label}
|
|
355
|
-
workspaceId={WORKSPACE_ID}
|
|
356
|
-
projectId={PROJECT_ID}
|
|
357
353
|
run={{
|
|
358
354
|
...run,
|
|
359
355
|
id: `22222222-2222-4222-8222-${String(index + 2).padStart(12, '0')}`,
|
|
@@ -369,6 +365,8 @@ export const ActionVariantsWithAttempts: Story = {
|
|
|
369
365
|
rerunMode: null,
|
|
370
366
|
}),
|
|
371
367
|
}}
|
|
368
|
+
workspaceSlug="acme"
|
|
369
|
+
projectSlug="project"
|
|
372
370
|
latestAttempt={3}
|
|
373
371
|
{...props}
|
|
374
372
|
/>
|
|
@@ -29,13 +29,14 @@ describe('WorkflowRunSummary', () => {
|
|
|
29
29
|
restoreElementWidthDescriptors();
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
test('renders status, trigger metadata, and trigger time', async () => {
|
|
33
|
-
renderSummary();
|
|
32
|
+
test('renders status, run number, trigger metadata, and trigger time', async () => {
|
|
33
|
+
renderSummary({workflow_name: 'CI', number: 5184});
|
|
34
34
|
|
|
35
35
|
const summary = await screen.findByRole('region', {name: 'deploy-web'});
|
|
36
36
|
|
|
37
37
|
expect(within(summary).getByRole('heading', {name: 'deploy-web'})).toBeInTheDocument();
|
|
38
38
|
expect(within(summary).getAllByText('Running')).not.toHaveLength(0);
|
|
39
|
+
expect(within(summary).getByText('CI #5184')).toBeInTheDocument();
|
|
39
40
|
expect(within(summary).getByText('fire')).toBeInTheDocument();
|
|
40
41
|
expect(within(summary).queryByText('manual')).not.toBeInTheDocument();
|
|
41
42
|
expect(within(summary).getByText(RELATIVE_TIME_TEXT_PATTERN)).toBeInTheDocument();
|
|
@@ -46,6 +47,30 @@ describe('WorkflowRunSummary', () => {
|
|
|
46
47
|
).not.toBeInTheDocument();
|
|
47
48
|
});
|
|
48
49
|
|
|
50
|
+
test('omits the run number before the server assigns one', async () => {
|
|
51
|
+
const run = {...workflowRunDetail({workflow_name: 'CI'}), number: null};
|
|
52
|
+
render(<WorkflowRunSummary run={run} />);
|
|
53
|
+
|
|
54
|
+
const summary = await screen.findByRole('region', {name: 'deploy-web'});
|
|
55
|
+
|
|
56
|
+
expect(within(summary).queryByText('CI #1')).not.toBeInTheDocument();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('separates a standalone run number from the run timestamp', async () => {
|
|
60
|
+
const run = workflowRunDetail({
|
|
61
|
+
workflow_name: 'CI',
|
|
62
|
+
trigger_source: '',
|
|
63
|
+
trigger_event: '',
|
|
64
|
+
});
|
|
65
|
+
render(<WorkflowRunSummary run={run} />);
|
|
66
|
+
|
|
67
|
+
const summary = await screen.findByRole('region', {name: 'deploy-web'});
|
|
68
|
+
|
|
69
|
+
expect(within(summary).getByText('CI #1')).toBeInTheDocument();
|
|
70
|
+
const timestamp = within(summary).getByText(RELATIVE_TIME_TEXT_PATTERN);
|
|
71
|
+
expect(timestamp.previousElementSibling).toHaveAttribute('aria-hidden', 'true');
|
|
72
|
+
});
|
|
73
|
+
|
|
49
74
|
test('uses the selected run attempt for summary status and trigger time', async () => {
|
|
50
75
|
const rootCreatedAt = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
|
|
51
76
|
const attemptCreatedAt = new Date(Date.now() - 5 * 60 * 1000).toISOString();
|