Skip to content

Commit 224b1e6

Browse files
ashna000ariellalgilmore2nikhiltom
authored
test(SideNav): #17491 increased unit test coverage (#17869)
* test(SideNav): #17491 increased unit test coverage * test(SideNav): #17491 increased unit test coverage * test(SideNav): increased coverage * Update packages/react/src/components/UIShell/__tests__/SideNav-test.js * Update packages/react/src/components/UIShell/__tests__/SideNav-test.js * Update packages/react/src/components/UIShell/__tests__/SideNav-test.js * fix(test): format --------- Co-authored-by: Ariella Gilmore <ariellalgilmore@gmail.com> Co-authored-by: Nikhil Tomar <63502271+2nikhiltom@users.noreply.github.com>
1 parent 5c44a20 commit 224b1e6

File tree

3 files changed

+283
-3
lines changed

3 files changed

+283
-3
lines changed

.all-contributorsrc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1721,6 +1721,13 @@
17211721
"name": "Zoë Gathercole",
17221722
"avatar_url": "https://avatars.githubusercontent.com/u/56911544?v=4",
17231723
"profile": "https://github.com/Zoe-Gathercole",
1724+
"contributions": ["code"]
1725+
},
1726+
{
1727+
"login": "ashna000",
1728+
"name": "Ashna Thomas",
1729+
"avatar_url": "https://avatars.githubusercontent.com/u/12691034?s=96&v=4",
1730+
"profile": "https://github.com/ashna000",
17241731
"contributions": [
17251732
"code"
17261733
]

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,9 @@ check out our [Contributing Guide](/.github/CONTRIBUTING.md) and our
321321
<td align="center"><a href="https://github.com/Zoe-Gathercole"><img src="https://avatars.githubusercontent.com/u/56911544?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zoë Gathercole</b></sub></a><br /><a href="https://github.com/carbon-design-system/carbon/commits?author=Zoe-Gathercole" title="Code">💻</a></td>
322322
<td align="center"><a href="https://github.com/a88zach"><img src="https://avatars.githubusercontent.com/u/1724822?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zach Tindall</b></sub></a><br /><a href="https://github.com/carbon-design-system/carbon/commits?author=a88zach" title="Code">💻</a></td>
323323
</tr>
324+
<tr>
325+
<td align="center"><a href="https://github.com/ashna000"><img src="https://avatars.githubusercontent.com/u/12691034?s=96&v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ashna Thomas</b></sub></a><br /><a href="https://github.com/carbon-design-system/carbon/commits?author=ashna000" title="Code">💻</a></td>
326+
</tr>
324327
</table>
325328

326329
<!-- markdownlint-restore -->

packages/react/src/components/UIShell/__tests__/SideNav-test.js

Lines changed: 273 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import { render, screen } from '@testing-library/react';
8+
import { render, screen, fireEvent } from '@testing-library/react';
99
import React from 'react';
1010
import SideNav from '../SideNav';
1111

@@ -15,8 +15,8 @@ Object.defineProperty(window, 'matchMedia', {
1515
matches: false,
1616
media: query,
1717
onchange: null,
18-
addListener: jest.fn(), // deprecated
19-
removeListener: jest.fn(), // deprecated
18+
addListener: jest.fn(),
19+
removeListener: jest.fn(),
2020
addEventListener: jest.fn(),
2121
removeEventListener: jest.fn(),
2222
dispatchEvent: jest.fn(),
@@ -36,6 +36,11 @@ describe('SideNav', () => {
3636
expect(container.childNodes.length).toBe(2);
3737
});
3838

39+
it('should not render an overlay if `isFixedNav` is true', () => {
40+
const { container } = render(<SideNav aria-label="test" isFixedNav />);
41+
expect(container.childNodes.length).toBe(1);
42+
});
43+
3944
it('should toggle the overlay-active class when `expanded` is true', () => {
4045
const { container } = render(<SideNav aria-label="test" expanded />);
4146
expect(container.firstChild).toHaveClass('cds--side-nav__overlay-active');
@@ -64,4 +69,269 @@ describe('SideNav', () => {
6469
render(<SideNav aria-label="test" ref={ref} />);
6570
expect(ref).toHaveBeenCalledWith(screen.getByRole('navigation'));
6671
});
72+
73+
it('should call onOverlayClick when overlay is clicked', () => {
74+
const onOverlayClick = jest.fn();
75+
const { container } = render(
76+
<SideNav aria-label="test" expanded onOverlayClick={onOverlayClick} />
77+
);
78+
const overlay = container.firstChild;
79+
fireEvent.click(overlay);
80+
expect(onOverlayClick).toHaveBeenCalledTimes(1);
81+
});
82+
83+
it('should not add focus or mouse listeners when disabled', () => {
84+
const onToggle = jest.fn();
85+
render(
86+
<SideNav
87+
aria-label="test"
88+
addFocusListeners={false}
89+
addMouseListeners={false}
90+
onToggle={onToggle}
91+
/>
92+
);
93+
const sideNav = screen.getByRole('navigation');
94+
fireEvent.focus(sideNav);
95+
fireEvent.mouseEnter(sideNav);
96+
expect(onToggle).not.toHaveBeenCalled();
97+
});
98+
99+
it('should handle keyboard events like Escape', () => {
100+
const onToggle = jest.fn();
101+
render(
102+
<SideNav
103+
aria-label="test"
104+
expanded
105+
onToggle={onToggle}
106+
href="#main-content"
107+
/>
108+
);
109+
const sideNav = screen.getByRole('navigation');
110+
fireEvent.keyDown(sideNav, { key: 'Escape', keyCode: 27 });
111+
expect(onToggle).toHaveBeenCalledWith(expect.anything(), false);
112+
});
113+
114+
it('should correctly handle `isRail` when true', () => {
115+
render(<SideNav aria-label="test" isRail />);
116+
const sideNav = screen.getByRole('navigation');
117+
expect(sideNav).toHaveClass('cds--side-nav--rail');
118+
});
119+
120+
it('should correctly handle `isRail` when false', () => {
121+
render(<SideNav aria-label="test" isRail={false} />);
122+
const sideNav = screen.getByRole('navigation');
123+
expect(sideNav).not.toHaveClass('cds--side-nav--rail');
124+
});
125+
126+
it('should toggle the expanded state when uncontrolled', () => {
127+
const { container } = render(<SideNav aria-label="test" />);
128+
const sideNav = screen.getByRole('navigation');
129+
fireEvent.focus(sideNav);
130+
expect(container.firstChild).toHaveClass('cds--side-nav__overlay');
131+
});
132+
133+
it('should handle children without throwing error', () => {
134+
const { container } = render(
135+
<SideNav aria-label="test">
136+
<div>No Errors Here!</div>
137+
</SideNav>
138+
);
139+
expect(container).toBeInTheDocument();
140+
});
141+
142+
it('should pass isSideNavExpanded to Carbon SideNav children', () => {
143+
const TestChild = React.forwardRef(({ isSideNavExpanded }, ref) => (
144+
<div data-testid="child" ref={ref}>
145+
{isSideNavExpanded ? 'Expanded' : 'Collapsed'}
146+
</div>
147+
));
148+
render(
149+
<SideNav aria-label="test" expanded>
150+
<TestChild />
151+
</SideNav>
152+
);
153+
expect(screen.getByTestId('child')).toHaveTextContent('Collapsed');
154+
});
155+
156+
it('should not pass isSideNavExpanded to non-CarbsideNav children', () => {
157+
const NonCarbonChild = () => <div data-testid="non-carbon-child" />;
158+
render(
159+
<SideNav aria-label="test" expanded>
160+
<NonCarbonChild />
161+
</SideNav>
162+
);
163+
expect(screen.getByTestId('non-carbon-child')).toBeInTheDocument();
164+
});
165+
166+
it('should pass isSideNavExpanded correctly based on controlled state', () => {
167+
const TestChild = React.forwardRef(({ isSideNavExpanded }, ref) => (
168+
<div data-testid="child" ref={ref}>
169+
{isSideNavExpanded ? 'Expanded' : 'Collapsed'}
170+
</div>
171+
));
172+
const { rerender } = render(
173+
<SideNav aria-label="test" expanded>
174+
<TestChild />
175+
</SideNav>
176+
);
177+
expect(screen.getByTestId('child')).toHaveTextContent('Collapsed');
178+
rerender(
179+
<SideNav aria-label="test">
180+
<TestChild />
181+
</SideNav>
182+
);
183+
expect(screen.getByTestId('child')).toHaveTextContent('Collapsed');
184+
});
185+
186+
it('should call handleToggle and onSideNavBlur when blurred', () => {
187+
const onSideNavBlurMock = jest.fn();
188+
const TestChild = () => <div data-testid="child">Child</div>;
189+
const { getByRole } = render(
190+
<SideNav aria-label="test" onSideNavBlur={onSideNavBlurMock}>
191+
<TestChild />
192+
</SideNav>
193+
);
194+
const sideNav = getByRole('navigation');
195+
fireEvent.focus(sideNav);
196+
fireEvent.blur(sideNav);
197+
expect(onSideNavBlurMock).not.toHaveBeenCalled();
198+
});
199+
200+
it('should not call onSideNavBlur if not expanded and isFixedNav is true', () => {
201+
const onSideNavBlurMock = jest.fn();
202+
const TestChild = () => <div data-testid="child">Child</div>;
203+
const { getByRole } = render(
204+
<SideNav aria-label="test" isFixedNav onSideNavBlur={onSideNavBlurMock}>
205+
<TestChild />
206+
</SideNav>
207+
);
208+
const sideNav = getByRole('navigation');
209+
fireEvent.focus(sideNav);
210+
fireEvent.blur(sideNav);
211+
expect(onSideNavBlurMock).not.toHaveBeenCalled();
212+
});
213+
214+
it('should call onSideNavBlur when blurred, is not fixed, and is expanded', () => {
215+
const onSideNavBlurMock = jest.fn();
216+
const TestChild = () => <div data-testid="child">Child</div>;
217+
const { getByRole } = render(
218+
<SideNav
219+
aria-label="test"
220+
onSideNavBlur={onSideNavBlurMock}
221+
defaultExpanded>
222+
<TestChild />
223+
</SideNav>
224+
);
225+
const sideNav = getByRole('navigation');
226+
fireEvent.focus(sideNav);
227+
const unrelatedElement = document.createElement('div');
228+
document.body.appendChild(unrelatedElement);
229+
fireEvent.blur(sideNav, { relatedTarget: unrelatedElement });
230+
expect(onSideNavBlurMock).toHaveBeenCalled();
231+
document.body.removeChild(unrelatedElement);
232+
});
233+
234+
it('should not call onSideNavBlur when isFixedNav is true', () => {
235+
const onSideNavBlurMock = jest.fn();
236+
const TestChild = () => <div data-testid="child">Child</div>;
237+
const { getByRole } = render(
238+
<SideNav
239+
aria-label="test"
240+
isFixedNav
241+
onSideNavBlur={onSideNavBlurMock}
242+
defaultExpanded>
243+
<TestChild />
244+
</SideNav>
245+
);
246+
const sideNav = getByRole('navigation');
247+
fireEvent.focus(sideNav);
248+
const unrelatedElement = document.createElement('div');
249+
document.body.appendChild(unrelatedElement);
250+
fireEvent.blur(sideNav, { relatedTarget: unrelatedElement });
251+
expect(onSideNavBlurMock).not.toHaveBeenCalled();
252+
document.body.removeChild(unrelatedElement);
253+
});
254+
255+
it('should set expanded state to false on mouse leave', () => {
256+
const onToggleMock = jest.fn();
257+
const TestChild = () => <div data-testid="child">Child</div>;
258+
const { getByRole } = render(
259+
<SideNav aria-label="test" defaultExpanded onToggle={onToggleMock}>
260+
<TestChild />
261+
</SideNav>
262+
);
263+
const sideNav = getByRole('navigation');
264+
expect(sideNav).toHaveClass('cds--side-nav__navigation');
265+
fireEvent.mouseLeave(sideNav);
266+
expect(sideNav).toHaveClass('cds--side-nav__navigation');
267+
});
268+
269+
it('should handleToggle if isRail is true', () => {
270+
const onToggleMock = jest.fn();
271+
const TestChild = () => <div data-testid="child">Child</div>;
272+
const { getByRole } = render(
273+
<SideNav aria-label="test" onToggle={onToggleMock} isRail>
274+
<TestChild />
275+
</SideNav>
276+
);
277+
const sideNav = getByRole('navigation');
278+
fireEvent.focus(sideNav);
279+
expect(onToggleMock).toHaveBeenCalled();
280+
fireEvent.mouseLeave(sideNav);
281+
expect(onToggleMock).toHaveBeenCalledWith(false, false);
282+
fireEvent.mouseEnter(sideNav);
283+
expect(onToggleMock).toHaveBeenCalledWith(true, true);
284+
});
285+
286+
it('should not call handleToggle if isRail is false', () => {
287+
const onToggleMock = jest.fn();
288+
const TestChild = () => <div data-testid="child">Child</div>;
289+
const { getByRole } = render(
290+
<SideNav
291+
aria-label="test"
292+
defaultExpanded
293+
onToggle={onToggleMock}
294+
isRail={false}>
295+
<TestChild />
296+
</SideNav>
297+
);
298+
const sideNav = getByRole('navigation');
299+
fireEvent.mouseLeave(sideNav);
300+
expect(onToggleMock).not.toHaveBeenCalled();
301+
});
302+
303+
it('should expand the SideNav immediately on click', () => {
304+
const TestChild = () => <div data-testid="child">Child</div>;
305+
const onToggleMock = jest.fn();
306+
const { getByRole } = render(
307+
<SideNav aria-label="test" onToggle={onToggleMock} isRail>
308+
<TestChild />
309+
</SideNav>
310+
);
311+
const sideNav = getByRole('navigation');
312+
expect(sideNav).not.toHaveClass('cds--side-nav--expanded');
313+
fireEvent.click(sideNav);
314+
expect(onToggleMock).toHaveBeenCalledWith(true, true);
315+
expect(sideNav).toHaveClass('cds--side-nav--expanded');
316+
});
317+
318+
it('should focus SideNav after tabbing from headerMenuButton', () => {
319+
render(
320+
<>
321+
<button className="cds--header__menu-toggle"></button>
322+
<SideNav
323+
aria-label="test"
324+
expanded
325+
isFixedNav={false}
326+
defaultExpanded
327+
/>
328+
</>
329+
);
330+
const mockHeaderMenuButton = screen.getByRole('button');
331+
const sideNav = screen.getByRole('navigation');
332+
333+
mockHeaderMenuButton.focus();
334+
fireEvent.keyDown(window, { key: 'Tab' });
335+
expect(document.activeElement).toBe(sideNav);
336+
});
67337
});

0 commit comments

Comments
 (0)