Skip to content

Commit e1b0250

Browse files
authored
Merge pull request #58 from coder/brett/add-preview-tests
chore: add tests for Preview.tsx
2 parents b74f740 + 6c99768 commit e1b0250

File tree

5 files changed

+1066
-9
lines changed

5 files changed

+1066
-9
lines changed

src/client/App.test.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import type { EditorProps } from "@monaco-editor/react";
22
import { TooltipProvider } from "@radix-ui/react-tooltip";
3-
import {
4-
cleanup,
5-
render,
6-
screen,
7-
waitFor,
8-
} from "@testing-library/react";
3+
import { cleanup, render, screen, waitFor } from "@testing-library/react";
94
import type { FC } from "react";
105
import { createBrowserRouter, RouterProvider } from "react-router";
116
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

src/client/Preview.test.tsx

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
import {
2+
cleanup,
3+
findByTestId,
4+
getByLabelText,
5+
queryAllByLabelText,
6+
render,
7+
} from "@testing-library/react";
8+
import type { FC } from "react";
9+
import { PanelGroup } from "react-resizable-panels";
10+
import { createBrowserRouter, RouterProvider } from "react-router";
11+
import { afterEach, describe, expect, it } from "vitest";
12+
import { TooltipProvider } from "@/client/components/Tooltip";
13+
import { EditorProvider } from "@/client/contexts/editor";
14+
import { ThemeProvider } from "@/client/contexts/theme";
15+
import { Preview } from "@/client/Preview";
16+
import type { ParameterWithSource } from "@/gen/types";
17+
import {
18+
defaultExampleParameters,
19+
defaultExampleParamterValues,
20+
formTypesExampleParameters,
21+
} from "@/test-data/preview";
22+
import { mockUsers } from "@/user";
23+
24+
type TestAppProps = {
25+
parameters: ParameterWithSource[];
26+
parameterValues?: Record<string, string>;
27+
};
28+
29+
const TestPreview: FC<TestAppProps> = ({
30+
parameters,
31+
parameterValues = {},
32+
}) => {
33+
return (
34+
<PanelGroup direction="horizontal">
35+
<Preview
36+
wasmLoadState="loaded"
37+
isDebouncing={false}
38+
onDownloadOutput={() => null}
39+
parameterValues={parameterValues}
40+
setParameterValues={() => null}
41+
output={null}
42+
parameters={parameters}
43+
onReset={() => null}
44+
users={mockUsers}
45+
currentUser={mockUsers[0]}
46+
setUsers={() => null}
47+
/>
48+
</PanelGroup>
49+
);
50+
};
51+
52+
const router = (parameters: ParameterWithSource[], parameterValues = {}) =>
53+
createBrowserRouter([
54+
{
55+
path: "*",
56+
Component: () => (
57+
<TestPreview
58+
parameters={parameters}
59+
parameterValues={parameterValues}
60+
/>
61+
),
62+
},
63+
]);
64+
65+
const TestApp: FC<TestAppProps> = ({ parameters, parameterValues }) => {
66+
return (
67+
<ThemeProvider>
68+
<TooltipProvider>
69+
<EditorProvider>
70+
<RouterProvider router={router(parameters, parameterValues)} />
71+
</EditorProvider>
72+
</TooltipProvider>
73+
</ThemeProvider>
74+
);
75+
};
76+
77+
describe("Preview - Rendering", () => {
78+
afterEach(() => {
79+
cleanup();
80+
});
81+
82+
it("should render the empty state when no parameters are present", async () => {
83+
const page = render(<TestApp parameters={[]} />);
84+
85+
expect(findByTestId(page.container, "preview-empty-state"));
86+
});
87+
88+
it("should render the default example as expected", async () => {
89+
const page = render(<TestApp parameters={defaultExampleParameters} />);
90+
91+
getByLabelText(page.container, "Pick your next parameter!*Required");
92+
93+
getByLabelText(
94+
page.container,
95+
"Use imaginary experimental features?Immutable",
96+
);
97+
});
98+
99+
it("should render the form type example as with the expected default values", async () => {
100+
const page = render(<TestApp parameters={formTypesExampleParameters} />);
101+
102+
const formTypeSelects = queryAllByLabelText(
103+
page.container,
104+
"How do you want to format the options of the next parameter?Immutable",
105+
);
106+
expect(formTypeSelects).toHaveLength(4);
107+
108+
expect(formTypeSelects[0].innerText).toBe("Radio Selector");
109+
110+
const radioGroup = getByLabelText(
111+
page.container,
112+
"Selecting a single value from a list of options.Immutable",
113+
);
114+
expect(getByLabelText(radioGroup, "Alpha").getAttribute("data-state")).toBe(
115+
"checked",
116+
);
117+
expect(getByLabelText(radioGroup, "Bravo").getAttribute("data-state")).toBe(
118+
"unchecked",
119+
);
120+
expect(
121+
getByLabelText(radioGroup, "Charlie").getAttribute("data-state"),
122+
).toBe("unchecked");
123+
124+
expect(formTypeSelects[1].innerText).toBe("Raw input");
125+
126+
const numberInput = getByLabelText(
127+
page.container,
128+
"What is your favorite number?Immutable",
129+
);
130+
expect(numberInput).toBeInstanceOf(HTMLInputElement);
131+
expect(numberInput.getAttribute("value")).toBe("7");
132+
133+
expect(formTypeSelects[2].innerText).toBe("Radio");
134+
135+
const doYouAgreeWithMeRadio = getByLabelText(
136+
page.container,
137+
"Do you agree with me?Immutable",
138+
);
139+
expect(
140+
getByLabelText(doYouAgreeWithMeRadio, "Yes").getAttribute("data-state"),
141+
).toBe("checked");
142+
expect(
143+
getByLabelText(doYouAgreeWithMeRadio, "No").getAttribute("data-state"),
144+
).toBe("unchecked");
145+
146+
expect(formTypeSelects[3].innerText).toBe("Multi-Select");
147+
148+
const checkbox = getByLabelText(
149+
page.container,
150+
"Did you like this demo?Immutable",
151+
);
152+
expect(checkbox.getAttribute("data-state")).toBe("unchecked");
153+
});
154+
155+
it("should render form elements with set values", async () => {
156+
const page = render(
157+
<TestApp
158+
parameters={formTypesExampleParameters}
159+
parameterValues={defaultExampleParamterValues}
160+
/>
161+
);
162+
163+
const formTypeSelects = queryAllByLabelText(
164+
page.container,
165+
"How do you want to format the options of the next parameter?Immutable",
166+
);
167+
168+
expect(formTypeSelects[0].textContent).toBe("Radio Selector");
169+
170+
const singleRadioGroup = getByLabelText(
171+
page.container,
172+
"Selecting a single value from a list of options.Immutable"
173+
);
174+
expect(getByLabelText(singleRadioGroup, "Alpha").getAttribute("data-state")).toBe(
175+
"unchecked"
176+
);
177+
expect(getByLabelText(singleRadioGroup, "Bravo").getAttribute("data-state")).toBe(
178+
"checked"
179+
);
180+
expect(getByLabelText(singleRadioGroup, "Charlie").getAttribute("data-state")).toBe(
181+
"unchecked"
182+
);
183+
184+
const numberInput = getByLabelText(
185+
page.container,
186+
"What is your favorite number?Immutable"
187+
) as HTMLInputElement;
188+
expect(numberInput.value).toBe("48");
189+
190+
const booleanRadioGroup = getByLabelText(
191+
page.container,
192+
"Do you agree with me?Immutable"
193+
);
194+
expect(getByLabelText(booleanRadioGroup, "Yes").getAttribute("data-state")).toBe(
195+
"unchecked"
196+
);
197+
expect(getByLabelText(booleanRadioGroup, "No").getAttribute("data-state")).toBe(
198+
"checked"
199+
);
200+
201+
const likeItCheckbox = getByLabelText(
202+
page.container,
203+
"Did you like this demo?Immutable"
204+
);
205+
expect(likeItCheckbox.getAttribute("data-state")).toBe("checked");
206+
});
207+
});

src/client/Preview.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,10 @@ export const Preview: FC<PreviewProps> = ({
235235

236236
const PreviewEmptyState = () => {
237237
return (
238-
<div className="flex flex-col items-center justify-center gap-3">
238+
<div
239+
className="flex flex-col items-center justify-center gap-3"
240+
data-testid="preview-empty-state"
241+
>
239242
<div className="flex items-center justify-center rounded-[6px] bg-highlight-sky p-2">
240243
<ActivityIcon className="text-content-invert" width={24} height={24} />
241244
</div>

src/client/components/DynamicParameter.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,15 +151,18 @@ const ParameterLabel: FC<ParameterLabelProps> = ({
151151
<div className="flex items-start gap-2">
152152
<div className="flex w-full flex-col gap-1">
153153
<Label
154+
id={`${id}-label`}
154155
htmlFor={id}
155156
className="flex flex-wrap gap-2 font-medium text-content-primary text-sm"
157+
role="button"
158+
onClick={onGoToDefinition}
156159
>
157-
<button className="flex hover:underline" onClick={onGoToDefinition}>
160+
<span className="flex hover:underline" onClick={onGoToDefinition}>
158161
{displayName}
159162
{parameter.required && (
160163
<span className="text-content-destructive">*</span>
161164
)}
162-
</button>
165+
</span>
163166
{!parameter.mutable && (
164167
<TooltipProvider delayDuration={100}>
165168
<Tooltip>
@@ -496,6 +499,7 @@ const ParameterField: FC<ParameterFieldProps> = ({
496499
disabled={disabled}
497500
value={`data-${value}`}
498501
className="relative"
502+
aria-labelledby={`${id}-label`}
499503
>
500504
{parameter.options.map((option) => (
501505
<div

0 commit comments

Comments
 (0)