Skip to content

Commit 35c7f1e

Browse files
fix(ui-v2): handle named pending states (#21931)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Alex S <alex.s@prefect.io>
1 parent 4a1d52c commit 35c7f1e

10 files changed

Lines changed: 162 additions & 3 deletions

File tree

ui-v2/src/api/flow-runs/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export const STATE_NAMES = [
3232
"AwaitingConcurrencySlot",
3333
"Pending",
3434
"Submitting",
35+
"InfrastructurePending",
3536
"Paused",
3637
"Suspended",
3738
"Running",
@@ -59,6 +60,7 @@ export const STATE_NAME_TO_TYPE: Record<StateName, StateType> = {
5960
AwaitingConcurrencySlot: "SCHEDULED",
6061
Pending: "PENDING",
6162
Submitting: "PENDING",
63+
InfrastructurePending: "PENDING",
6264
Paused: "PAUSED",
6365
Suspended: "PAUSED",
6466
Running: "RUNNING",

ui-v2/src/api/flow-runs/state-utilities.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, expect, it } from "vitest";
22
import {
33
isPausedState,
4+
isPendingLikeState,
45
isRunningState,
56
isStuckState,
67
isTerminalState,
@@ -88,6 +89,24 @@ describe("state-utilities", () => {
8889
});
8990
});
9091

92+
describe("isPendingLikeState", () => {
93+
it.each([
94+
"Pending",
95+
"Submitting",
96+
"InfrastructurePending",
97+
] as const)("returns true for PENDING/%s", (stateName) => {
98+
expect(isPendingLikeState("PENDING", stateName)).toBe(true);
99+
});
100+
101+
it("returns true for Scheduled", () => {
102+
expect(isPendingLikeState("SCHEDULED", "Scheduled")).toBe(true);
103+
});
104+
105+
it("returns false for AwaitingRetry", () => {
106+
expect(isPendingLikeState("SCHEDULED", "AwaitingRetry")).toBe(false);
107+
});
108+
});
109+
91110
describe("constants", () => {
92111
it("STUCK_STATES contains expected values", () => {
93112
expect(STUCK_STATES).toEqual([

ui-v2/src/components/automations/automations-wizard/trigger-step/state-multi-select.test.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ describe("StateMultiSelect", () => {
2626
expect(screen.getByText("Any state")).toBeVisible();
2727
});
2828

29-
it("displays all 18 state options in the dropdown", async () => {
29+
it("displays all state options in the dropdown", async () => {
3030
const user = userEvent.setup();
3131

3232
render(<StateMultiSelect selectedStates={[]} onStateChange={vi.fn()} />);
@@ -177,6 +177,8 @@ describe("StateMultiSelect", () => {
177177
const stateNamesToTest = [
178178
"Late",
179179
"AwaitingRetry",
180+
"Submitting",
181+
"InfrastructurePending",
180182
"Suspended",
181183
"Retrying",
182184
"Cached",

ui-v2/src/components/automations/trigger-details/trigger-details-flow-run-state.test.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,36 @@ describe("TriggerDetailsFlowRunState", () => {
6969
expect(screen.getByText("Failed")).toBeInTheDocument();
7070
});
7171

72+
it("renders named pending state badges", () => {
73+
render(
74+
<TriggerDetailsFlowRunState
75+
flowIds={[]}
76+
tags={[]}
77+
posture="Reactive"
78+
states={["Pending", "Submitting", "InfrastructurePending"]}
79+
/>,
80+
{ wrapper: QueryWrapper },
81+
);
82+
83+
expect(screen.getByText("Pending")).toBeInTheDocument();
84+
expect(screen.getByText("Submitting")).toBeInTheDocument();
85+
expect(screen.getByText("InfrastructurePending")).toBeInTheDocument();
86+
});
87+
88+
it("does not render badges for inherited object keys", () => {
89+
render(
90+
<TriggerDetailsFlowRunState
91+
flowIds={[]}
92+
tags={[]}
93+
posture="Reactive"
94+
states={["constructor"]}
95+
/>,
96+
{ wrapper: QueryWrapper },
97+
);
98+
99+
expect(screen.queryByText("constructor")).not.toBeInTheDocument();
100+
});
101+
72102
it("renders 'stays in' for Proactive posture", () => {
73103
render(
74104
<TriggerDetailsFlowRunState

ui-v2/src/components/automations/trigger-details/trigger-details-flow-run-state.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { STATE_NAME_TO_TYPE, type StateName } from "@/api/flow-runs/constants";
12
import type { components } from "@/api/prefect";
23
import { FlowLink } from "@/components/flows/flow-link";
34
import { StateBadge } from "@/components/ui/state-badge";
@@ -24,6 +25,10 @@ type TriggerDetailsFlowRunStateProps = {
2425
* and maps them to the uppercase StateType values expected by StateBadge.
2526
*/
2627
function mapStateNameToStateType(stateName: string): StateType | null {
28+
if (Object.hasOwn(STATE_NAME_TO_TYPE, stateName)) {
29+
return STATE_NAME_TO_TYPE[stateName as StateName];
30+
}
31+
2732
const normalizedName = stateName.toUpperCase();
2833

2934
const validStateTypes: StateType[] = [
@@ -83,7 +88,15 @@ export const TriggerDetailsFlowRunState = ({
8388
states.map((state) => {
8489
const stateType = mapStateNameToStateType(state);
8590
if (stateType) {
86-
return <StateBadge key={state} type={stateType} />;
91+
return (
92+
<StateBadge
93+
key={state}
94+
type={stateType}
95+
name={
96+
Object.hasOwn(STATE_NAME_TO_TYPE, state) ? state : undefined
97+
}
98+
/>
99+
);
87100
}
88101
return null;
89102
})

ui-v2/src/components/flow-runs/flow-run-details-page/flow-run-details-page.test.tsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,35 @@ describe("FlowRunDetailsPage", () => {
427427
expect(screen.queryByTestId("flow-run-graph")).not.toBeInTheDocument();
428428
});
429429

430+
it.each([
431+
"Submitting",
432+
"InfrastructurePending",
433+
] as const)("hides FlowRunGraph when flow run state is %s", async (stateName) => {
434+
const pendingFlowRun = createFakeFlowRun({
435+
name: `${stateName}-flow-run`,
436+
state_type: "PENDING",
437+
state_name: stateName,
438+
state: createFakeState({
439+
type: "PENDING",
440+
name: stateName,
441+
}),
442+
});
443+
444+
server.use(
445+
http.get(buildApiUrl("/flow_runs/:id"), () => {
446+
return HttpResponse.json(pendingFlowRun);
447+
}),
448+
);
449+
450+
renderFlowRunDetailsPage();
451+
452+
await waitFor(() => {
453+
expect(screen.getByText(`${stateName}-flow-run`)).toBeInTheDocument();
454+
});
455+
456+
expect(screen.queryByTestId("flow-run-graph")).not.toBeInTheDocument();
457+
});
458+
430459
it("hides FlowRunGraph and execution tabs when flow run state is SCHEDULED", async () => {
431460
const scheduledFlowRun = createFakeFlowRun({
432461
name: "scheduled-flow-run",
@@ -525,6 +554,45 @@ describe("FlowRunDetailsPage", () => {
525554
).not.toBeInTheDocument();
526555
});
527556

557+
it.each([
558+
"Submitting",
559+
"InfrastructurePending",
560+
] as const)("hides execution tabs for %s flow runs", async (stateName) => {
561+
const pendingFlowRun = createFakeFlowRun({
562+
name: `${stateName}-tabs-flow-run`,
563+
state_type: "PENDING",
564+
state_name: stateName,
565+
state: createFakeState({
566+
type: "PENDING",
567+
name: stateName,
568+
}),
569+
});
570+
571+
server.use(
572+
http.get(buildApiUrl("/flow_runs/:id"), () => {
573+
return HttpResponse.json(pendingFlowRun);
574+
}),
575+
);
576+
577+
renderFlowRunDetailsPage();
578+
579+
await waitFor(() => {
580+
expect(
581+
screen.getByText(`${stateName}-tabs-flow-run`),
582+
).toBeInTheDocument();
583+
});
584+
585+
expect(
586+
screen.queryByRole("tab", { name: "Task Runs" }),
587+
).not.toBeInTheDocument();
588+
expect(
589+
screen.queryByRole("tab", { name: "Subflow Runs" }),
590+
).not.toBeInTheDocument();
591+
expect(
592+
screen.queryByRole("tab", { name: "Artifacts" }),
593+
).not.toBeInTheDocument();
594+
});
595+
528596
it("renders FlowRunGraph for FAILED flow runs", async () => {
529597
const failedFlowRun = createFakeFlowRun({
530598
name: "failed-flow-run",

ui-v2/src/components/flow-runs/flow-runs-list/flow-runs-filters/state-filter.test.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,27 @@ describe("FlowRunsDataTable -- StateFilter", () => {
3333
).toBeVisible();
3434
});
3535

36+
it("includes all pending state names in All except scheduled", async () => {
37+
// Setup
38+
const user = userEvent.setup();
39+
render(<TestStateFilter />);
40+
// Test
41+
await user.click(screen.getByRole("button", { name: /all run states/i }));
42+
await user.click(
43+
screen.getByRole("menuitem", { name: /all except scheduled/i }),
44+
);
45+
46+
// Assert
47+
expect(screen.getByRole("menuitem", { name: /submitting/i })).toBeVisible();
48+
expect(
49+
screen.getByRole("menuitem", { name: /infrastructurepending/i }),
50+
).toBeVisible();
51+
await user.keyboard("{Escape}");
52+
expect(
53+
screen.getByRole("button", { name: /all except scheduled/i }),
54+
).toBeVisible();
55+
});
56+
3657
it("selects All run states option", async () => {
3758
// Setup
3859
const user = userEvent.setup();

ui-v2/src/components/flow-runs/flow-runs-list/flow-runs-filters/state-filters.constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const FLOW_RUN_STATES = [
88
"AwaitingConcurrencySlot",
99
"Pending",
1010
"Submitting",
11+
"InfrastructurePending",
1112
"Paused",
1213
"Suspended",
1314
"Running",
@@ -34,6 +35,7 @@ export const FLOW_RUN_STATES_NO_SCHEDULED = FLOW_RUN_STATES.filter(
3435
export const FLOW_RUN_STATES_WITHOUT_SCHEDULED: FlowRunState[] = [
3536
"Pending",
3637
"Submitting",
38+
"InfrastructurePending",
3739
"Paused",
3840
"Suspended",
3941
"Running",
@@ -53,6 +55,7 @@ export const FLOW_RUN_STATES_MAP = {
5355
AwaitingConcurrencySlot: "SCHEDULED",
5456
Pending: "PENDING",
5557
Submitting: "PENDING",
58+
InfrastructurePending: "PENDING",
5659
Paused: "PAUSED",
5760
Suspended: "PAUSED",
5861
Running: "RUNNING",

ui-v2/src/components/task-runs/task-run-state-filter.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ describe("TaskRunStateFilter", () => {
182182
"AwaitingConcurrencySlot",
183183
"Pending",
184184
"Submitting",
185+
"InfrastructurePending",
185186
"Paused",
186187
"Suspended",
187188
"Running",

ui-v2/src/components/ui/state-badge/state-badge.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const badgesByState: Record<components["schemas"]["StateType"], string[]> = {
66
COMPLETED: ["Completed"],
77
FAILED: ["Failed"],
88
RUNNING: ["Running"],
9-
PENDING: ["Pending"],
9+
PENDING: ["Pending", "Submitting", "InfrastructurePending"],
1010
PAUSED: ["Paused"],
1111
CANCELLED: ["Cancelled"],
1212
CANCELLING: ["Cancelling"],

0 commit comments

Comments
 (0)