Skip to content

Commit

Permalink
test: migrate tests to use vitest
Browse files Browse the repository at this point in the history
  • Loading branch information
ajkl2533 committed Dec 2, 2024
1 parent 860dd1a commit 233013e
Show file tree
Hide file tree
Showing 47 changed files with 651 additions and 605 deletions.
2 changes: 1 addition & 1 deletion TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Please beware all features or bug fixes must be tested by one or more specs (uni

## Unit tests

For unit testing, we are using Jest along with [React Testing Library](https://testing-library.com/docs/intro)
For unit testing, we are using Vitest along with [React Testing Library](https://testing-library.com/docs/intro)
and [jest-dom](https://github.com/testing-library/jest-dom/blob/master/README.md).

Unit tests should primarily be used for testing component interactions. For testing the visual aspects of components, visual regression tests should be used. However, this does not mean that visual changes in components cannot be used for testing the result of interaction with the component. For example, it is completely valid to test whether the component content is not visible after closing a modal window.
Expand Down
21 changes: 0 additions & 21 deletions jest.config.ts

This file was deleted.

15 changes: 7 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@
"test:storybook:visual": "./visual-regressions/bin/take-and-check-in-docker.sh",
"test:storybook:visual:update": "./visual-regressions/bin/take-and-update-in-docker.sh",
"test:storybook:visual:ci": "./visual-regressions/bin/take-and-check-in-docker-ci.sh",
"test": "jest -w 4",
"test:watch": "jest --watch",
"test": "vitest --run",
"test:watch": "vitest",
"test:a11y": "test-storybook --maxWorkers 4 --url=http://localhost:8008",
"test:a11y:ci": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"http-server storybook-static --port 6006 --silent\" \"wait-on tcp:6006 && yarn test-storybook --maxWorkers 2 --shard ${SHARD}/${MAX_SHARDS}\"",
"test:storybook:coverage": "test-storybook --coverage --coverageDirectory coverage/storybook --maxWorkers 4 --url=http://localhost:8008",
"test:unit:coverage": "yarn run test --coverage --coverageDirectory coverage/jest",
"test:unit:coverage": "yarn run test --coverage",
"test:coverage": "yarn run test:unit:coverage & yarn run test:storybook:coverage",
"types": "tsc -b",
"lint": "eslint . --ext js,ts,jsx,tsx,md,mdx",
Expand Down Expand Up @@ -140,11 +140,10 @@
"@storybook/test-runner": "^0.19.1",
"@storybook/theming": "^8.4.4",
"@testing-library/dom": ">=7.21.4",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^12.1.4",
"@testing-library/react-hooks": "^7.0.2",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^29.5.12",
"@types/node": "^22.5.5",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
Expand All @@ -157,6 +156,7 @@
"@typescript-eslint/eslint-plugin": "^7.14.1",
"@typescript-eslint/parser": "^7.14.1",
"@vitejs/plugin-react": "^4.3.1",
"@vitest/coverage-v8": "2.1.7",
"axe-playwright": "^2.0.3",
"babel-plugin-styled-components": "^2.0.7",
"concurrently": "^9.0.1",
Expand All @@ -177,12 +177,11 @@
"eslint-plugin-react-hooks": "^4.0.4",
"eslint-plugin-storybook": "^0.9.0",
"eslint-plugin-testing-library": "^5.1.0",
"happy-dom": "^15.11.7",
"history": "^5.3.0",
"http-server": "^14.1.1",
"husky": "^7.0.4",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-styled-components": "^7.0.8",
"knip": "^5.30.5",
"lint-staged": "^12.3.7",
Expand Down Expand Up @@ -210,13 +209,13 @@
"styled-components": "^5.3.3",
"stylelint": "^16.6.1",
"stylelint-config-standard": "^36.0.0",
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"tslib": "^2.6.2",
"typescript": "^5.6.2",
"vite": "^5.4.6",
"vite-plugin-dts": "^4.2.1",
"vite-require": "^0.2.3",
"vitest": "^2.1.7",
"wait-on": "^8.0.1"
},
"resolutions": {
Expand Down
3 changes: 2 additions & 1 deletion src/components/Accordion/Accordion.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { vi } from 'vitest';

import Accordion, { filterState } from './Accordion';

Expand Down Expand Up @@ -89,7 +90,7 @@ describe('Accordion', () => {
});

it('should handle click events correctly, updating the state based on whether the item is already open and the `isCollapsedOnOpen` setting', () => {
const onChangeMock = jest.fn();
const onChangeMock = vi.fn();
render(
<Accordion
items={[items[0], { ...items[1], isOpen: true }, items[2]]}
Expand Down
15 changes: 8 additions & 7 deletions src/components/Collapsible/Collapsible.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ReactNode, useState } from 'react';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { type Mock, vi } from 'vitest';

import { renderWithProviders } from '../../utils/tests/renderWithProviders';
import { Collapsible } from './index';
Expand All @@ -12,8 +13,8 @@ const ControllingComponent = ({
children,
}: {
isOpen?: boolean;
onOpenChange: jest.Mock;
onOpen?: jest.Mock;
onOpenChange: Mock;
onOpen?: Mock;
children: ReactNode;
}) => {
const [controlledIsOpen, setControlledIsOpen] = useState(isOpen ?? false);
Expand Down Expand Up @@ -76,7 +77,7 @@ describe('Collapsible', () => {
expect(screen.queryByText('Collapsible content')).not.toBeInTheDocument();
});
it('should call "onOpen" on trigger click', async () => {
const onOpenMock = jest.fn();
const onOpenMock = vi.fn();
renderWithProviders(
<Collapsible title="test" onOpen={onOpenMock}>
Collapsible content
Expand All @@ -87,7 +88,7 @@ describe('Collapsible', () => {
expect(onOpenMock).toBeCalledTimes(1);
});
it('should call "onOpen" only when is open', async () => {
const onOpenMock = jest.fn();
const onOpenMock = vi.fn();
renderWithProviders(
<Collapsible title="test" onOpen={onOpenMock} defaultIsOpen>
Collapsible content
Expand All @@ -101,7 +102,7 @@ describe('Collapsible', () => {

describe('controlled mode', () => {
it('should toggle open state on trigger click', async () => {
const onOpenChangeMock = jest.fn();
const onOpenChangeMock = vi.fn();
renderWithProviders(
<ControllingComponent onOpenChange={onOpenChangeMock}>
Collapsible content
Expand All @@ -115,7 +116,7 @@ describe('Collapsible', () => {
});

it('should toggle open state on external state change', async () => {
const onOpenChangeMock = jest.fn();
const onOpenChangeMock = vi.fn();
renderWithProviders(
<ControllingComponent onOpenChange={onOpenChangeMock}>
Collapsible content
Expand All @@ -129,7 +130,7 @@ describe('Collapsible', () => {
});

it('should be opened when "isOpen" is true', () => {
const onOpenChangeMock = jest.fn();
const onOpenChangeMock = vi.fn();
renderWithProviders(
<ControllingComponent isOpen onOpenChange={onOpenChangeMock}>
Collapsible content
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { act, fireEvent, screen, waitFor } from '@testing-library/react';
import { vi } from 'vitest';

import { renderWithProviders } from '../../../../utils/tests/renderWithProviders';
import { DatatableStore, datatableInitialState } from '../../Datatable.store';
import BatchActions from './BatchActions';

const actionFnMock = jest.fn();
const subactionFnMock = jest.fn();
const actionFnMock = vi.fn();
const subactionFnMock = vi.fn();
const actions = [
{
label: 'Action',
Expand All @@ -31,7 +32,7 @@ describe('Datatable/BatchActions', () => {
DatatableStore.replace(datatableInitialState);
});
afterEach(() => {
jest.resetAllMocks();
vi.resetAllMocks();
});

describe('given exclusive selection is disabled', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { fireEvent, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { vi } from 'vitest';

import { renderWithProviders } from '../../../utils/tests/renderWithProviders';
import { InputFilter } from '../../Filters/components';
Expand Down Expand Up @@ -162,7 +163,7 @@ describe('Datatable/ControlsModule', () => {

describe('given search is enabled', () => {
it('should perform search on mount when defaultValue is provided', async () => {
const onSearchMock = jest.fn();
const onSearchMock = vi.fn();
renderWithProviders(
<ControlsModule
{...defaultControlsConfig}
Expand All @@ -182,7 +183,7 @@ describe('Datatable/ControlsModule', () => {
});
});
it('should not perform search when no defaultValue is provided', async () => {
const onSearchMock = jest.fn();
const onSearchMock = vi.fn();
renderWithProviders(
<ControlsModule
{...defaultControlsConfig}
Expand Down Expand Up @@ -274,7 +275,6 @@ describe('Datatable/ControlsModule', () => {
expect(DatatableStore.getRawState().hasAppliedFilters).toBe(false);
});
it('should store search query when perform search', async () => {
jest.useFakeTimers();
const query = 'Search query';
renderWithProviders(
<ControlsModule
Expand All @@ -291,10 +291,8 @@ describe('Datatable/ControlsModule', () => {
await waitFor(() =>
expect(DatatableStore.getRawState().query).toBe(query),
);
jest.useRealTimers();
});
it('should clear search query on Clear button click', async () => {
jest.useFakeTimers();
const query = 'Search query';
renderWithProviders(
<ControlsModule
Expand All @@ -313,7 +311,6 @@ describe('Datatable/ControlsModule', () => {

await waitFor(() => DatatableStore.getRawState().query === query);
expect(DatatableStore.getRawState().query).toBe('');
jest.useRealTimers();
});
});
});
9 changes: 5 additions & 4 deletions src/components/Datatable/Datatable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Column } from 'react-table';
import { filter } from 'ramda';
import { vi } from 'vitest';

import { renderWithProviders } from '../../utils/tests/renderWithProviders';
import { fields } from '../Filters/mocks/options';
Expand Down Expand Up @@ -64,10 +65,10 @@ describe('Datatable', () => {
DatatableStore.replace(datatableInitialState);
});
afterEach(() => {
jest.resetAllMocks();
vi.resetAllMocks();
});
it('should not call "onDataFetch" on mount', () => {
const onDataFetchMock = jest.fn();
const onDataFetchMock = vi.fn();

renderWithProviders(
<Datatable<Data>
Expand All @@ -83,10 +84,10 @@ describe('Datatable', () => {
});
describe('on request cancelation', () => {
it('should call "onCancelLoading"', () => {
const onCancelLoading = jest.fn();
const onCancelLoading = vi.fn();
renderWithProviders(
<Datatable<Data>
onDataFetch={jest.fn()}
onDataFetch={vi.fn()}
onCancelLoading={onCancelLoading}
data={data}
columns={columns}
Expand Down
9 changes: 5 additions & 4 deletions src/components/Datatable/Table/Table.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { act, screen, waitFor } from '@testing-library/react';
import { Column } from 'react-table';
import userEvent from '@testing-library/user-event';
import { vi } from 'vitest';

import { renderWithProviders } from '../../../utils/tests/renderWithProviders';
import { DatatableStore, datatableInitialState } from '../Datatable.store';
Expand Down Expand Up @@ -40,7 +41,7 @@ describe('Datatable/Table', () => {
DatatableStore.replace(datatableInitialState);
});
afterEach(() => {
jest.resetAllMocks();
vi.resetAllMocks();
});

it('should select all rows on Toggle All click', () => {
Expand Down Expand Up @@ -134,7 +135,7 @@ describe('Datatable/Table', () => {
});
});
it('should call onClick handler in row action dropdown with correct parameters', async () => {
const rowActionMock = jest.fn();
const rowActionMock = vi.fn();
const rowIndex = 0;
renderWithProviders(
<Table<Data>
Expand Down Expand Up @@ -409,7 +410,7 @@ describe('Datatable/Table', () => {
{...defaultTableConfig}
isDataLoading
isCancelDisabled={false}
onCancelLoading={jest.fn}
onCancelLoading={vi.fn}
/>,
);

Expand All @@ -429,7 +430,7 @@ describe('Datatable/Table', () => {
{...defaultTableConfig}
isDataLoading
isCancelDisabled
onCancelLoading={jest.fn}
onCancelLoading={vi.fn}
/>,
);

Expand Down
23 changes: 7 additions & 16 deletions src/components/Datatable/components/Search/Search.test.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
/* eslint-disable testing-library/await-async-query */
import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { vi } from 'vitest';

import { createIconLibrary, resetIconLibrary } from '../../../../theme';
import Search from './Search';
import { renderWithProviders } from '../../../../utils/tests/renderWithProviders';

const onSearch = jest.fn();
const onClear = jest.fn();
const onSearch = vi.fn();
const onClear = vi.fn();

describe('Search', () => {
beforeAll(() => {
createIconLibrary();
});
afterEach(() => {
jest.clearAllMocks();
});
afterAll(() => {
resetIconLibrary();
vi.clearAllMocks();
});

it('should have defaultValue when provided', async () => {
renderWithProviders(
<Search
Expand Down Expand Up @@ -50,25 +45,21 @@ describe('Search', () => {
it('should trigger search when value is changed', async () => {
renderWithProviders(<Search onClear={onClear} onSearch={onSearch} />);
const searchInput = screen.getByRole('searchbox');
jest.useFakeTimers();

userEvent.type(searchInput, 'query');
expect(searchInput).toHaveValue('query');

await waitFor(() => expect(onSearch).toHaveBeenCalled());
jest.useRealTimers();
});
it('should trigger search if value is empty', async () => {
renderWithProviders(
<Search defaultValue="ab" onClear={onClear} onSearch={onSearch} />,
);
const searchInput = screen.getByRole('searchbox');
jest.useFakeTimers();

expect(searchInput).toHaveValue('ab');
userEvent.type(searchInput, '{Backspace}{Backspace}');
await waitFor(() => expect(onSearch).toBeCalledWith(''));
jest.useRealTimers();
await waitFor(() => expect(onSearch).toHaveBeenCalledWith(''));
});
it('should not trigger search when value is invalid', async () => {
renderWithProviders(
Expand All @@ -77,7 +68,7 @@ describe('Search', () => {
const searchInput = screen.getByRole('searchbox');

userEvent.type(searchInput, 'query{Enter}');
await waitFor(() => expect(onSearch).not.toBeCalled());
await waitFor(() => expect(onSearch).not.toHaveBeenCalled());
});

it('should validate according to pattern', () => {
Expand Down
Loading

0 comments on commit 233013e

Please sign in to comment.