@shipfox/client-workflows 14.0.1 → 16.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.
Files changed (272) hide show
  1. package/.storybook/preview.css +4 -0
  2. package/.storybook/preview.tsx +7 -2
  3. package/.turbo/turbo-build.log +1 -1
  4. package/CHANGELOG.md +42 -0
  5. package/dist/components/job-detail/agent-config-failure-callout.d.ts.map +1 -0
  6. package/dist/components/{workflow-run-view → job-detail}/agent-config-failure-callout.js +2 -2
  7. package/dist/components/job-detail/agent-config-failure-callout.js.map +1 -0
  8. package/dist/components/job-detail/job-detail-header.d.ts +15 -0
  9. package/dist/components/job-detail/job-detail-header.d.ts.map +1 -0
  10. package/dist/components/job-detail/job-detail-header.js +124 -0
  11. package/dist/components/job-detail/job-detail-header.js.map +1 -0
  12. package/dist/components/job-detail/job-detail-view.d.ts +16 -0
  13. package/dist/components/job-detail/job-detail-view.d.ts.map +1 -0
  14. package/dist/components/job-detail/job-detail-view.js +420 -0
  15. package/dist/components/job-detail/job-detail-view.js.map +1 -0
  16. package/dist/components/job-detail/job-empty-states.d.ts +10 -0
  17. package/dist/components/job-detail/job-empty-states.d.ts.map +1 -0
  18. package/dist/components/job-detail/job-empty-states.js +195 -0
  19. package/dist/components/job-detail/job-empty-states.js.map +1 -0
  20. package/dist/components/job-detail/job-execution-switcher.d.ts.map +1 -0
  21. package/dist/components/job-detail/job-execution-switcher.js.map +1 -0
  22. package/dist/components/job-detail/job-execution-time-text.d.ts.map +1 -0
  23. package/dist/components/job-detail/job-execution-time-text.js.map +1 -0
  24. package/dist/components/job-detail/job-selection.d.ts +24 -0
  25. package/dist/components/job-detail/job-selection.d.ts.map +1 -0
  26. package/dist/components/job-detail/job-selection.js +66 -0
  27. package/dist/components/job-detail/job-selection.js.map +1 -0
  28. package/dist/components/job-detail/step-attempt-log-panel.d.ts +15 -0
  29. package/dist/components/job-detail/step-attempt-log-panel.d.ts.map +1 -0
  30. package/dist/components/{workflow-run-view → job-detail}/step-attempt-log-panel.js +61 -16
  31. package/dist/components/job-detail/step-attempt-log-panel.js.map +1 -0
  32. package/dist/components/job-graph/job-graph-content.js +1 -1
  33. package/dist/components/job-graph/job-graph-content.js.map +1 -1
  34. package/dist/components/step-list/step-list-model.js +2 -2
  35. package/dist/components/step-list/step-list-model.js.map +1 -1
  36. package/dist/components/step-list/step-list.d.ts +1 -0
  37. package/dist/components/step-list/step-list.d.ts.map +1 -1
  38. package/dist/components/step-list/step-list.js +29 -4
  39. package/dist/components/step-list/step-list.js.map +1 -1
  40. package/dist/components/workflow-run-duration-label.d.ts +9 -4
  41. package/dist/components/workflow-run-duration-label.d.ts.map +1 -1
  42. package/dist/components/workflow-run-duration-label.js +27 -7
  43. package/dist/components/workflow-run-duration-label.js.map +1 -1
  44. package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
  45. package/dist/components/workflow-run-list/workflow-run-row.js +6 -5
  46. package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
  47. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
  48. package/dist/components/workflow-run-summary/workflow-run-summary.js +69 -64
  49. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  50. package/dist/components/workflow-run-tabs/index.d.ts +3 -3
  51. package/dist/components/workflow-run-tabs/index.d.ts.map +1 -1
  52. package/dist/components/workflow-run-tabs/index.js +3 -3
  53. package/dist/components/workflow-run-tabs/index.js.map +1 -1
  54. package/dist/components/workflow-run-tabs/run-annotation-count-chip.d.ts +16 -0
  55. package/dist/components/workflow-run-tabs/run-annotation-count-chip.d.ts.map +1 -0
  56. package/dist/components/workflow-run-tabs/run-annotation-count-chip.js +50 -0
  57. package/dist/components/workflow-run-tabs/run-annotation-count-chip.js.map +1 -0
  58. package/dist/components/workflow-run-tabs/run-annotation-item.d.ts +20 -0
  59. package/dist/components/workflow-run-tabs/run-annotation-item.d.ts.map +1 -0
  60. package/dist/components/workflow-run-tabs/run-annotation-item.js +102 -0
  61. package/dist/components/workflow-run-tabs/run-annotation-item.js.map +1 -0
  62. package/dist/components/workflow-run-tabs/run-annotation-list.d.ts +32 -0
  63. package/dist/components/workflow-run-tabs/run-annotation-list.d.ts.map +1 -0
  64. package/dist/components/workflow-run-tabs/run-annotation-list.js +202 -0
  65. package/dist/components/workflow-run-tabs/run-annotation-list.js.map +1 -0
  66. package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts +1 -1
  67. package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts.map +1 -1
  68. package/dist/components/workflow-run-tabs/run-annotation-summary-line.js +87 -22
  69. package/dist/components/workflow-run-tabs/run-annotation-summary-line.js.map +1 -1
  70. package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts +3 -1
  71. package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts.map +1 -1
  72. package/dist/components/workflow-run-tabs/run-annotations-empty.js +2 -2
  73. package/dist/components/workflow-run-tabs/run-annotations-empty.js.map +1 -1
  74. package/dist/components/workflow-run-tabs/severity-visuals.d.ts +20 -0
  75. package/dist/components/workflow-run-tabs/severity-visuals.d.ts.map +1 -0
  76. package/dist/components/workflow-run-tabs/severity-visuals.js +31 -0
  77. package/dist/components/workflow-run-tabs/severity-visuals.js.map +1 -0
  78. package/dist/components/workflow-run-view/index.d.ts +0 -1
  79. package/dist/components/workflow-run-view/index.d.ts.map +1 -1
  80. package/dist/components/workflow-run-view/index.js +0 -1
  81. package/dist/components/workflow-run-view/index.js.map +1 -1
  82. package/dist/components/workflow-run-view/run-workspace-nav.d.ts +20 -0
  83. package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -0
  84. package/dist/components/workflow-run-view/run-workspace-nav.js +277 -0
  85. package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -0
  86. package/dist/components/workflow-run-view/workflow-run-selection.d.ts.map +1 -1
  87. package/dist/components/workflow-run-view/workflow-run-selection.js +1 -14
  88. package/dist/components/workflow-run-view/workflow-run-selection.js.map +1 -1
  89. package/dist/components/workflow-run-view/workflow-run-view.d.ts +10 -10
  90. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
  91. package/dist/components/workflow-run-view/workflow-run-view.js +351 -239
  92. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  93. package/dist/components/workflow-status/status-visuals.d.ts.map +1 -1
  94. package/dist/components/workflow-status/status-visuals.js +10 -2
  95. package/dist/components/workflow-status/status-visuals.js.map +1 -1
  96. package/dist/components/workflow-status/workflow-status-icon.d.ts +3 -1
  97. package/dist/components/workflow-status/workflow-status-icon.d.ts.map +1 -1
  98. package/dist/components/workflow-status/workflow-status-icon.js +13 -2
  99. package/dist/components/workflow-status/workflow-status-icon.js.map +1 -1
  100. package/dist/core/entities/job-execution.d.ts +9 -0
  101. package/dist/core/entities/job-execution.d.ts.map +1 -1
  102. package/dist/core/entities/job-execution.js +13 -3
  103. package/dist/core/entities/job-execution.js.map +1 -1
  104. package/dist/core/entities/job.d.ts.map +1 -1
  105. package/dist/core/entities/step-attempt.d.ts.map +1 -1
  106. package/dist/core/entities/step.d.ts +2 -0
  107. package/dist/core/entities/step.d.ts.map +1 -1
  108. package/dist/core/entities/step.js +13 -0
  109. package/dist/core/entities/step.js.map +1 -1
  110. package/dist/core/entities/workflow-run-attempt.d.ts.map +1 -1
  111. package/dist/core/entities/workflow-run.d.ts +56 -1
  112. package/dist/core/entities/workflow-run.d.ts.map +1 -1
  113. package/dist/core/entities/workflow-run.js +98 -0
  114. package/dist/core/entities/workflow-run.js.map +1 -1
  115. package/dist/core/run-annotation.d.ts +73 -0
  116. package/dist/core/run-annotation.d.ts.map +1 -0
  117. package/dist/core/run-annotation.js +125 -0
  118. package/dist/core/run-annotation.js.map +1 -0
  119. package/dist/core/workflow-run.d.ts +4 -4
  120. package/dist/core/workflow-run.d.ts.map +1 -1
  121. package/dist/core/workflow-run.js +3 -3
  122. package/dist/core/workflow-run.js.map +1 -1
  123. package/dist/feature.d.ts +20 -16
  124. package/dist/feature.d.ts.map +1 -1
  125. package/dist/feature.js +5 -0
  126. package/dist/feature.js.map +1 -1
  127. package/dist/hooks/api/run-annotation-mapper.d.ts +10 -0
  128. package/dist/hooks/api/run-annotation-mapper.d.ts.map +1 -0
  129. package/dist/hooks/api/run-annotation-mapper.js +22 -0
  130. package/dist/hooks/api/run-annotation-mapper.js.map +1 -0
  131. package/dist/hooks/api/run-annotations.d.ts +25 -0
  132. package/dist/hooks/api/run-annotations.d.ts.map +1 -0
  133. package/dist/hooks/api/run-annotations.js +78 -0
  134. package/dist/hooks/api/run-annotations.js.map +1 -0
  135. package/dist/hooks/api/workflow-runs.d.ts +5 -3
  136. package/dist/hooks/api/workflow-runs.d.ts.map +1 -1
  137. package/dist/hooks/api/workflow-runs.js +5 -4
  138. package/dist/hooks/api/workflow-runs.js.map +1 -1
  139. package/dist/index.d.ts +1 -0
  140. package/dist/index.d.ts.map +1 -1
  141. package/dist/index.js +1 -0
  142. package/dist/index.js.map +1 -1
  143. package/dist/pages/workflow-job-detail-page.d.ts +11 -0
  144. package/dist/pages/workflow-job-detail-page.d.ts.map +1 -0
  145. package/dist/pages/workflow-job-detail-page.js +66 -0
  146. package/dist/pages/workflow-job-detail-page.js.map +1 -0
  147. package/dist/pages/workflow-run-detail-page.d.ts.map +1 -1
  148. package/dist/pages/workflow-run-detail-page.js +4 -37
  149. package/dist/pages/workflow-run-detail-page.js.map +1 -1
  150. package/dist/routes/inputs.d.ts +52 -34
  151. package/dist/routes/inputs.d.ts.map +1 -1
  152. package/dist/routes/inputs.js +74 -43
  153. package/dist/routes/inputs.js.map +1 -1
  154. package/dist/routes/job-detail.d.ts +10 -0
  155. package/dist/routes/job-detail.d.ts.map +1 -0
  156. package/dist/routes/job-detail.js +27 -0
  157. package/dist/routes/job-detail.js.map +1 -0
  158. package/dist/routes/project-route.d.ts +1 -1
  159. package/dist/routes/run-detail.d.ts.map +1 -1
  160. package/dist/routes/runs.d.ts.map +1 -1
  161. package/dist/routes/workflows.d.ts.map +1 -1
  162. package/dist/tsconfig.test.tsbuildinfo +1 -1
  163. package/package.json +10 -9
  164. package/src/components/{workflow-run-view → job-detail}/agent-config-failure-callout.stories.tsx +1 -1
  165. package/src/components/{workflow-run-view → job-detail}/agent-config-failure-callout.tsx +2 -2
  166. package/src/components/job-detail/job-detail-header.test.tsx +34 -0
  167. package/src/components/job-detail/job-detail-header.tsx +136 -0
  168. package/src/components/job-detail/job-detail-view.stories.tsx +531 -0
  169. package/src/components/job-detail/job-detail-view.tsx +467 -0
  170. package/src/components/job-detail/job-empty-states.test.ts +27 -0
  171. package/src/components/job-detail/job-empty-states.tsx +248 -0
  172. package/src/components/job-detail/job-selection.test.ts +188 -0
  173. package/src/components/job-detail/job-selection.ts +96 -0
  174. package/src/components/{workflow-run-view → job-detail}/step-attempt-log-panel.test.tsx +32 -9
  175. package/src/components/{workflow-run-view → job-detail}/step-attempt-log-panel.tsx +52 -18
  176. package/src/components/job-graph/job-graph-content.tsx +1 -1
  177. package/src/components/step-list/step-list-model.test.ts +1 -1
  178. package/src/components/step-list/step-list-model.ts +2 -2
  179. package/src/components/step-list/step-list.stories.tsx +1 -1
  180. package/src/components/step-list/step-list.test.tsx +33 -0
  181. package/src/components/step-list/step-list.tsx +40 -9
  182. package/src/components/workflow-run-duration-label.tsx +50 -12
  183. package/src/components/workflow-run-list/workflow-run-row.tsx +6 -4
  184. package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +38 -0
  185. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +79 -5
  186. package/src/components/workflow-run-summary/workflow-run-summary.tsx +56 -60
  187. package/src/components/workflow-run-tabs/index.ts +3 -3
  188. package/src/components/workflow-run-tabs/run-annotation-count-chip.tsx +63 -0
  189. package/src/components/workflow-run-tabs/run-annotation-item.tsx +123 -0
  190. package/src/components/workflow-run-tabs/run-annotation-list.tsx +258 -0
  191. package/src/components/workflow-run-tabs/run-annotation-summary-line.test.tsx +77 -3
  192. package/src/components/workflow-run-tabs/run-annotation-summary-line.tsx +92 -17
  193. package/src/components/workflow-run-tabs/run-annotations-empty.tsx +6 -2
  194. package/src/components/workflow-run-tabs/run-annotations.stories.tsx +293 -0
  195. package/src/components/workflow-run-tabs/severity-visuals.ts +37 -0
  196. package/src/components/workflow-run-view/index.ts +0 -1
  197. package/src/components/workflow-run-view/run-workspace-nav.test.tsx +286 -0
  198. package/src/components/workflow-run-view/run-workspace-nav.tsx +314 -0
  199. package/src/components/workflow-run-view/workflow-run-selection.ts +1 -23
  200. package/src/components/workflow-run-view/workflow-run-view.test.tsx +237 -1085
  201. package/src/components/workflow-run-view/workflow-run-view.tsx +450 -323
  202. package/src/components/workflow-status/status-visuals.ts +5 -2
  203. package/src/components/workflow-status/workflow-status-icon.stories.tsx +1 -0
  204. package/src/components/workflow-status/workflow-status-icon.tsx +26 -1
  205. package/src/core/entities/job-execution.ts +17 -2
  206. package/src/core/entities/step.ts +26 -0
  207. package/src/core/entities/workflow-run-display.test.ts +159 -0
  208. package/src/core/entities/workflow-run.ts +139 -1
  209. package/src/core/run-annotation.test.ts +315 -0
  210. package/src/core/run-annotation.ts +238 -0
  211. package/src/core/workflow-run.ts +16 -1
  212. package/src/feature.ts +5 -0
  213. package/src/hooks/api/run-annotation-mapper.ts +30 -0
  214. package/src/hooks/api/run-annotations.ts +137 -0
  215. package/src/hooks/api/workflow-runs.ts +6 -2
  216. package/src/index.ts +1 -0
  217. package/src/page-harness-budget.test.ts +1 -0
  218. package/src/pages/workflow-job-detail-page.test.tsx +605 -0
  219. package/src/pages/workflow-job-detail-page.tsx +84 -0
  220. package/src/pages/workflow-run-detail-page.tsx +3 -43
  221. package/src/pages/workflow-run-pages.test.tsx +77 -332
  222. package/src/routes/inputs.test.ts +56 -6
  223. package/src/routes/inputs.ts +71 -19
  224. package/src/routes/job-detail.tsx +29 -0
  225. package/test/pages.tsx +25 -3
  226. package/test/render.tsx +6 -1
  227. package/tsconfig.build.tsbuildinfo +1 -1
  228. package/dist/components/workflow-run-tabs/run-jobs-list.d.ts +0 -18
  229. package/dist/components/workflow-run-tabs/run-jobs-list.d.ts.map +0 -1
  230. package/dist/components/workflow-run-tabs/run-jobs-list.js +0 -107
  231. package/dist/components/workflow-run-tabs/run-jobs-list.js.map +0 -1
  232. package/dist/components/workflow-run-tabs/run-tab-count.d.ts +0 -6
  233. package/dist/components/workflow-run-tabs/run-tab-count.d.ts.map +0 -1
  234. package/dist/components/workflow-run-tabs/run-tab-count.js +0 -27
  235. package/dist/components/workflow-run-tabs/run-tab-count.js.map +0 -1
  236. package/dist/components/workflow-run-tabs/run-tab-strip.d.ts +0 -8
  237. package/dist/components/workflow-run-tabs/run-tab-strip.d.ts.map +0 -1
  238. package/dist/components/workflow-run-tabs/run-tab-strip.js +0 -82
  239. package/dist/components/workflow-run-tabs/run-tab-strip.js.map +0 -1
  240. package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts.map +0 -1
  241. package/dist/components/workflow-run-view/agent-config-failure-callout.js.map +0 -1
  242. package/dist/components/workflow-run-view/job-card.d.ts +0 -17
  243. package/dist/components/workflow-run-view/job-card.d.ts.map +0 -1
  244. package/dist/components/workflow-run-view/job-card.js +0 -496
  245. package/dist/components/workflow-run-view/job-card.js.map +0 -1
  246. package/dist/components/workflow-run-view/job-execution-switcher.d.ts.map +0 -1
  247. package/dist/components/workflow-run-view/job-execution-switcher.js.map +0 -1
  248. package/dist/components/workflow-run-view/job-execution-time-text.d.ts.map +0 -1
  249. package/dist/components/workflow-run-view/job-execution-time-text.js.map +0 -1
  250. package/dist/components/workflow-run-view/step-attempt-log-panel.d.ts +0 -9
  251. package/dist/components/workflow-run-view/step-attempt-log-panel.d.ts.map +0 -1
  252. package/dist/components/workflow-run-view/step-attempt-log-panel.js.map +0 -1
  253. package/dist/core/workflow-run-tabs.d.ts +0 -9
  254. package/dist/core/workflow-run-tabs.d.ts.map +0 -1
  255. package/dist/core/workflow-run-tabs.js +0 -3
  256. package/dist/core/workflow-run-tabs.js.map +0 -1
  257. package/src/components/workflow-run-tabs/run-jobs-list.tsx +0 -151
  258. package/src/components/workflow-run-tabs/run-tab-count.tsx +0 -28
  259. package/src/components/workflow-run-tabs/run-tab-strip.stories.tsx +0 -52
  260. package/src/components/workflow-run-tabs/run-tab-strip.test.tsx +0 -117
  261. package/src/components/workflow-run-tabs/run-tab-strip.tsx +0 -97
  262. package/src/components/workflow-run-view/job-card.stories.tsx +0 -789
  263. package/src/components/workflow-run-view/job-card.test.tsx +0 -115
  264. package/src/components/workflow-run-view/job-card.tsx +0 -614
  265. package/src/core/workflow-run-tabs.ts +0 -8
  266. /package/dist/components/{workflow-run-view → job-detail}/agent-config-failure-callout.d.ts +0 -0
  267. /package/dist/components/{workflow-run-view → job-detail}/job-execution-switcher.d.ts +0 -0
  268. /package/dist/components/{workflow-run-view → job-detail}/job-execution-switcher.js +0 -0
  269. /package/dist/components/{workflow-run-view → job-detail}/job-execution-time-text.d.ts +0 -0
  270. /package/dist/components/{workflow-run-view → job-detail}/job-execution-time-text.js +0 -0
  271. /package/src/components/{workflow-run-view → job-detail}/job-execution-switcher.tsx +0 -0
  272. /package/src/components/{workflow-run-view → job-detail}/job-execution-time-text.tsx +0 -0
@@ -1,14 +1,5 @@
1
- import {useNavigate} from '@tanstack/react-router';
2
- import {useCallback} from 'react';
3
1
  import {WorkflowRunView} from '#components/workflow-run-view/index.js';
4
- import type {WorkflowRunSelectionInput} from '#core/workflow-run-url-state.js';
5
- import {
6
- validateWorkflowRunsSearch,
7
- type WorkflowRunsSearch,
8
- type WorkflowRunTab,
9
- workflowRunSearchParams,
10
- workflowRunTab,
11
- } from '#routes/inputs.js';
2
+ import {type WorkflowRunsSearch, workflowRunTab} from '#routes/inputs.js';
12
3
 
13
4
  interface WorkflowRunDetailPageProps {
14
5
  projectId: string;
@@ -25,36 +16,6 @@ export function WorkflowRunDetailPage({
25
16
  workflowRunId,
26
17
  search = {},
27
18
  }: WorkflowRunDetailPageProps) {
28
- const navigate = useNavigate();
29
- const selection: WorkflowRunSelectionInput = search;
30
- const onSelectionChange = useCallback(
31
- (nextSelection: WorkflowRunSelectionInput) => {
32
- navigate({
33
- search: ((previous: Record<string, unknown>) => {
34
- const current = validateWorkflowRunsSearch(previous);
35
- return workflowRunSearchParams(current, nextSelection);
36
- }) as never,
37
- replace: true,
38
- });
39
- },
40
- [navigate],
41
- );
42
- const onTabChange = useCallback(
43
- (nextTab: WorkflowRunTab) => {
44
- navigate({
45
- search: ((previous: Record<string, unknown>) => {
46
- const current = validateWorkflowRunsSearch(previous);
47
- const nextSearch = workflowRunSearchParams({...current, tab: nextTab}, current);
48
- return nextTab === 'summary' &&
49
- (current.jobId || current.jobExecutionId || current.stepId || current.stepAttemptId)
50
- ? {...nextSearch, tab: nextTab}
51
- : nextSearch;
52
- }) as never,
53
- });
54
- },
55
- [navigate],
56
- );
57
-
58
19
  return (
59
20
  <div className="flex min-h-0 flex-1 overflow-hidden bg-background-neutral-background">
60
21
  <WorkflowRunView
@@ -62,10 +23,9 @@ export function WorkflowRunDetailPage({
62
23
  workspaceSlug={workspaceSlug}
63
24
  projectSlug={projectSlug}
64
25
  workflowRunId={workflowRunId}
65
- selection={selection}
66
- onSelectionChange={onSelectionChange}
26
+ runAttempt={search.runAttempt}
27
+ selection={search}
67
28
  tab={workflowRunTab(search)}
68
- onTabChange={onTabChange}
69
29
  />
70
30
  </div>
71
31
  );
@@ -5,16 +5,17 @@ import type {
5
5
  import {configureApiClient} from '@shipfox/client-api';
6
6
  import {act, screen, waitFor} from '@testing-library/react';
7
7
  import userEvent from '@testing-library/user-event';
8
+ import type {WorkflowJobSearch, WorkflowRunsSearch} from '#routes/inputs.js';
8
9
  import {inlineLogBody, outputLine} from '#test/fixtures/logs.js';
9
10
  import {
10
11
  workflowJobDto,
11
- workflowJobExecutionDto,
12
12
  workflowRunDetailDto,
13
13
  workflowRunDto,
14
14
  workflowStepAttemptDto,
15
15
  workflowStepDto,
16
16
  } from '#test/fixtures/workflow-run.js';
17
17
  import {jsonResponse, PROJECT_TEST_WSLUG, renderProjectPage} from '#test/pages.js';
18
+ import {WorkflowJobDetailPage} from './workflow-job-detail-page.js';
18
19
  import {WorkflowRunDetailPage} from './workflow-run-detail-page.js';
19
20
  import {WorkflowRunsPage} from './workflow-run-list-page.js';
20
21
 
@@ -31,19 +32,15 @@ const DEPLOY_STEP_ID = '99999999-9999-4999-8999-999999999999';
31
32
  const DEPLOY_RETRY_STEP_ID = '99999999-9999-4999-8999-000000000003';
32
33
  const DEPLOY_ATTEMPT_ONE_ID = 'aaaaaaaa-aaaa-4aaa-8aaa-000000000001';
33
34
  const DEPLOY_ATTEMPT_TWO_ID = 'aaaaaaaa-aaaa-4aaa-8aaa-000000000002';
34
- const DEPLOY_EXECUTION_ONE_ID = 'bbbbbbbb-bbbb-4bbb-8bbb-000000000001';
35
- const DEPLOY_EXECUTION_TWO_ID = 'bbbbbbbb-bbbb-4bbb-8bbb-000000000002';
36
35
  const SMOKE_WEB_RE = /smoke-web/u;
37
36
  const DEPLOY_WEB_RE = /deploy-web/u;
38
37
  const OLDER_RUN_RE = /older-run/u;
39
38
  const INTEGRATION_TESTS_RE = /integration-tests/u;
40
39
  const BUILD_IMAGE_RE = /build-image/u;
41
40
  const STATUS_FILTER_RE = /^Status\b.*filter$/u;
42
- const EXECUTION_ONE_MENU_ITEM = /Execution #1: deploy review #1/u;
43
41
  const JOBS_TAB_NAME = /^Jobs/u;
44
42
  const BUILD_JOB_BUTTON_NAME = 'build, Succeeded';
45
43
  const DEPLOY_JOB_BUTTON_NAME = 'deploy, Running';
46
- const LIVE_DEPLOY_JOB_BUTTON_NAME = /^deploy, Running, running /u;
47
44
  const RUN_DETAIL_PATH_RE = /^\/workflows\/runs\/([^/]+)$/u;
48
45
  const RUN_OVERRIDES = {
49
46
  id: RUN_ID,
@@ -172,163 +169,77 @@ describe('WorkflowRunPages', () => {
172
169
  expect(screen.queryByLabelText('Loading workflow run')).not.toBeInTheDocument();
173
170
  });
174
171
 
175
- test('renders the detail route without mounting the run list', async () => {
176
- configureApiClient({fetchImpl: createRunDetailFetch()});
177
-
178
- renderRunPath('?tab=jobs');
179
-
180
- expect(await screen.findByRole('button', {name: DEPLOY_JOB_BUTTON_NAME})).toBeInTheDocument();
181
- expect(screen.getByRole('link', {name: 'Runs'})).toHaveAttribute(
182
- 'href',
183
- `/w/${PROJECT_TEST_WSLUG}/p/project/runs`,
184
- );
185
- expect(screen.queryByLabelText('Workflow runs')).not.toBeInTheDocument();
186
- });
187
-
188
- test('back to runs preserves list filters and drops run detail state', async () => {
189
- configureApiClient({fetchImpl: createRunDetailFetch()});
190
-
191
- renderRunPath(`?search=deploy-web&status=running&tab=jobs&job=${DEPLOY_JOB_ID}`);
192
-
193
- expect(await screen.findByRole('link', {name: 'Runs'})).toHaveAttribute(
194
- 'href',
195
- `/w/${PROJECT_TEST_WSLUG}/p/project/runs?search=deploy-web&status=running`,
196
- );
197
- });
198
-
199
- test('defaults the detail route to the Summary tab', async () => {
172
+ test('renders the run workspace with the graph as the default all-jobs Summary', async () => {
200
173
  configureApiClient({fetchImpl: createRunDetailFetch()});
201
174
 
202
175
  const {router} = renderRunPath();
203
176
 
204
177
  expect(await screen.findByRole('region', {name: 'deploy-web'})).toBeInTheDocument();
205
- const summaryTab = screen.getByRole('tab', {name: 'Summary'});
206
- expect(summaryTab).toHaveAttribute('aria-selected', 'true');
207
- expect(summaryTab).toHaveAttribute('aria-controls', screen.getByRole('tabpanel').id);
208
- expect(screen.queryByRole('list', {name: 'Run jobs'})).not.toBeInTheDocument();
178
+ expect(screen.getByRole('navigation', {name: 'Run workspace'})).toBeInTheDocument();
179
+ expect(screen.getByRole('link', {name: 'Summary'})).toHaveAttribute('aria-current', 'page');
180
+ expect(screen.getByRole('region', {name: 'All jobs summary'})).toBeInTheDocument();
181
+ expect(screen.getByRole('region', {name: 'Workflow jobs'})).toBeInTheDocument();
182
+ expect(screen.queryByLabelText('Workflow runs')).not.toBeInTheDocument();
183
+ expect(screen.queryByRole('tab', {name: JOBS_TAB_NAME})).not.toBeInTheDocument();
209
184
  expect(currentSearch(router).tab).toBeUndefined();
210
185
  });
211
186
 
212
- test('opens legacy selection-only detail URLs on the Jobs tab', async () => {
187
+ test('maps the removed Jobs tab URL to the graph Summary', async () => {
213
188
  configureApiClient({fetchImpl: createRunDetailFetch()});
214
189
 
215
- renderRunPath(`?job=${DEPLOY_JOB_ID}`);
190
+ renderRunPath('?tab=jobs');
216
191
 
217
- expect(await screen.findByRole('list', {name: 'Run jobs'})).toBeInTheDocument();
218
- expect(screen.getByRole('tab', {name: JOBS_TAB_NAME})).toHaveAttribute('aria-selected', 'true');
192
+ expect(await screen.findByRole('region', {name: 'All jobs summary'})).toBeInTheDocument();
193
+ expect(screen.getByRole('link', {name: 'Summary'})).toHaveAttribute('aria-current', 'page');
219
194
  });
220
195
 
221
- test('opens a legacy job execution URL with its owning job selected', async () => {
222
- configureApiClient({
223
- fetchImpl: createRunDetailFetch({details: {[RUN_ID]: retryRunDetailDto()}}),
224
- });
196
+ test('redirects a legacy selected-step URL to its owning dedicated job page', async () => {
197
+ configureApiClient({fetchImpl: createRunDetailFetch()});
225
198
 
226
- renderRunPath(`?jobExecution=${DEPLOY_EXECUTION_TWO_ID}`);
199
+ const {router} = renderRunPath(`?step=${DEPLOY_STEP_ID}&stepAttempt=${DEPLOY_ATTEMPT_TWO_ID}`);
227
200
 
228
- expect(await screen.findByRole('region', {name: 'deploy review #2'})).toBeInTheDocument();
229
- });
230
-
231
- test('uses the derived execution status in a live job duration label', async () => {
232
- configureApiClient({
233
- fetchImpl: createRunDetailFetch({
234
- details: {
235
- [RUN_ID]: defaultRunDetailDto({
236
- jobs: [
237
- workflowJobDto({
238
- id: DEPLOY_JOB_ID,
239
- run_attempt_id: RUN_ID,
240
- name: 'deploy',
241
- status: 'running',
242
- job_executions: [
243
- workflowJobExecutionDto({
244
- job_id: DEPLOY_JOB_ID,
245
- status: 'pending',
246
- started_at: '2026-08-04T00:00:00.000Z',
247
- steps: [workflowStepDto({status: 'running'})],
248
- }),
249
- ],
250
- }),
251
- ],
252
- }),
253
- },
254
- }),
201
+ await waitFor(() =>
202
+ expect(router.state.location.pathname).toBe(
203
+ `/w/${PROJECT_TEST_WSLUG}/p/project/runs/${RUN_ID}/jobs/${DEPLOY_JOB_ID}`,
204
+ ),
205
+ );
206
+ expect(currentSearch(router)).toMatchObject({
207
+ step: DEPLOY_STEP_ID,
208
+ stepAttempt: DEPLOY_ATTEMPT_TWO_ID,
255
209
  });
256
-
257
- renderRunPath(`?tab=jobs&job=${DEPLOY_JOB_ID}`);
258
-
259
- expect(
260
- await screen.findByRole('button', {name: LIVE_DEPLOY_JOB_BUTTON_NAME}),
261
- ).toBeInTheDocument();
210
+ expect(await screen.findByRole('heading', {name: 'deploy'})).toBeInTheDocument();
262
211
  });
263
212
 
264
- test('pushes tab changes and restores them with browser history', async () => {
213
+ test('navigates between run details and restores Summary with browser history', async () => {
265
214
  const user = userEvent.setup();
266
215
  configureApiClient({fetchImpl: createRunDetailFetch()});
267
216
 
268
217
  const {router} = renderRunPath();
269
- const initialHistoryLength = router.history.length;
270
-
271
- await user.click(await screen.findByRole('tab', {name: JOBS_TAB_NAME}));
218
+ await user.click(await screen.findByRole('link', {name: 'Source'}));
272
219
 
273
- await waitFor(() => expect(currentSearch(router).tab).toBe('jobs'));
274
- expect(router.history.length).toBe(initialHistoryLength + 1);
275
- expect(screen.getByRole('tab', {name: JOBS_TAB_NAME})).toHaveAttribute('aria-selected', 'true');
276
- expect(screen.getByRole('list', {name: 'Run jobs'})).toBeInTheDocument();
277
-
278
- await user.click(screen.getByRole('tab', {name: 'Summary'}));
220
+ await waitFor(() => expect(currentSearch(router).tab).toBe('source'));
221
+ expect(screen.getByRole('link', {name: 'Source'})).toHaveAttribute('aria-current', 'page');
279
222
 
223
+ await act(() => router.history.back());
280
224
  await waitFor(() => expect(currentSearch(router).tab).toBeUndefined());
281
- expect(router.history.length).toBe(initialHistoryLength + 2);
282
-
283
- await act(() => {
284
- router.history.back();
285
- });
286
- await waitFor(() => expect(currentSearch(router).tab).toBe('jobs'));
287
- expect(screen.getByRole('list', {name: 'Run jobs'})).toBeInTheDocument();
225
+ expect(screen.getByRole('region', {name: 'All jobs summary'})).toBeInTheDocument();
288
226
  });
289
227
 
290
- test('opens the Jobs tab and selects a job from the Summary graph', async () => {
228
+ test('navigates from the Summary graph to the job detail route', async () => {
291
229
  const user = userEvent.setup();
292
230
  configureApiClient({fetchImpl: createRunDetailFetch()});
293
231
 
294
232
  const {router} = renderRunPath();
295
- const initialHistoryLength = router.history.length;
296
233
 
297
234
  await user.click(await screen.findByRole('button', {name: 'deploy, Running'}));
298
235
 
299
- await waitFor(() => {
300
- expect(currentSearch(router)).toMatchObject({tab: 'jobs', job: DEPLOY_JOB_ID});
301
- });
302
- expect(router.history.length).toBe(initialHistoryLength + 1);
303
- expect(screen.getByRole('button', {name: 'deploy, Running'})).toHaveAttribute(
304
- 'aria-expanded',
305
- 'true',
306
- );
307
- expect(screen.getByRole('button', {name: 'deploy, Running'})).toHaveAttribute(
308
- 'aria-controls',
309
- `job-card-${DEPLOY_JOB_ID}`,
310
- );
311
- expect(screen.getByRole('button', {name: BUILD_JOB_BUTTON_NAME})).not.toHaveAttribute(
312
- 'aria-controls',
236
+ await waitFor(() =>
237
+ expect(router.state.location.pathname).toBe(
238
+ `/w/${PROJECT_TEST_WSLUG}/p/project/runs/${RUN_ID}/jobs/${DEPLOY_JOB_ID}`,
239
+ ),
313
240
  );
314
241
  });
315
242
 
316
- test('preserves an explicit Summary tab with a selected job', async () => {
317
- const user = userEvent.setup();
318
- configureApiClient({fetchImpl: createRunDetailFetch()});
319
-
320
- const {router} = renderRunPath(`?tab=jobs&job=${DEPLOY_JOB_ID}`);
321
-
322
- await screen.findByRole('button', {name: DEPLOY_JOB_BUTTON_NAME});
323
- await user.click(screen.getByRole('tab', {name: 'Summary'}));
324
-
325
- await waitFor(() => {
326
- expect(currentSearch(router)).toMatchObject({tab: 'summary', job: DEPLOY_JOB_ID});
327
- });
328
- expect(screen.getByRole('tab', {name: 'Summary'})).toHaveAttribute('aria-selected', 'true');
329
- expect(screen.queryByRole('list', {name: 'Run jobs'})).not.toBeInTheDocument();
330
- });
331
-
332
243
  test('keeps Summary active while keyboard navigation moves graph focus', async () => {
333
244
  const user = userEvent.setup();
334
245
  configureApiClient({fetchImpl: createRunDetailFetch()});
@@ -342,125 +253,27 @@ describe('WorkflowRunPages', () => {
342
253
 
343
254
  expect(deploy).toHaveFocus();
344
255
  expect(deploy).toHaveAttribute('aria-pressed', 'true');
345
- expect(screen.getByRole('tab', {name: 'Summary'})).toHaveAttribute('aria-selected', 'true');
346
- expect(screen.queryByRole('list', {name: 'Run jobs'})).not.toBeInTheDocument();
256
+ expect(screen.getByRole('link', {name: 'Summary'})).toHaveAttribute('aria-current', 'page');
347
257
  expect(currentSearch(router).job).toBeUndefined();
348
258
  });
349
259
 
350
- test('restores a deep-linked job and exact attempt after data loads', async () => {
351
- configureApiClient({fetchImpl: createRunDetailFetch()});
352
-
353
- renderRunPath(
354
- `?tab=jobs&job=${BUILD_JOB_ID}&step=${DEPLOY_STEP_ID}&stepAttempt=${DEPLOY_ATTEMPT_TWO_ID}`,
355
- );
356
-
357
- const deployJob = await screen.findByRole('button', {name: DEPLOY_JOB_BUTTON_NAME});
358
- const deployAttempt = await screen.findByRole('button', {
359
- name: 'deploy, Running, attempt 2',
360
- });
361
-
362
- expect(deployJob).toHaveAttribute('aria-expanded', 'true');
363
- expect(deployAttempt).toHaveAttribute('aria-expanded', 'true');
364
- expect(await screen.findByText('attempt two log')).toBeInTheDocument();
365
- });
366
-
367
- test('selecting a job writes job search state and clears stale step state', async () => {
260
+ test('selecting a job opens its dedicated page and clears stale step state', async () => {
368
261
  const user = userEvent.setup();
369
262
  configureApiClient({fetchImpl: createRunDetailFetch()});
370
- const {router} = renderRunPath(
371
- `?tab=jobs&step=${DEPLOY_STEP_ID}&stepAttempt=${DEPLOY_ATTEMPT_TWO_ID}&runAttempt=1`,
372
- );
263
+ const {router} = renderRunPath(`?runAttempt=1`);
373
264
  const initialHistoryLength = router.history.length;
374
265
 
375
266
  await user.click(await screen.findByRole('button', {name: BUILD_JOB_BUTTON_NAME}));
376
267
 
377
- await waitFor(() => {
378
- expect(currentSearch(router)).toMatchObject({job: BUILD_JOB_ID});
379
- });
380
- expect(currentSearch(router).tab).toBe('jobs');
381
- expect(router.history.length).toBe(initialHistoryLength);
382
- expect(currentSearch(router).step).toBeUndefined();
383
- expect(currentSearch(router).stepAttempt).toBeUndefined();
384
- expect(screen.getByRole('button', {name: 'checkout, Succeeded, attempt 1'})).toHaveAttribute(
385
- 'aria-expanded',
386
- 'false',
387
- );
388
- });
389
-
390
- test('selecting an attempt writes job, step, and attempt search state', async () => {
391
- const user = userEvent.setup();
392
- configureApiClient({fetchImpl: createRunDetailFetch()});
393
- const {router} = renderRunPath(`?tab=jobs&job=${DEPLOY_JOB_ID}`);
394
-
395
- await user.click(await screen.findByRole('button', {name: 'deploy, Running, attempt 2'}));
396
-
397
- await waitFor(() => {
398
- expect(currentSearch(router)).toMatchObject({
399
- job: DEPLOY_JOB_ID,
400
- step: DEPLOY_STEP_ID,
401
- stepAttempt: DEPLOY_ATTEMPT_TWO_ID,
402
- });
403
- });
404
- });
405
-
406
- test('selecting a listening execution writes job execution search state and scopes step selection', async () => {
407
- const user = userEvent.setup();
408
- configureApiClient({
409
- fetchImpl: createRunDetailFetch({details: {[RUN_ID]: retryRunDetailDto()}}),
410
- });
411
- const {router} = renderRunPath(`?tab=jobs&job=${DEPLOY_JOB_ID}`);
412
-
413
- await user.click(
414
- await screen.findByRole('button', {
415
- name: 'Switch job execution, currently execution 2: deploy review #2',
416
- }),
417
- );
418
- await user.click(
419
- screen.getByRole('menuitem', {
420
- name: EXECUTION_ONE_MENU_ITEM,
421
- }),
422
- );
423
-
424
- await waitFor(() => {
425
- expect(currentSearch(router)).toMatchObject({
426
- job: DEPLOY_JOB_ID,
427
- jobExecution: DEPLOY_EXECUTION_ONE_ID,
428
- });
429
- });
430
- expect(screen.getByRole('button', {name: 'deploy, Failed, attempt 1'})).toBeInTheDocument();
431
- expect(screen.getAllByText('deploy review #1')).not.toHaveLength(0);
432
-
433
- await user.click(screen.getByRole('button', {name: 'deploy, Failed, attempt 1'}));
434
-
435
- await waitFor(() => {
436
- expect(currentSearch(router)).toMatchObject({
437
- job: DEPLOY_JOB_ID,
438
- jobExecution: DEPLOY_EXECUTION_ONE_ID,
439
- step: DEPLOY_STEP_ID,
440
- stepAttempt: DEPLOY_ATTEMPT_ONE_ID,
441
- });
442
- });
443
- expect(await screen.findByText('attempt one log')).toBeInTheDocument();
444
- });
445
-
446
- test('collapsing an attempt removes step and attempt while preserving job', async () => {
447
- const user = userEvent.setup();
448
- configureApiClient({fetchImpl: createRunDetailFetch()});
449
- const {router} = renderRunPath(
450
- `?tab=jobs&step=${DEPLOY_STEP_ID}&stepAttempt=${DEPLOY_ATTEMPT_TWO_ID}`,
268
+ await waitFor(() =>
269
+ expect(router.state.location.pathname).toBe(
270
+ `/w/${PROJECT_TEST_WSLUG}/p/project/runs/${RUN_ID}/jobs/${BUILD_JOB_ID}`,
271
+ ),
451
272
  );
452
-
453
- const deployAttempt = await screen.findByRole('button', {
454
- name: 'deploy, Running, attempt 2',
455
- });
456
- await user.click(deployAttempt);
457
-
458
- await waitFor(() => {
459
- expect(currentSearch(router)).toMatchObject({job: DEPLOY_JOB_ID});
460
- });
273
+ expect(router.history.length).toBe(initialHistoryLength + 1);
274
+ expect(currentSearch(router).runAttempt).toBe('1');
461
275
  expect(currentSearch(router).step).toBeUndefined();
462
276
  expect(currentSearch(router).stepAttempt).toBeUndefined();
463
- expect(deployAttempt).toHaveAttribute('aria-expanded', 'false');
464
277
  });
465
278
 
466
279
  test('run list links navigate to detail and back restores list filters', async () => {
@@ -502,21 +315,30 @@ describe('WorkflowRunPages', () => {
502
315
  function renderRunsPath(search = '') {
503
316
  return renderProjectPage(
504
317
  `/w/${PROJECT_TEST_WSLUG}/p/project/runs${search}`,
505
- ({workflowRunId, search}) =>
506
- workflowRunId ? (
318
+ ({workflowRunId, jobId, search}) =>
319
+ workflowRunId && jobId ? (
320
+ <WorkflowJobDetailPage
321
+ projectId={PROJECT_ID}
322
+ workspaceSlug={PROJECT_TEST_WSLUG}
323
+ projectSlug="project"
324
+ workflowRunId={workflowRunId}
325
+ jobId={jobId}
326
+ search={search as WorkflowJobSearch}
327
+ />
328
+ ) : workflowRunId ? (
507
329
  <WorkflowRunDetailPage
508
330
  projectId={PROJECT_ID}
509
331
  workspaceSlug={PROJECT_TEST_WSLUG}
510
332
  projectSlug="project"
511
333
  workflowRunId={workflowRunId}
512
- search={search}
334
+ search={search as WorkflowRunsSearch}
513
335
  />
514
336
  ) : (
515
337
  <WorkflowRunsPage
516
338
  projectId={PROJECT_ID}
517
339
  workspaceSlug={PROJECT_TEST_WSLUG}
518
340
  projectSlug="project"
519
- search={search}
341
+ search={search as WorkflowRunsSearch}
520
342
  />
521
343
  ),
522
344
  );
@@ -525,15 +347,25 @@ function renderRunsPath(search = '') {
525
347
  function renderRunPath(search = '') {
526
348
  return renderProjectPage(
527
349
  `/w/${PROJECT_TEST_WSLUG}/p/project/runs/${RUN_ID}${search}`,
528
- ({workflowRunId, search}) => (
529
- <WorkflowRunDetailPage
530
- projectId={PROJECT_ID}
531
- workspaceSlug={PROJECT_TEST_WSLUG}
532
- projectSlug="project"
533
- workflowRunId={workflowRunId}
534
- search={search}
535
- />
536
- ),
350
+ ({workflowRunId, jobId, search}) =>
351
+ workflowRunId && jobId ? (
352
+ <WorkflowJobDetailPage
353
+ projectId={PROJECT_ID}
354
+ workspaceSlug={PROJECT_TEST_WSLUG}
355
+ projectSlug="project"
356
+ workflowRunId={workflowRunId}
357
+ jobId={jobId}
358
+ search={search as WorkflowJobSearch}
359
+ />
360
+ ) : (
361
+ <WorkflowRunDetailPage
362
+ projectId={PROJECT_ID}
363
+ workspaceSlug={PROJECT_TEST_WSLUG}
364
+ projectSlug="project"
365
+ workflowRunId={workflowRunId}
366
+ search={search as WorkflowRunsSearch}
367
+ />
368
+ ),
537
369
  );
538
370
  }
539
371
 
@@ -731,93 +563,6 @@ function defaultRunDetailDto(
731
563
  });
732
564
  }
733
565
 
734
- function retryRunDetailDto(): WorkflowRunDetailResponseDto {
735
- return defaultRunDetailDto({
736
- jobs: [
737
- workflowJobDto({
738
- id: BUILD_JOB_ID,
739
- run_attempt_id: RUN_ID,
740
- name: 'build',
741
- status: 'succeeded',
742
- steps: [
743
- workflowStepDto({
744
- id: BUILD_STEP_ID,
745
- name: 'checkout',
746
- status: 'succeeded',
747
- attempts: [
748
- workflowStepAttemptDto({
749
- id: BUILD_ATTEMPT_ID,
750
- step_id: BUILD_STEP_ID,
751
- status: 'succeeded',
752
- }),
753
- ],
754
- }),
755
- ],
756
- }),
757
- workflowJobDto({
758
- id: DEPLOY_JOB_ID,
759
- run_attempt_id: RUN_ID,
760
- name: 'deploy',
761
- mode: 'listening',
762
- status: 'running',
763
- listener_status: 'listening',
764
- position: 1,
765
- dependencies: ['build'],
766
- job_executions: [
767
- workflowJobExecutionDto({
768
- id: DEPLOY_EXECUTION_ONE_ID,
769
- job_id: DEPLOY_JOB_ID,
770
- sequence: 1,
771
- name: 'deploy review #1',
772
- status: 'failed',
773
- started_at: '2026-05-07T01:01:00.000Z',
774
- finished_at: '2026-05-07T01:02:00.000Z',
775
- steps: [
776
- workflowStepDto({
777
- id: DEPLOY_STEP_ID,
778
- name: 'deploy',
779
- status: 'failed',
780
- attempts: [
781
- workflowStepAttemptDto({
782
- id: DEPLOY_ATTEMPT_ONE_ID,
783
- step_id: DEPLOY_STEP_ID,
784
- status: 'failed',
785
- exit_code: 1,
786
- }),
787
- ],
788
- }),
789
- ],
790
- }),
791
- workflowJobExecutionDto({
792
- id: DEPLOY_EXECUTION_TWO_ID,
793
- job_id: DEPLOY_JOB_ID,
794
- sequence: 2,
795
- name: 'deploy review #2',
796
- status: 'running',
797
- started_at: '2026-05-07T01:03:00.000Z',
798
- steps: [
799
- workflowStepDto({
800
- id: DEPLOY_RETRY_STEP_ID,
801
- name: 'deploy retry',
802
- status: 'running',
803
- attempts: [
804
- workflowStepAttemptDto({
805
- id: DEPLOY_ATTEMPT_TWO_ID,
806
- step_id: DEPLOY_RETRY_STEP_ID,
807
- status: 'running',
808
- exit_code: null,
809
- finished_at: null,
810
- }),
811
- ],
812
- }),
813
- ],
814
- }),
815
- ],
816
- }),
817
- ],
818
- });
819
- }
820
-
821
566
  function currentSearch({state}: ReturnType<typeof renderRunPath>['router']) {
822
567
  return state.location.search as Record<string, unknown>;
823
568
  }