Skip to content

Commit a54d1cc

Browse files
test(frontend): add tests for BaseWorkflowRelay
test(frontend): update mock values
1 parent 39d7349 commit a54d1cc

File tree

4 files changed

+180
-6
lines changed

4 files changed

+180
-6
lines changed

frontend/dashboard/src/mocks/responses/workflows/WorkflowsListViewQueryResponse.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ export const workflowsListViewQueryResponse = {
1414
},
1515
templateRef: "conditional-steps",
1616
parameters: {},
17+
creator: {
18+
creatorId: "abc12345",
19+
},
1720
status: {
1821
__typename: "WorkflowSucceededStatus",
1922
startTime: "2025-08-22T10:35:22+00:00",
@@ -104,6 +107,9 @@ export const workflowsListViewQueryResponse = {
104107
},
105108
templateRef: "conditional-steps",
106109
parameters: {},
110+
creator: {
111+
creatorId: "abc12345",
112+
},
107113
status: {
108114
__typename: "WorkflowSucceededStatus",
109115
startTime: "2025-08-05T17:06:00+00:00",
@@ -194,6 +200,9 @@ export const workflowsListViewQueryResponse = {
194200
},
195201
templateRef: "conditional-steps",
196202
parameters: {},
203+
creator: {
204+
creatorId: "abc12345",
205+
},
197206
status: {
198207
__typename: "WorkflowSucceededStatus",
199208
startTime: "2025-08-19T09:45:55+00:00",
@@ -284,6 +293,9 @@ export const workflowsListViewQueryResponse = {
284293
},
285294
templateRef: "mount-tmpdir",
286295
parameters: {},
296+
creator: {
297+
creatorId: "abc12345",
298+
},
287299
status: {
288300
__typename: "WorkflowSucceededStatus",
289301
startTime: "2025-08-19T09:49:41+00:00",
@@ -342,6 +354,9 @@ export const workflowsListViewQueryResponse = {
342354
},
343355
templateRef: "notebook",
344356
parameters: {},
357+
creator: {
358+
creatorId: "abc12345",
359+
},
345360
status: {
346361
__typename: "WorkflowSucceededStatus",
347362
startTime: "2025-08-19T11:14:17+00:00",
@@ -405,6 +420,9 @@ export const workflowsListViewQueryResponse = {
405420
},
406421
templateRef: "notebook",
407422
parameters: {},
423+
creator: {
424+
creatorId: "abc12345",
425+
},
408426
status: {
409427
__typename: "WorkflowSucceededStatus",
410428
startTime: "2025-08-21T18:12:48+00:00",
@@ -468,6 +486,9 @@ export const workflowsListViewQueryResponse = {
468486
},
469487
templateRef: "ptycho-tomo-job",
470488
parameters: {},
489+
creator: {
490+
creatorId: "abc12345",
491+
},
471492
status: {
472493
__typename: "WorkflowSucceededStatus",
473494
startTime: "2025-08-22T10:27:18+00:00",
@@ -607,6 +628,9 @@ export const workflowsListViewQueryResponse = {
607628
},
608629
templateRef: "ptycho-tomo-job",
609630
parameters: {},
631+
creator: {
632+
creatorId: "abc12345",
633+
},
610634
status: {
611635
__typename: "WorkflowSucceededStatus",
612636
startTime: "2025-08-22T10:27:22+00:00",
@@ -746,6 +770,9 @@ export const workflowsListViewQueryResponse = {
746770
},
747771
templateRef: "ptycho-tomo-job",
748772
parameters: {},
773+
creator: {
774+
creatorId: "abc12345",
775+
},
749776
status: {
750777
__typename: "WorkflowSucceededStatus",
751778
startTime: "2025-08-22T10:18:00+00:00",
@@ -889,6 +916,9 @@ export const workflowsListViewQueryResponse = {
889916
step: "1",
890917
start: "0",
891918
},
919+
creator: {
920+
creatorId: "abc12345",
921+
},
892922
status: {
893923
__typename: "WorkflowSucceededStatus",
894924
startTime: "2025-08-19T09:47:26+00:00",
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { render, screen } from "@testing-library/react";
2+
import { useState } from "react";
3+
import "@testing-library/jest-dom";
4+
import BaseWorkflowRelay from "relay-workflows-lib/lib/components/BaseWorkflowRelay";
5+
import { workflowsListViewQueryResponse } from "dashboard/src/mocks/responses/workflows/WorkflowsListViewQueryResponse";
6+
import { BaseWorkflowRelayFragment$key } from "relay-workflows-lib/lib/components/__generated__/BaseWorkflowRelayFragment.graphql";
7+
import { RelayEnvironmentProvider } from "react-relay";
8+
import { getRelayEnvironment } from "dashboard/src/RelayEnvironment";
9+
import { beforeAll } from "vitest";
10+
import { server } from "relay-workflows-lib/tests/mocks/browser.ts";
11+
import userEvent from "@testing-library/user-event";
12+
import { mockReactFlow } from "../mocks/mockReactFlow";
13+
14+
vi.mock("react-relay", async () => {
15+
const actual = await import("react-relay");
16+
17+
return {
18+
...actual,
19+
useFragment: vi.fn(() => workflowsListViewQueryResponse.workflows.nodes[0]),
20+
RelayEnvironmentProvider: actual.RelayEnvironmentProvider,
21+
};
22+
});
23+
24+
vi.mock("react-router-dom", () => ({
25+
useNavigate: vi.fn(),
26+
useLocation: vi.fn(),
27+
useParams: vi.fn(() => ({ workflowName: "conditional-steps" })),
28+
useSearchParams: vi.fn(() => [new URLSearchParams(""), vi.fn()]),
29+
NavLink: () => <div></div>,
30+
}));
31+
32+
const ExpansionHandler = () => {
33+
const [isExpanded, setIsExpanded] = useState(false);
34+
const mockFragmentRef = {} as BaseWorkflowRelayFragment$key;
35+
return (
36+
<BaseWorkflowRelay
37+
fragmentRef={mockFragmentRef}
38+
expanded={isExpanded}
39+
onChange={() => {
40+
setIsExpanded(!isExpanded);
41+
}}
42+
/>
43+
);
44+
};
45+
46+
describe("BaseWorkflowRelay", () => {
47+
const user = userEvent.setup();
48+
49+
beforeAll(() => {
50+
server.listen();
51+
});
52+
beforeEach(async () => {
53+
mockReactFlow();
54+
const environment = await getRelayEnvironment();
55+
56+
render(
57+
<RelayEnvironmentProvider environment={environment}>
58+
<ExpansionHandler />
59+
</RelayEnvironmentProvider>,
60+
);
61+
});
62+
afterAll(() => {
63+
server.close();
64+
});
65+
66+
it("renders a workflow card with name and creator", async () => {
67+
expect(
68+
await screen.findByText("conditional-steps-first"),
69+
).toBeInTheDocument();
70+
expect(screen.getByText("Creator: abc12345")).toBeInTheDocument();
71+
});
72+
73+
it("should display flow box nodes when expanded", async () => {
74+
const accordionButton = await screen.findByRole("button", {
75+
name: /conditional-steps-first/i,
76+
});
77+
expect(accordionButton).toHaveAttribute("aria-expanded", "false");
78+
expect(screen.queryByText("even")).not.toBeVisible();
79+
80+
await user.click(accordionButton);
81+
82+
expect(accordionButton).toHaveAttribute("aria-expanded", "true");
83+
expect(screen.getByText("even")).toBeVisible();
84+
});
85+
});
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Taken from https://reactflow.dev/learn/advanced-use/testing
2+
3+
class ResizeObserver {
4+
callback: globalThis.ResizeObserverCallback;
5+
6+
constructor(callback: globalThis.ResizeObserverCallback) {
7+
this.callback = callback;
8+
}
9+
10+
observe(target: Element) {
11+
this.callback([{ target } as globalThis.ResizeObserverEntry], this);
12+
}
13+
14+
unobserve() {}
15+
16+
disconnect() {}
17+
}
18+
19+
class DOMMatrixReadOnly {
20+
m22: number;
21+
constructor(transform: string) {
22+
const scale = transform.match(/scale\(([1-9.])\)/)?.[1];
23+
this.m22 = scale !== undefined ? +scale : 1;
24+
}
25+
}
26+
27+
let init = false;
28+
29+
export const mockReactFlow = () => {
30+
if (init) return;
31+
init = true;
32+
33+
global.ResizeObserver = ResizeObserver;
34+
35+
// @ts-expect-error: using partial mock of DOMMatrixReadOnly; full implementation requires many more methods
36+
global.DOMMatrixReadOnly = DOMMatrixReadOnly;
37+
38+
Object.defineProperties(global.HTMLElement.prototype, {
39+
offsetHeight: {
40+
get(this: HTMLElement) {
41+
return parseFloat(this.style.height) || 300;
42+
},
43+
},
44+
offsetWidth: {
45+
get(this: HTMLElement) {
46+
return parseFloat(this.style.width) || 300;
47+
},
48+
},
49+
});
50+
51+
(global.SVGElement as unknown as { getBBox: () => DOMRect }).getBBox =
52+
() => ({
53+
x: 0,
54+
y: 0,
55+
width: 0,
56+
height: 0,
57+
top: 0,
58+
left: 0,
59+
bottom: 0,
60+
right: 0,
61+
toJSON: () => {},
62+
});
63+
};

frontend/workflows-lib/lib/components/workflow/WorkflowAccordion.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,12 @@ const WorkflowAccordion: React.FC<WorkflowProps> = ({
3636
workflow,
3737
children,
3838
workflowLink = false,
39-
expanded = false,
39+
expanded,
4040
onChange,
4141
retriggerComponent,
4242
}) => {
4343
return (
44-
<Accordion
45-
key={workflow.name}
46-
defaultExpanded={expanded}
47-
onChange={onChange}
48-
>
44+
<Accordion key={workflow.name} expanded={expanded} onChange={onChange}>
4945
<AccordionSummary expandIcon={<ArrowDropDownIcon />}>
5046
<Box sx={{ display: "flex", flexBasis: 0, flexGrow: 5, gap: 2 }}>
5147
{getWorkflowStatusIcon(workflow.status)}

0 commit comments

Comments
 (0)