Migrated to testing-library/user-event for complex events in tests

This commit is contained in:
Alejandro Celaya 2022-05-10 20:54:14 +02:00
parent 686fe5abbe
commit 7f059c3f3b
6 changed files with 76 additions and 33 deletions

21
package-lock.json generated
View file

@ -57,6 +57,7 @@
"@stryker-mutator/typescript-checker": "^6.0.2", "@stryker-mutator/typescript-checker": "^6.0.2",
"@testing-library/jest-dom": "^5.16.4", "@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.1.1", "@testing-library/react": "^13.1.1",
"@testing-library/user-event": "^14.1.1",
"@types/classnames": "^2.3.1", "@types/classnames": "^2.3.1",
"@types/enzyme": "^3.10.11", "@types/enzyme": "^3.10.11",
"@types/jest": "^27.4.1", "@types/jest": "^27.4.1",
@ -5494,6 +5495,19 @@
"react-dom": "^18.0.0" "react-dom": "^18.0.0"
} }
}, },
"node_modules/@testing-library/user-event": {
"version": "14.1.1",
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.1.1.tgz",
"integrity": "sha512-XrjH/iEUqNl9lF2HX9YhPNV7Amntkcnpw0Bo1KkRzowNDcgSN9i0nm4Q8Oi5wupgdfPaJNMAWa61A+voD6Kmwg==",
"dev": true,
"engines": {
"node": ">=12",
"npm": ">=6"
},
"peerDependencies": {
"@testing-library/dom": ">=7.21.4"
}
},
"node_modules/@tootallnate/once": { "node_modules/@tootallnate/once": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
@ -31968,6 +31982,13 @@
"@types/react-dom": "^18.0.0" "@types/react-dom": "^18.0.0"
} }
}, },
"@testing-library/user-event": {
"version": "14.1.1",
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.1.1.tgz",
"integrity": "sha512-XrjH/iEUqNl9lF2HX9YhPNV7Amntkcnpw0Bo1KkRzowNDcgSN9i0nm4Q8Oi5wupgdfPaJNMAWa61A+voD6Kmwg==",
"dev": true,
"requires": {}
},
"@tootallnate/once": { "@tootallnate/once": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",

View file

@ -73,6 +73,7 @@
"@stryker-mutator/typescript-checker": "^6.0.2", "@stryker-mutator/typescript-checker": "^6.0.2",
"@testing-library/jest-dom": "^5.16.4", "@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.1.1", "@testing-library/react": "^13.1.1",
"@testing-library/user-event": "^14.1.1",
"@types/classnames": "^2.3.1", "@types/classnames": "^2.3.1",
"@types/enzyme": "^3.10.11", "@types/enzyme": "^3.10.11",
"@types/jest": "^27.4.1", "@types/jest": "^27.4.1",

View file

@ -1,29 +1,38 @@
import { fireEvent, render, screen } from '@testing-library/react'; import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { AppUpdateBanner } from '../../src/common/AppUpdateBanner'; import { AppUpdateBanner } from '../../src/common/AppUpdateBanner';
describe('<AppUpdateBanner />', () => { describe('<AppUpdateBanner />', () => {
const toggle = jest.fn(); const toggle = jest.fn();
const forceUpdate = jest.fn(); const forceUpdate = jest.fn();
const setUp = () => ({
beforeEach(() => render(<AppUpdateBanner isOpen toggle={toggle} forceUpdate={forceUpdate} />)); user: userEvent.setup(),
...render(<AppUpdateBanner isOpen toggle={toggle} forceUpdate={forceUpdate} />),
});
afterEach(jest.clearAllMocks); afterEach(jest.clearAllMocks);
it('renders initial state', () => { it('renders initial state', () => {
setUp();
expect(screen.getByRole('heading')).toHaveTextContent('This app has just been updated!'); expect(screen.getByRole('heading')).toHaveTextContent('This app has just been updated!');
expect(screen.queryByText('Restarting...')).not.toBeInTheDocument(); expect(screen.queryByText('Restarting...')).not.toBeInTheDocument();
expect(screen.getByText('Restart now')).not.toHaveAttribute('disabled'); expect(screen.getByText('Restart now')).not.toHaveAttribute('disabled');
}); });
it('invokes toggle when alert is closed', () => { it('invokes toggle when alert is closed', async () => {
const { user } = setUp();
expect(toggle).not.toHaveBeenCalled(); expect(toggle).not.toHaveBeenCalled();
fireEvent.click(screen.getByLabelText('Close')); await user.click(screen.getByLabelText('Close'));
expect(toggle).toHaveBeenCalled(); expect(toggle).toHaveBeenCalled();
}); });
it('triggers the update when clicking the button', async () => { it('triggers the update when clicking the button', async () => {
const { user } = setUp();
expect(forceUpdate).not.toHaveBeenCalled(); expect(forceUpdate).not.toHaveBeenCalled();
fireEvent.click(screen.getByText(/^Restart now/)); await user.click(screen.getByText(/^Restart now/));
expect(forceUpdate).toHaveBeenCalled(); expect(forceUpdate).toHaveBeenCalled();
expect(await screen.findByText('Restarting...')).toBeInTheDocument(); expect(await screen.findByText('Restarting...')).toBeInTheDocument();
expect(screen.queryByText(/^Restart now/)).not.toBeInTheDocument(); expect(screen.queryByText(/^Restart now/)).not.toBeInTheDocument();

View file

@ -1,4 +1,5 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Mock } from 'ts-mockery'; import { Mock } from 'ts-mockery';
import { DomainsList } from '../../src/domains/reducers/domainsList'; import { DomainsList } from '../../src/domains/reducers/domainsList';
import { ManageDomains } from '../../src/domains/ManageDomains'; import { ManageDomains } from '../../src/domains/ManageDomains';
@ -8,16 +9,19 @@ import { SelectedServer } from '../../src/servers/data';
describe('<ManageDomains />', () => { describe('<ManageDomains />', () => {
const listDomains = jest.fn(); const listDomains = jest.fn();
const filterDomains = jest.fn(); const filterDomains = jest.fn();
const setUp = (domainsList: DomainsList) => render( const setUp = (domainsList: DomainsList) => ({
<ManageDomains user: userEvent.setup(),
listDomains={listDomains} ...render(
filterDomains={filterDomains} <ManageDomains
editDomainRedirects={jest.fn()} listDomains={listDomains}
checkDomainHealth={jest.fn()} filterDomains={filterDomains}
domainsList={domainsList} editDomainRedirects={jest.fn()}
selectedServer={Mock.all<SelectedServer>()} checkDomainHealth={jest.fn()}
/>, domainsList={domainsList}
); selectedServer={Mock.all<SelectedServer>()}
/>,
),
});
afterEach(jest.clearAllMocks); afterEach(jest.clearAllMocks);
@ -40,11 +44,11 @@ describe('<ManageDomains />', () => {
}); });
it('filters domains when SearchField changes', async () => { it('filters domains when SearchField changes', async () => {
setUp(Mock.of<DomainsList>({ loading: false, error: false, filteredDomains: [] })); const { user } = setUp(Mock.of<DomainsList>({ loading: false, error: false, filteredDomains: [] }));
expect(filterDomains).not.toHaveBeenCalled(); expect(filterDomains).not.toHaveBeenCalled();
fireEvent.change(screen.getByPlaceholderText('Search...'), { target: { value: 'Foo' } }); await user.type(screen.getByPlaceholderText('Search...'), 'Foo');
await waitFor(() => expect(filterDomains).toHaveBeenCalledTimes(1)); await waitFor(() => expect(filterDomains).toHaveBeenCalledWith('Foo'));
}); });
it('shows expected headers and one row when list of domains is empty', () => { it('shows expected headers and one row when list of domains is empty', () => {

View file

@ -1,11 +1,15 @@
import { fireEvent, render, screen } from '@testing-library/react'; import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Mock } from 'ts-mockery'; import { Mock } from 'ts-mockery';
import { DomainStatus } from '../../../src/domains/data'; import { DomainStatus } from '../../../src/domains/data';
import { DomainStatusIcon } from '../../../src/domains/helpers/DomainStatusIcon'; import { DomainStatusIcon } from '../../../src/domains/helpers/DomainStatusIcon';
describe('<DomainStatusIcon />', () => { describe('<DomainStatusIcon />', () => {
const matchMedia = jest.fn().mockReturnValue(Mock.of<MediaQueryList>({ matches: false })); const matchMedia = jest.fn().mockReturnValue(Mock.of<MediaQueryList>({ matches: false }));
const setUp = (status: DomainStatus) => render(<DomainStatusIcon status={status} matchMedia={matchMedia} />); const setUp = (status: DomainStatus) => ({
user: userEvent.setup(),
...render(<DomainStatusIcon status={status} matchMedia={matchMedia} />),
});
beforeEach(jest.clearAllMocks); beforeEach(jest.clearAllMocks);
@ -22,9 +26,9 @@ describe('<DomainStatusIcon />', () => {
['invalid' as DomainStatus], ['invalid' as DomainStatus],
['valid' as DomainStatus], ['valid' as DomainStatus],
])('renders proper tooltip based on state', async (status) => { ])('renders proper tooltip based on state', async (status) => {
const { container } = setUp(status); const { container, user } = setUp(status);
container.firstChild && fireEvent.mouseOver(container.firstChild); container.firstElementChild && await user.hover(container.firstElementChild);
expect(await screen.findByRole('tooltip')).toMatchSnapshot(); expect(await screen.findByRole('tooltip')).toMatchSnapshot();
}); });
}); });

View file

@ -1,4 +1,5 @@
import { fireEvent, render, screen } from '@testing-library/react'; import { fireEvent, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Mock } from 'ts-mockery'; import { Mock } from 'ts-mockery';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { EditServer as editServerConstruct } from '../../src/servers/EditServer'; import { EditServer as editServerConstruct } from '../../src/servers/EditServer';
@ -17,9 +18,10 @@ describe('<EditServer />', () => {
apiKey: 'the_api_key', apiKey: 'the_api_key',
}); });
const EditServer = editServerConstruct(ServerError); const EditServer = editServerConstruct(ServerError);
const setUp = (selectedServer: SelectedServer = defaultSelectedServer) => render( const setUp = (selectedServer: SelectedServer = defaultSelectedServer) => ({
<EditServer editServer={editServerMock} selectedServer={selectedServer} selectServer={jest.fn()} />, user: userEvent.setup(),
); ...render(<EditServer editServer={editServerMock} selectedServer={selectedServer} selectServer={jest.fn()} />),
});
beforeEach(() => { beforeEach(() => {
(useNavigate as any).mockReturnValue(navigate); (useNavigate as any).mockReturnValue(navigate);
@ -48,16 +50,18 @@ describe('<EditServer />', () => {
expect(screen.getByDisplayValue('the_api_key')).toBeInTheDocument(); expect(screen.getByDisplayValue('the_api_key')).toBeInTheDocument();
}); });
it('edits server and redirects to it when form is submitted', () => { it('edits server and redirects to it when form is submitted', async () => {
setUp(); const { user } = setUp();
fireEvent.change(screen.getByDisplayValue('the_name'), { target: { value: 'the_new_name' } }); await user.type(screen.getByDisplayValue('the_name'), ' edited');
fireEvent.change(screen.getByDisplayValue('the_url'), { target: { value: 'the_new_url' } }); await user.type(screen.getByDisplayValue('the_url'), ' edited');
// TODO Using fire event because userEvent.click on the Submit button does not submit the form
// await user.click(screen.getByRole('button', { name: 'Save' }));
fireEvent.submit(screen.getByRole('form')); fireEvent.submit(screen.getByRole('form'));
expect(editServerMock).toHaveBeenCalledWith('abc123', { expect(editServerMock).toHaveBeenCalledWith('abc123', {
name: 'the_new_name', name: 'the_name edited',
url: 'the_new_url', url: 'the_url edited',
apiKey: 'the_api_key', apiKey: 'the_api_key',
}); });
expect(navigate).toHaveBeenCalledWith(-1); expect(navigate).toHaveBeenCalledWith(-1);