Skip to content

Commit 7bf7a05

Browse files
committed
test(core); increase test coverage
1 parent 6d0d077 commit 7bf7a05

File tree

17 files changed

+942
-7
lines changed

17 files changed

+942
-7
lines changed

packages/core/jest.config.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const config = {
6262
// able to remove this one I start testing the documentation site
6363
"!<rootDir>/src/**/index.ts",
6464
"!<rootDir>/src/**/types.ts",
65+
"!<rootDir>/src/test-utils/data-testid.ts",
6566
// it's hard to verify these since they probably only occur when trying to
6667
// use react-dom/server in jsdom?
6768
"!<rootDir>/src/test-utils/polyfills/TextEncoder.ts",

packages/core/src/form/__tests__/Slider.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,5 +1206,30 @@ describe("Slider", () => {
12061206
expect(marks).toHaveLength(4);
12071207
expect(sliderContainer).toMatchSnapshot();
12081208
});
1209+
1210+
it("should allow for marks to have custom labels", () => {
1211+
rmdRender(
1212+
<SingleThumbTest
1213+
marks={[
1214+
{ value: 0, label: "First" },
1215+
{ value: 20, label: "Second" },
1216+
{ value: 50, label: "Third" },
1217+
{ value: 100, label: "Fourth" },
1218+
]}
1219+
options={{ step: 10 }}
1220+
getMarkProps={({ value }) => ({
1221+
"data-testid": `mark-${value}`,
1222+
})}
1223+
/>
1224+
);
1225+
const { sliderContainer } = getSliderTestElements({ name: "Slider" });
1226+
const marks = screen.getAllByTestId(/^mark-/);
1227+
expect(marks).toHaveLength(4);
1228+
expect(() => screen.getByText("First")).not.toThrow();
1229+
expect(() => screen.getByText("Second")).not.toThrow();
1230+
expect(() => screen.getByText("Third")).not.toThrow();
1231+
expect(() => screen.getByText("Fourth")).not.toThrow();
1232+
expect(sliderContainer).toMatchSnapshot();
1233+
});
12091234
});
12101235
});

packages/core/src/form/__tests__/__snapshots__/Slider.tsx.snap

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,83 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`Slider discrete sliders should allow for marks to have custom labels 1`] = `
4+
<div
5+
class="rmd-slider-container rmd-slider-container--h rmd-slider-container--pad-left rmd-slider-container--pad-right"
6+
>
7+
<span
8+
class="rmd-slider-track rmd-slider-track--hoverable rmd-slider-track--h rmd-slider-track--h1"
9+
style="--rmd-slider-offset-1: 50%;"
10+
>
11+
<span
12+
aria-label="Slider"
13+
aria-valuemax="100"
14+
aria-valuemin="0"
15+
aria-valuenow="50"
16+
class="rmd-slider-thumb rmd-slider-thumb--h rmd-slider-thumb--h1"
17+
id="slider-:r1d:"
18+
role="slider"
19+
tabindex="0"
20+
/>
21+
<input
22+
aria-hidden="true"
23+
aria-label="Slider"
24+
class="rmd-slider-thumb__input"
25+
id="slider-:r1d:-value"
26+
max="100"
27+
min="0"
28+
step="10"
29+
tabindex="-1"
30+
type="range"
31+
value="50"
32+
/>
33+
<span
34+
class="rmd-slider-mark rmd-slider-mark--active rmd-slider-mark--h"
35+
data-testid="mark-0"
36+
style="--rmd-slider-mark-offset: 0%;"
37+
/>
38+
<span
39+
class="rmd-typography rmd-typography--body-2 rmd-slider-mark-label rmd-slider-mark-label--h"
40+
style="--rmd-slider-mark-offset: 0%;"
41+
>
42+
First
43+
</span>
44+
<span
45+
class="rmd-slider-mark rmd-slider-mark--active rmd-slider-mark--h"
46+
data-testid="mark-20"
47+
style="--rmd-slider-mark-offset: 20%;"
48+
/>
49+
<span
50+
class="rmd-typography rmd-typography--body-2 rmd-slider-mark-label rmd-slider-mark-label--h"
51+
style="--rmd-slider-mark-offset: 20%;"
52+
>
53+
Second
54+
</span>
55+
<span
56+
class="rmd-slider-mark rmd-slider-mark--inactive rmd-slider-mark--h"
57+
data-testid="mark-50"
58+
style="--rmd-slider-mark-offset: 50%;"
59+
/>
60+
<span
61+
class="rmd-typography rmd-typography--body-2 rmd-slider-mark-label rmd-slider-mark-label--h"
62+
style="--rmd-slider-mark-offset: 50%;"
63+
>
64+
Third
65+
</span>
66+
<span
67+
class="rmd-slider-mark rmd-slider-mark--inactive rmd-slider-mark--h"
68+
data-testid="mark-100"
69+
style="--rmd-slider-mark-offset: 100%;"
70+
/>
71+
<span
72+
class="rmd-typography rmd-typography--body-2 rmd-slider-mark-label rmd-slider-mark-label--h"
73+
style="--rmd-slider-mark-offset: 100%;"
74+
>
75+
Fourth
76+
</span>
77+
</span>
78+
</div>
79+
`;
80+
381
exports[`Slider discrete sliders should support manually defining the marks using a list 1`] = `
482
<div
583
class="rmd-slider-container rmd-slider-container--h rmd-slider-container--pad-left rmd-slider-container--pad-right"
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
import { describe, expect, it } from "@jest/globals";
2+
import { type ReactElement } from "react";
3+
4+
import { Button } from "../../button/Button.js";
5+
import {
6+
fireEvent,
7+
render,
8+
screen,
9+
userEvent,
10+
} from "../../test-utils/index.js";
11+
import { Form } from "../Form.js";
12+
import { TextField } from "../TextField.js";
13+
import {
14+
type NumberFieldHookOptions,
15+
useNumberField,
16+
} from "../useNumberField.js";
17+
18+
type TestProps = Partial<NumberFieldHookOptions>;
19+
20+
function Test({ name = "field", ...options }: TestProps): ReactElement {
21+
const { fieldProps, value } = useNumberField({
22+
...options,
23+
name,
24+
});
25+
26+
return (
27+
<Form name="form">
28+
<span data-testid="value">{value}</span>
29+
<TextField
30+
label="Field"
31+
{...fieldProps}
32+
containerProps={{
33+
"data-testid": "container",
34+
}}
35+
messageContainerProps={{
36+
"data-testid": "message-container",
37+
}}
38+
messageProps={{
39+
"data-testid": "message",
40+
...fieldProps.messageProps,
41+
counterProps: {
42+
"data-testid": "counter",
43+
},
44+
messageProps: {
45+
"data-testid": "p",
46+
},
47+
}}
48+
/>
49+
<Button type="reset">Reset</Button>
50+
<Button type="submit">Submit</Button>
51+
</Form>
52+
);
53+
}
54+
55+
const setup = (options?: TestProps) => {
56+
const user = userEvent.setup();
57+
const { rerender } = render(<Test {...options} />);
58+
const field = screen.getByRole<HTMLInputElement>("spinbutton", {
59+
name: "Field",
60+
});
61+
const message = screen.getByTestId("message");
62+
const container = screen.getByTestId("container");
63+
const messageContainer = screen.getByTestId("message-container");
64+
const form = screen.getByRole("form");
65+
const submit = screen.getByRole("button", { name: "Submit" });
66+
const reset = screen.getByRole("button", { name: "Reset" });
67+
const value = screen.getByTestId("value");
68+
return {
69+
user,
70+
rerender: (options?: TestProps) => rerender(<Test {...options} />),
71+
form,
72+
value,
73+
submit,
74+
reset,
75+
field,
76+
container,
77+
message,
78+
messageContainer,
79+
};
80+
};
81+
82+
describe("useNumberField", () => {
83+
it("should default to rendering with an empty FormMessage and linking it to the field", () => {
84+
const { field, message } = setup();
85+
expect(message.id).not.toBe(null);
86+
expect(field).toHaveAttribute("aria-describedby", message.id);
87+
});
88+
89+
it("should apply all of the constraint props to the TextField", () => {
90+
const { field, rerender } = setup({
91+
min: 0,
92+
max: 10,
93+
required: true,
94+
});
95+
96+
expect(field).toHaveAttribute("min", "0");
97+
expect(field).toHaveAttribute("max", "10");
98+
expect(field).toHaveAttribute("required");
99+
100+
rerender({
101+
min: 20,
102+
max: 25,
103+
});
104+
expect(field).toHaveAttribute("min", "20");
105+
expect(field).toHaveAttribute("max", "25");
106+
expect(field).not.toBeRequired();
107+
});
108+
109+
it("should render the helpText if when there is no error text", async () => {
110+
const { user, field, container, message } = setup({
111+
helpText: "Help Text",
112+
min: 0,
113+
max: 5,
114+
});
115+
116+
expect(message).toHaveTextContent("Help Text");
117+
expect(container).not.toHaveClass("rmd-text-field-container--error");
118+
expect(message).not.toHaveClass("rmd-error-color");
119+
120+
await user.type(field, "8");
121+
expect(message).toHaveTextContent("Constraints not satisfied");
122+
expect(container).toHaveClass("rmd-text-field-container--error");
123+
expect(message).toHaveClass("rmd-error-color");
124+
});
125+
126+
it("should be able to render any number", async () => {
127+
const { user, field } = setup();
128+
129+
await user.type(field, "8");
130+
expect(field).toHaveValue(8);
131+
});
132+
133+
it("should automatically update the value to be within the range by default when blurred", async () => {
134+
const { user, field } = setup({
135+
min: 0,
136+
max: 5,
137+
});
138+
139+
await user.type(field, "8");
140+
expect(field).toHaveValue(8);
141+
142+
fireEvent.blur(field);
143+
expect(field).toHaveValue(5);
144+
});
145+
146+
it("should enforce the defaultValue when blurred if it was provided", async () => {
147+
const { user, field } = setup({
148+
defaultValue: 3,
149+
});
150+
151+
await user.clear(field);
152+
expect(field).toHaveValue(null);
153+
154+
fireEvent.blur(field);
155+
expect(field).toHaveValue(3);
156+
});
157+
158+
it("should default to the min value on blur if the number is out or range", async () => {
159+
const { user, field } = setup({
160+
defaultValue: 3,
161+
min: 0,
162+
});
163+
164+
await user.clear(field);
165+
expect(field).toHaveValue(null);
166+
167+
fireEvent.blur(field);
168+
expect(field).toHaveValue(0);
169+
});
170+
171+
it("should allow bad input if the user types some invalid number", async () => {
172+
const { user, field } = setup();
173+
174+
await user.type(field, "--0");
175+
expect(field).toHaveValue(null);
176+
177+
fireEvent.blur(field);
178+
expect(field).toHaveValue(null);
179+
});
180+
181+
it("should support updating the saved value on blur only", async () => {
182+
const { user, field, value } = setup({
183+
updateValue: "blur",
184+
});
185+
186+
expect(value).toBeEmptyDOMElement();
187+
188+
await user.type(field, "3");
189+
expect(value).toBeEmptyDOMElement();
190+
191+
fireEvent.blur(field);
192+
expect(value).toHaveTextContent("3");
193+
});
194+
195+
describe("error behavior", () => {
196+
it("should not update the error state on change if `validationType` is an empty array", async () => {
197+
const { user, field, container, message } = setup({
198+
min: 10,
199+
validationType: [],
200+
});
201+
202+
expect(container).not.toHaveClass("rmd-text-field-container--error");
203+
expect(message).not.toHaveClass("rmd-error-color");
204+
205+
await user.type(field, "1");
206+
expect(field).toHaveValue(1);
207+
expect(container).not.toHaveClass("rmd-text-field-container--error");
208+
expect(message).not.toHaveClass("rmd-error-color");
209+
210+
fireEvent.blur(field);
211+
expect(container).not.toHaveClass("rmd-text-field-container--error");
212+
expect(message).not.toHaveClass("rmd-error-color");
213+
expect(field).toHaveValue(10);
214+
});
215+
216+
it("should render an error icon as the rightAddon when there is an error", async () => {
217+
const { field, user } = setup({
218+
errorIcon: <span data-testid="error-icon" />,
219+
max: 3,
220+
disableMaxLength: true,
221+
});
222+
223+
expect(() => screen.getByTestId("error-icon")).toThrow();
224+
225+
await user.type(field, "5");
226+
expect(() => screen.getByTestId("error-icon")).not.toThrow();
227+
228+
await user.clear(field);
229+
expect(() => screen.getByTestId("error-icon")).toThrow();
230+
});
231+
});
232+
233+
describe("form behavior", () => {
234+
it("should enable the error state if a form is submitted while required", async () => {
235+
const { user, submit, container } = setup({
236+
required: true,
237+
});
238+
expect(container).not.toHaveClass("rmd-text-field-container--error");
239+
240+
await user.click(submit);
241+
expect(container).toHaveClass("rmd-text-field-container--error");
242+
});
243+
244+
it("should automatically reset to the initial state if the form reset event is fired", async () => {
245+
const { user, field, reset, container } = setup({
246+
required: true,
247+
});
248+
expect(container).not.toHaveClass("rmd-text-field-container--error");
249+
250+
await user.type(field, "10");
251+
expect(field).toHaveValue(10);
252+
expect(container).not.toHaveClass("rmd-text-field-container--error");
253+
254+
await user.clear(field);
255+
expect(field).toHaveValue(null);
256+
expect(container).toHaveClass("rmd-text-field-container--error");
257+
258+
await user.click(reset);
259+
expect(field).toHaveValue(null);
260+
expect(container).not.toHaveClass("rmd-text-field-container--error");
261+
});
262+
});
263+
});

0 commit comments

Comments
 (0)