Skip to content

Commit

Permalink
test: add unit test to Navigation components
Browse files Browse the repository at this point in the history
  • Loading branch information
AliKdhim87 committed Sep 13, 2024
1 parent 29261f6 commit 6a24034
Show file tree
Hide file tree
Showing 13 changed files with 593 additions and 3 deletions.
1 change: 1 addition & 0 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"@rollup/plugin-typescript": "11.1.6",
"@testing-library/react": "15.0.7",
"@testing-library/jest-dom": "6.4.5",
"@testing-library/user-event": "14.5.2",
"@types/lodash.kebabcase": "4.1.9",
"@types/jest": "29.5.12",
"@types/react-dom": "18.2.19",
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/Markdown/Markdown.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('Markdown', () => {
});
it('should render the Markdown component with richtext class', () => {
const { container } = render(<Markdown>Hello World!</Markdown>);
const text = container.querySelector('.utrecht-rich-text');
const text = container.querySelector(':only-child');
expect(text).toBeInTheDocument();
});
it('should render custom component', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { createRef } from 'react';
import { NavigationIcon } from './index';

describe('NavigationIcon', () => {
it('renders the hamburger icon', () => {
const { container } = render(<NavigationIcon name="hamburger" />);
expect(container.querySelector('svg')).toHaveAttribute('aria-hidden', 'true');
expect(container.querySelectorAll('line')).toHaveLength(3);
});

it('renders the close icon', () => {
const { container } = render(<NavigationIcon name="close" />);
expect(container.querySelector('svg')).toHaveAttribute('aria-hidden', 'true');
expect(container.querySelectorAll('line')).toHaveLength(2);
});

it('renders the icon with the provided ref', () => {
const ref = createRef<SVGSVGElement>();
const { container } = render(<NavigationIcon name="hamburger" ref={ref} />);
expect(ref.current).toBe(container.querySelector('svg'));
});

it('renders a design system BEM class name', () => {
const { container } = render(<NavigationIcon name="hamburger" />);
expect(container.querySelector('svg')).toHaveClass('utrecht-topnav__icon');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { createRef } from 'react';
import { NavigationItem } from './index';

describe('NavigationItem', () => {
it('renders NavigationItem component', () => {
const { container } = render(<NavigationItem>Test</NavigationItem>);
const navItem = container.querySelector(':only-child');
expect(navItem).toBeInTheDocument();
});

it('renders a design system BEM class name', () => {
const { container } = render(<NavigationItem>Test</NavigationItem>);
const navItem = container.querySelector(':only-child');
expect(navItem).toHaveClass('utrecht-navigation__item');
});

it('should render a list item with the appropriate class name when mobile', () => {
const { container } = render(<NavigationItem mobile>Test</NavigationItem>);
const navItem = container.querySelector(':only-child');
expect(navItem).toHaveClass('utrecht-navigation__item--mobile');
expect(navItem).toHaveClass('utrecht-navigation__item-icon');
});

it('should forward the ref to the list item', () => {
const ref = createRef<HTMLLIElement>();
const { container } = render(<NavigationItem ref={ref}>Test</NavigationItem>);
expect(container.firstChild).toBe(ref.current);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { fireEvent, render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { createRef } from 'react';
import { NavigationLink } from './index';

describe('NavigationLink', () => {
it('renders the NavigationLink component', () => {
const { container } = render(<NavigationLink href="/">Home</NavigationLink>);
const link = container.querySelector(':only-child');
expect(link).toBeInTheDocument();
});
it('applies the correct class when the link is current', () => {
const { container } = render(
<NavigationLink href="/" isCurrent>
Home
</NavigationLink>,
);
const link = container.querySelector(':only-child');
expect(link).toHaveClass('utrecht-navigation__link--is-current');
});
it('calls the onClick handler when clicked', () => {
const onClick = jest.fn();
const { container } = render(
<NavigationLink href="/" onClick={onClick}>
Home
</NavigationLink>,
);
const link = container.querySelector(':only-child');
fireEvent.click(link as HTMLAnchorElement);
expect(onClick).toHaveBeenCalled();
});
it('renders the marker when provided', () => {
const { container } = render(
<NavigationLink href="/" marker={<span>Marker</span>}>
Home
</NavigationLink>,
);
const marker = container.querySelector('span');
expect(marker).toBeInTheDocument();
});
it('renders the children when provided', () => {
const { container } = render(<NavigationLink href="/">Home</NavigationLink>);
const link = container.querySelector(':only-child');
expect(link).toHaveTextContent('Home');
});
it('applies the correct class when the link is mobile', () => {
const { container } = render(
<NavigationLink href="/" mobile>
Home
</NavigationLink>,
);
const link = container.querySelector(':only-child');
expect(link).toHaveClass('utrecht-navigation__link--mobile');
});
it('sets the correct href attribute', () => {
const { container } = render(<NavigationLink href="/">Home</NavigationLink>);
const link = container.querySelector(':only-child');
expect(link).toHaveAttribute('href', '/');
});
it('sets the correct aria-current attribute', () => {
const { container } = render(
<NavigationLink href="/" isCurrent>
Home
</NavigationLink>,
);
const link = container.querySelector(':only-child');
expect(link).toHaveAttribute('aria-current', 'page');
});
it('renders the correct role attribute', () => {
const { getByRole } = render(<NavigationLink href="/">Home</NavigationLink>);
const link = getByRole('link');
expect(link).toBeInTheDocument();
});
it('supports ForwardRef in React', () => {
const ref = createRef<HTMLAnchorElement>();

const { container } = render(
<NavigationLink ref={ref} href="/">
Home
</NavigationLink>,
);
const link = container.querySelector(':only-child');
expect(ref.current).toBe(link);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { AnchorHTMLAttributes, ForwardedRef, forwardRef, PropsWithChildren, Reac
import styles from './index.module.scss';

interface NavigationLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'placeholder'> {
placeholder?: boolean;
mobile?: boolean;
isCurrent?: boolean;
marker?: ReactNode;
Expand Down
118 changes: 118 additions & 0 deletions packages/ui/src/components/Navigation/NavigationList/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { fireEvent, render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { NavigationList } from './index';
import { NavigationListType } from '../index';

const listData: NavigationListType[] = [
{
href: '#',
textContent: 'Home',
children: [
{
href: '#',
textContent: 'Sublink 1',
},
{
href: '#',
textContent: 'Sublink 2',
},
],
},
{
href: '#',
textContent: 'About',
},
];
describe('NavigationList', () => {
it('renders NavigationList component', () => {
const { container } = render(<NavigationList list={[]} />);
const navigationList = container.querySelector(':only-child');
expect(navigationList).toBeInTheDocument();
});
it('renders a design system BEM class name', () => {
const { container } = render(<NavigationList list={[]} />);
const navigationList = container.querySelector(':only-child');
expect(navigationList).toHaveClass('utrecht-navigation__list');
});
it('renders NavigationList component with children', () => {
const { container } = render(<NavigationList list={listData} />);
const navigationList = container.querySelector(':only-child');
const navListItem = container.querySelectorAll('li a');
navListItem.forEach((item) => {
expect(item).toBeInTheDocument();
expect(item).toHaveAttribute('href');
expect(item).toHaveTextContent(/Home|About/);
});
expect(navListItem.length).toBe(2);
expect(navigationList).toBeInTheDocument();
});
describe('Mobile', () => {
it('renders NavigationList component with children and mobile prop', () => {
const { container } = render(<NavigationList list={listData} mobile />);
const navigationList = container.querySelector(':only-child');
const navListItem = container.querySelectorAll('li');
navListItem.forEach((item) => {
expect(item).toBeInTheDocument();
expect(item).toHaveClass('utrecht-navigation__item--mobile');
});
expect(navigationList).toBeInTheDocument();
expect(navigationList).toHaveClass('utrecht-navigation__list--mobile');
});
it('renders NavigationList component with children and filled marker prop by default', () => {
const { container } = render(<NavigationList list={listData} mobile />);
const navigationList = container.querySelector(':only-child');
const navListItem = navigationList?.querySelectorAll('li a')[0];
const marker = navListItem?.querySelector(':only-child');
expect(marker).toBeInTheDocument();
expect(marker).toHaveClass('utrecht-navigation__marker');
expect(marker).toHaveClass('utrecht-navigation__marker--fill');
expect(navListItem).toHaveClass('utrecht-navigation__link--mobile');
expect(navListItem).toBeInTheDocument();
expect(navigationList).toBeInTheDocument();
});
it('renders NavigationList component with children and outlined marker prop', () => {
const { container } = render(<NavigationList list={listData} mobile />);
const navigationList = container.querySelector(':only-child');
const navListItem = navigationList?.querySelectorAll('li a')[1];
const marker = navListItem?.querySelector(':only-child');
expect(marker).toBeInTheDocument();
expect(marker).toHaveClass('utrecht-navigation__marker');
expect(marker).toHaveClass('utrecht-navigation__marker--outline');
expect(navListItem).toHaveClass('utrecht-navigation__link--mobile');
expect(navListItem).toBeInTheDocument();
expect(navigationList).toBeInTheDocument();
});
});

it('renders NavigationList component with children and sideNav prop', () => {
const { container } = render(<NavigationList list={listData} sideNav />);
const navigationList = container.querySelector(':only-child');
expect(navigationList).toBeInTheDocument();
expect(navigationList).toHaveClass('utrecht-navigation__list--side-nav');
});
it('renders NavigationList component with children and subList prop', () => {
const { container } = render(<NavigationList list={listData} subList mobile />);
const navigationList = container.querySelector(':only-child');
const navListItem = navigationList?.querySelectorAll('li');
navListItem?.forEach((item) => {
const subList = item.querySelectorAll('li ul');
subList?.forEach((subItem) => {
expect(subItem).toBeInTheDocument();
expect(subItem).toHaveClass('utrecht-navigation__list--sub-list');
});
expect(item).toBeInTheDocument();
expect(item).toHaveClass('utrecht-navigation__item');
});
expect(navigationList).toBeInTheDocument();
expect(navigationList).toHaveClass('utrecht-navigation__list--sub-list');
});
test('focuses on the first link when the list receives focus', () => {
render(<NavigationList list={listData} />);
const firstLink = screen.getByText('Home');
const navList = firstLink.closest('ul');
expect(navList).toHaveAttribute('tabIndex', '-1');

fireEvent.focus(navList as HTMLUListElement);
expect(firstLink).toHaveFocus();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
// import { createRef } from 'react';
import { NavigationMarker } from './index';

describe('NavigationMarker', () => {
it('renders a NavigationMarker', () => {
const { container } = render(<NavigationMarker />);
const marker = container.querySelector(':only-child');
expect(marker).toBeInTheDocument();
});
it('renders a design system BEM class name', () => {
const { container } = render(<NavigationMarker />);
const marker = container.querySelector(':only-child');
expect(marker).toHaveClass('utrecht-navigation__marker');
});
it('renders a fill marker by default', () => {
const { container } = render(<NavigationMarker />);
const marker = container.querySelector(':only-child');
expect(marker).toBeInTheDocument();
expect(marker).toHaveClass('utrecht-navigation__marker--fill');
expect(marker).not.toHaveClass('utrecht-navigation__marker--outline');
});

it('renders an outline marker when appearance is set to outline', () => {
const { container } = render(<NavigationMarker appearance="outline" />);
const marker = container.querySelector(':only-child');
expect(marker).toBeInTheDocument();
expect(marker).toHaveClass('utrecht-navigation__marker--outline');
expect(marker).not.toHaveClass('utrecht-navigation__marker--fill');
});

it('renders a current marker when isCurrent is true', () => {
const { container } = render(<NavigationMarker isCurrent />);
const marker = container.querySelector(':only-child');
expect(marker).toBeInTheDocument();
expect(marker).toHaveClass('utrecht-navigation__marker--current');
});
// TODO add ForwardRef support to the component
// it('supports ForwardRef in React', () => {
// const ref = createRef<SVGSVGElement>();

// const { container } = render(<NavigationMarker ref={ref} />);

// const drawer = container.querySelector(':only-child');

// expect(ref.current).toBe(drawer);
// });
});
Loading

0 comments on commit 6a24034

Please sign in to comment.