Skip to content

Commit

Permalink
[Unit Test] Write Component Unit Tests (#1110)
Browse files Browse the repository at this point in the history
Components needed unit tests so some unit tests have been written.

## Changes

- Updated vite config to facilitate in IDE running of tests
- Wrote tests for the following component files
  - ScreenReaderOnly
  - ScrollToTop
  - SectionIntro.tsx
  - TextInput.tsx
  - WarningErrorIcon.tsx

## How to test this PR

1. Pull the branch for this PR
2. Navigate to the sbl-frontend directory
3. run `yarn run vitest`
4. Verify that the coverage is 100% across the board for the above files
mentioned
  • Loading branch information
tanner-ricks authored Jan 24, 2025
1 parent e253495 commit f0ad871
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 1 deletion.
9 changes: 9 additions & 0 deletions src/components/__tests__/ScreenReaderOnly.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { render, screen } from '@testing-library/react';
import ScreenReaderOnly from 'components/ScreenReaderOnly';

describe('<ScreenReaderOnly />', () => {
it('Renders expected content', async () => {
render(<ScreenReaderOnly>test children</ScreenReaderOnly>);
expect(screen.getByText('test children')).toBeInTheDocument();
});
});
69 changes: 69 additions & 0 deletions src/components/__tests__/ScrollToTop.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { vi } from 'vitest';
import { render } from '@testing-library/react';
import ScrollToTop from 'components/ScrollToTop';
import type { Location } from 'react-router';
// eslint-disable-next-line import/no-extraneous-dependencies
import * as ReactRouter from 'react-router';
import { useLocation } from 'react-router-dom';

const location: Location = {
state: null,
key: 'default',
pathname: '/test',
search: '',
hash: '',
};

vi.mock('react-router-dom', async () => {
const actual = await vi.importActual('react-router-dom');
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return {
// @ts-expect-error This is standard testing methodology
...actual,
useLocation: vi.fn().mockImplementation(() => location),
};
});

describe('<ScrollToTop />', () => {
it('Renders expected content', async () => {
const oldLocation: Location = {
state: null,
key: 'default',
pathname: '/test',
search: '',
hash: '',
};
const newLocation: Location = {
state: null,
key: 'default',
pathname: '/test-new',
search: '',
hash: '',
};

const scrollToMock = vi.spyOn(window, 'scrollTo');
const useLocationMock = vi.spyOn(ReactRouter, 'useLocation');
// @ts-expect-error This is standard testing methodology
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
useLocation.mockImplementation(() => oldLocation);
useLocationMock.mockImplementation(() => oldLocation);

expect(window.scrollTo).not.toHaveBeenCalled();
const { rerender } = render(<ScrollToTop />);
expect(window.scrollTo).toHaveBeenCalled();
scrollToMock.mockReset();

expect(window.scrollTo).not.toHaveBeenCalled();
// @ts-expect-error This is standard testing methodology
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
useLocation.mockClear();
// @ts-expect-error This is standard testing methodology
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
useLocation.mockImplementation(() => newLocation);
useLocationMock.mockClear();
useLocationMock.mockImplementation(() => newLocation);

rerender(<ScrollToTop />);
expect(window.scrollTo).toHaveBeenCalled();
});
});
31 changes: 31 additions & 0 deletions src/components/__tests__/SectionIntro.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { render, screen } from '@testing-library/react';
import SectionIntro from 'components/SectionIntro';

describe('<SectionIntro />', () => {
it('Renders expected content', async () => {
render(
<SectionIntro data-testid='test' id='test'>
{undefined}
</SectionIntro>,
);
expect(screen.getByTestId('test')).toBeInTheDocument();
});

it('Renders expected heading content', async () => {
render(
<SectionIntro data-testid='test' id='test' heading='test heading'>
{undefined}
</SectionIntro>,
);
expect(screen.getByText('test heading')).toBeInTheDocument();
});

it('Renders expected child content', async () => {
render(
<SectionIntro data-testid='test' id='test'>
<span>test content</span>
</SectionIntro>,
);
expect(screen.getByText('test content')).toBeInTheDocument();
});
});
98 changes: 98 additions & 0 deletions src/components/__tests__/TextInput.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { render, screen } from '@testing-library/react';
import TextInput from 'components/TextInput';
import {
DefaultInputCharLimit,
EmailInputCharLimit,
PhoneInputCharLimit,
UrlInputCharLimit,
} from 'utils/constants';

describe('<TextInput />', () => {
it('Renders expected content', async () => {
render(<TextInput data-testid='test' id='test' name='test' />);
const input = screen.getByTestId('test');
expect(input).toBeInTheDocument();
expect(input).toHaveAttribute('maxLength', String(DefaultInputCharLimit));
});

it('Renders expected content - empty type', async () => {
// @ts-expect-error This is a test that exists to test an otherwise unreachable default case
render(<TextInput data-testid='test' id='test' name='test' type='' />);
const input = screen.getByTestId('test');
expect(input).toBeInTheDocument();
expect(input).toHaveAttribute('maxLength', String(DefaultInputCharLimit));
});

it('Has correct char limit - positive manual limit', async () => {
render(
<TextInput data-testid='test' id='test' name='test' maxLength={261} />,
);
const input = screen.getByTestId('test');
expect(input).toBeInTheDocument();
expect(input).toHaveAttribute('maxLength', '261');
});

it('Has correct char limit - negative manual limit', async () => {
render(
<TextInput data-testid='test' id='test' name='test' maxLength={-1} />,
);
const input = screen.getByTestId('test');
expect(input).toBeInTheDocument();
expect(input).toHaveAttribute('maxLength', String(DefaultInputCharLimit));
});

it('Has correct char limit - url', async () => {
render(<TextInput data-testid='test' id='test' name='test' type='url' />);
const input = screen.getByTestId('test');
expect(input).toBeInTheDocument();
expect(input).toHaveAttribute('maxLength', String(UrlInputCharLimit));
});

it('Has correct char limit - tel', async () => {
render(<TextInput data-testid='test' id='test' name='test' type='tel' />);
const input = screen.getByTestId('test');
expect(input).toBeInTheDocument();
expect(input).toHaveAttribute('maxLength', String(PhoneInputCharLimit));
});

it('Has correct char limit - email', async () => {
render(<TextInput data-testid='test' id='test' name='test' type='email' />);
const input = screen.getByTestId('test');
expect(input).toBeInTheDocument();
expect(input).toHaveAttribute('maxLength', String(EmailInputCharLimit));
});

it('Has correct char limit - number', async () => {
render(
<TextInput data-testid='test' id='test' name='test' type='number' />,
);
const input = screen.getByTestId('test');
expect(input).toBeInTheDocument();
expect(input).toHaveAttribute('maxLength', String(DefaultInputCharLimit));
});

it('Has correct char limit - password', async () => {
render(
<TextInput data-testid='test' id='test' name='test' type='password' />,
);
const input = screen.getByTestId('test');
expect(input).toBeInTheDocument();
expect(input).toHaveAttribute('maxLength', String(DefaultInputCharLimit));
});

it('Has correct char limit - search', async () => {
render(
<TextInput data-testid='test' id='test' name='test' type='search' />,
);
const input = screen.getByTestId('test');
expect(input).toBeInTheDocument();
expect(input).toHaveAttribute('maxLength', String(DefaultInputCharLimit));
});

it('Has correct char limit - text', async () => {
render(<TextInput data-testid='test' id='test' name='test' type='text' />);
const input = screen.getByTestId('test');
expect(input).toBeInTheDocument();
expect(input).toHaveAttribute('maxLength', String(DefaultInputCharLimit));
});
});
9 changes: 9 additions & 0 deletions src/components/__tests__/WarningErrorIcon.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { render, screen } from '@testing-library/react';
import WarningErrorIcon from 'components/WarningErrorIcon';

describe('<WarningErrorIcon />', () => {
it('Renders expected content', async () => {
render(<WarningErrorIcon />);
expect(screen.getByRole('img')).toBeInTheDocument();
});
});
3 changes: 2 additions & 1 deletion vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default async ({ mode }) => {
const environment = loadEnv(mode, process.cwd(), '');

return defineConfig({
root: __dirname,
optimizeDeps: {
exclude: [],
},
Expand Down Expand Up @@ -58,10 +59,10 @@ export default async ({ mode }) => {
svgr(),
tsconfigPaths(),
react(),
importMetaEnv.vite({ example: '.env.example.public' }),
...(mode === 'test'
? []
: [
importMetaEnv.vite({ example: '.env.example.public' }),
eslintPlugin(),
VitePWA({
registerType: 'autoUpdate',
Expand Down

0 comments on commit f0ad871

Please sign in to comment.