Skip to content

Commit 823ff29

Browse files
committed
add tests
1 parent 4c00a2f commit 823ff29

File tree

2 files changed

+666
-0
lines changed

2 files changed

+666
-0
lines changed
Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
/* eslint-disable jsx-a11y/label-has-associated-control */
2+
import * as React from 'react';
3+
import { axe } from 'vitest-axe';
4+
import type { RenderResult } from '@testing-library/react';
5+
import { cleanup, render, fireEvent, screen, waitFor, act } from '@testing-library/react';
6+
import * as RadioGroup from '.';
7+
import { afterEach, describe, it, beforeEach, vi, expect } from 'vitest';
8+
9+
const RADIO_ROLE = 'radio';
10+
const INDICATOR_TEST_ID = 'radio-indicator';
11+
12+
global.ResizeObserver = class ResizeObserver {
13+
cb: any;
14+
constructor(cb: any) {
15+
this.cb = cb;
16+
}
17+
observe() {
18+
this.cb([{ borderBoxSize: { inlineSize: 0, blockSize: 0 } }]);
19+
}
20+
unobserve() {}
21+
disconnect() {}
22+
};
23+
24+
describe('RadioGroup (legacy API)', () => {
25+
afterEach(cleanup);
26+
27+
describe('given a default RadioGroup', () => {
28+
let rendered: RenderResult;
29+
beforeEach(() => {
30+
rendered = render(
31+
<>
32+
<span id="label">Items</span>
33+
<RadioGroup.Root aria-labelledby="label">
34+
<label>
35+
<RadioGroup.Item value="1">
36+
<RadioGroup.Indicator data-testid={INDICATOR_TEST_ID} />
37+
</RadioGroup.Item>
38+
<span>One</span>
39+
</label>
40+
<label>
41+
<RadioGroup.Item value="2">
42+
<RadioGroup.Indicator data-testid={INDICATOR_TEST_ID} />
43+
</RadioGroup.Item>
44+
<span>Two</span>
45+
</label>
46+
<label>
47+
<RadioGroup.Item value="3">
48+
<RadioGroup.Indicator data-testid={INDICATOR_TEST_ID} />
49+
</RadioGroup.Item>
50+
<span>Three</span>
51+
</label>
52+
</RadioGroup.Root>
53+
</>
54+
);
55+
});
56+
57+
it('should have no accessibility violations', async () => {
58+
expect(await axe(rendered.container)).toHaveNoViolations();
59+
});
60+
61+
it('should toggle the indicator when clicked', async () => {
62+
const radioOne = screen.getByRole(RADIO_ROLE, { name: 'One' });
63+
const radioTwo = screen.getByRole(RADIO_ROLE, { name: 'Two' });
64+
await act(async () => fireEvent.click(radioOne));
65+
66+
const indicator = radioOne.querySelector(`[data-testid="${INDICATOR_TEST_ID}"]`);
67+
expect(indicator).toBeVisible();
68+
69+
await act(async () => fireEvent.click(radioTwo));
70+
expect(indicator).not.toBeInTheDocument();
71+
});
72+
});
73+
74+
describe('given a disabled RadioGroup', () => {
75+
let rendered: RenderResult;
76+
beforeEach(() => {
77+
rendered = render(
78+
<>
79+
<span id="label">Items</span>
80+
<RadioGroup.Root aria-labelledby="label" disabled>
81+
<label>
82+
<RadioGroup.Item value="1">
83+
<RadioGroup.Indicator data-testid={INDICATOR_TEST_ID} />
84+
</RadioGroup.Item>
85+
<span>One</span>
86+
</label>
87+
<label>
88+
<RadioGroup.Item value="2">
89+
<RadioGroup.Indicator data-testid={INDICATOR_TEST_ID} />
90+
</RadioGroup.Item>
91+
<span>Two</span>
92+
</label>
93+
<label>
94+
<RadioGroup.Item value="3">
95+
<RadioGroup.Indicator data-testid={INDICATOR_TEST_ID} />
96+
</RadioGroup.Item>
97+
<span>Three</span>
98+
</label>
99+
</RadioGroup.Root>
100+
</>
101+
);
102+
});
103+
104+
it('should have no accessibility violations', async () => {
105+
expect(await axe(rendered.container)).toHaveNoViolations();
106+
});
107+
108+
it('should not toggle the indicator when clicked', async () => {
109+
const radioOne = screen.getByRole(RADIO_ROLE, { name: 'One' });
110+
const radioTwo = screen.getByRole(RADIO_ROLE, { name: 'Two' });
111+
await act(async () => fireEvent.click(radioOne));
112+
113+
const indicator = radioOne.querySelector(`[data-testid="${INDICATOR_TEST_ID}"]`);
114+
expect(indicator).not.toBeInTheDocument();
115+
116+
await act(async () => fireEvent.click(radioTwo));
117+
expect(indicator).not.toBeInTheDocument();
118+
});
119+
});
120+
121+
describe('given an uncontrolled RadioGroup with a disabled Radio', () => {
122+
let rendered: RenderResult;
123+
beforeEach(() => {
124+
rendered = render(
125+
<>
126+
<span id="label">Items</span>
127+
<RadioGroup.Root aria-labelledby="label">
128+
<label>
129+
<RadioGroup.Item value="1">
130+
<RadioGroup.Indicator data-testid={INDICATOR_TEST_ID} />
131+
</RadioGroup.Item>
132+
<span>One</span>
133+
</label>
134+
<label>
135+
<RadioGroup.Item value="2" disabled>
136+
<RadioGroup.Indicator data-testid={INDICATOR_TEST_ID} />
137+
</RadioGroup.Item>
138+
<span>Two</span>
139+
</label>
140+
<label>
141+
<RadioGroup.Item value="3">
142+
<RadioGroup.Indicator data-testid={INDICATOR_TEST_ID} />
143+
</RadioGroup.Item>
144+
<span>Three</span>
145+
</label>
146+
</RadioGroup.Root>
147+
</>
148+
);
149+
});
150+
151+
it('should have no accessibility violations', async () => {
152+
expect(await axe(rendered.container)).toHaveNoViolations();
153+
});
154+
155+
it('should not toggle the disabled indicator when clicked', async () => {
156+
const radioOne = screen.getByRole(RADIO_ROLE, { name: 'One' });
157+
const radioTwo = screen.getByRole(RADIO_ROLE, { name: 'Two' });
158+
await act(async () => fireEvent.click(radioOne));
159+
160+
const indicator = radioOne.querySelector(`[data-testid="${INDICATOR_TEST_ID}"]`);
161+
expect(indicator).toBeVisible();
162+
163+
await act(async () => fireEvent.click(radioTwo));
164+
expect(indicator).toBeVisible();
165+
const indicatorTwo = radioTwo.querySelector(`[data-testid="${INDICATOR_TEST_ID}"]`);
166+
expect(indicatorTwo).not.toBeInTheDocument();
167+
});
168+
});
169+
170+
describe('given an uncontrolled RadioGroup with `checked` Radio', () => {
171+
const onValueChange = vi.fn();
172+
let rendered: RenderResult;
173+
174+
beforeEach(() => {
175+
rendered = render(
176+
<>
177+
<span id="label">Items</span>
178+
<RadioGroup.Root aria-labelledby="label" defaultValue="1" onValueChange={onValueChange}>
179+
<label>
180+
<RadioGroup.Item value="1">
181+
<RadioGroup.Indicator data-testid={INDICATOR_TEST_ID} />
182+
</RadioGroup.Item>
183+
<span>One</span>
184+
</label>
185+
<label>
186+
<RadioGroup.Item value="2">
187+
<RadioGroup.Indicator data-testid={INDICATOR_TEST_ID} />
188+
</RadioGroup.Item>
189+
<span>Two</span>
190+
</label>
191+
<label>
192+
<RadioGroup.Item value="3">
193+
<RadioGroup.Indicator data-testid={INDICATOR_TEST_ID} />
194+
</RadioGroup.Item>
195+
<span>Three</span>
196+
</label>
197+
</RadioGroup.Root>
198+
</>
199+
);
200+
});
201+
202+
afterEach(cleanup);
203+
204+
it('should have no accessibility violations', async () => {
205+
expect(await axe(rendered.container)).toHaveNoViolations();
206+
});
207+
208+
it('should render a visible indicator', () => {
209+
const radioOne = screen.getByRole(RADIO_ROLE, { name: 'One' });
210+
const indicator = radioOne.querySelector(`[data-testid="${INDICATOR_TEST_ID}"]`);
211+
expect(indicator).toBeVisible();
212+
});
213+
214+
it('should toggle the indicator when clicked', async () => {
215+
const radioOne = screen.getByRole(RADIO_ROLE, { name: 'One' });
216+
const radioTwo = screen.getByRole(RADIO_ROLE, { name: 'Two' });
217+
218+
const indicator = radioOne.querySelector(`[data-testid="${INDICATOR_TEST_ID}"]`);
219+
expect(indicator).toBeVisible();
220+
221+
await act(async () => fireEvent.click(radioTwo));
222+
expect(indicator).not.toBeInTheDocument();
223+
});
224+
225+
it('should call `onValueChange` prop', () => {
226+
const radioTwo = screen.getByRole(RADIO_ROLE, { name: 'Two' });
227+
fireEvent.click(radioTwo);
228+
waitFor(() => {
229+
expect(onValueChange).toHaveBeenCalledWith('2');
230+
});
231+
});
232+
});
233+
234+
describe('given a controlled RadioGroup', () => {
235+
const onValueChange = vi.fn();
236+
let rendered: RenderResult;
237+
238+
function ControlledRadioGroup() {
239+
const [value, setValue] = React.useState<null | string>(null);
240+
const [blockToggle, setBlockToggle] = React.useState(false);
241+
return (
242+
<div>
243+
<RadioGroup.Root
244+
value={value}
245+
onValueChange={(value) => {
246+
onValueChange(value);
247+
if (!blockToggle) {
248+
setValue(value);
249+
}
250+
}}
251+
>
252+
<label>
253+
<RadioGroup.Item value="1">
254+
<RadioGroup.Indicator data-testid={INDICATOR_TEST_ID} />
255+
</RadioGroup.Item>
256+
<span>One</span>
257+
</label>
258+
<label>
259+
<RadioGroup.Item value="2">
260+
<RadioGroup.Indicator data-testid={INDICATOR_TEST_ID} />
261+
</RadioGroup.Item>
262+
<span>Two</span>
263+
</label>
264+
<label>
265+
<RadioGroup.Item value="3">
266+
<RadioGroup.Indicator data-testid={INDICATOR_TEST_ID} />
267+
</RadioGroup.Item>
268+
<span>Three</span>
269+
</label>
270+
</RadioGroup.Root>
271+
<button type="button" onClick={() => setValue(null)}>
272+
Clear selection
273+
</button>
274+
<button type="button" onClick={() => setBlockToggle((prev) => !prev)}>
275+
{blockToggle ? 'Unblock' : 'Block'} radio
276+
</button>
277+
</div>
278+
);
279+
}
280+
281+
beforeEach(() => {
282+
rendered = render(<ControlledRadioGroup />);
283+
});
284+
285+
it('should have no accessibility violations', async () => {
286+
expect(await axe(rendered.container)).toHaveNoViolations();
287+
});
288+
289+
it('should toggle the indicator when clicked', async () => {
290+
const radioOne = screen.getByRole(RADIO_ROLE, { name: 'One' });
291+
const radioTwo = screen.getByRole(RADIO_ROLE, { name: 'Two' });
292+
await act(async () => fireEvent.click(radioOne));
293+
294+
const indicator = radioOne.querySelector(`[data-testid="${INDICATOR_TEST_ID}"]`);
295+
expect(indicator).toBeVisible();
296+
297+
await act(async () => fireEvent.click(radioTwo));
298+
expect(indicator).not.toBeInTheDocument();
299+
});
300+
301+
it('should call `onValueChange` prop', async () => {
302+
const radioTwo = screen.getByRole(RADIO_ROLE, { name: 'Two' });
303+
await act(async () => fireEvent.click(radioTwo));
304+
waitFor(() => {
305+
expect(onValueChange).toHaveBeenCalledWith('2');
306+
});
307+
});
308+
309+
it('should not toggle unless state is updated', async () => {
310+
const radioOne = screen.getByRole(RADIO_ROLE, { name: 'One' });
311+
const blocker = screen.getByText('Block radio');
312+
await act(async () => fireEvent.click(blocker));
313+
await act(async () => fireEvent.click(radioOne));
314+
const indicator = radioOne.querySelector(`[data-testid="${INDICATOR_TEST_ID}"]`);
315+
expect(indicator).not.toBeInTheDocument();
316+
});
317+
});
318+
});

0 commit comments

Comments
 (0)