mirror of
https://github.com/Graylog2/graylog2-server.git
synced 2026-03-13 09:32:21 +08:00
Improve test reliability in preparation of React 18. (#17990)
* Improve test reliability in preparation of React 18. * Improving more tests. * Updating `react-select-event`. * Improving more tests. * Improving test. * Using `act` when needed. * Migrate `connect` test to testing-library. * Make sure enzyme types are present.
This commit is contained in:
@@ -28,7 +28,7 @@
|
||||
"jest-environment-jsdom": "29.6.2",
|
||||
"jest-enzyme": "^7.1.2",
|
||||
"jest-styled-components": "7.2.0",
|
||||
"react-select-event": "^5.0.0",
|
||||
"react-select-event": "5.5.1",
|
||||
"resize-observer-polyfill": "^1.5.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import { render, screen, act, fireEvent, waitFor } from 'wrappedTestingLibrary';
|
||||
|
||||
import { HTTPHeaderAuthConfigActions } from 'stores/authentication/HTTPHeaderAuthConfigStore';
|
||||
import HTTPHeaderAuthConfig from 'logic/authentication/HTTPHeaderAuthConfig';
|
||||
import asMock from 'helpers/mocking/AsMock';
|
||||
|
||||
import HTTPHeaderAuthConfigSection from './HTTPHeaderAuthConfigSection';
|
||||
|
||||
@@ -39,6 +40,7 @@ describe('<HTTPHeaderAuthConfigSection />', () => {
|
||||
|
||||
it('should display loading indicator while loading', async () => {
|
||||
jest.useFakeTimers();
|
||||
asMock(HTTPHeaderAuthConfigActions.load).mockImplementationOnce(() => new Promise(() => {}));
|
||||
|
||||
render(<HTTPHeaderAuthConfigSection />);
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import { render, screen, waitFor } from 'wrappedTestingLibrary';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import MenuItem from 'components/bootstrap/MenuItem';
|
||||
|
||||
@@ -30,7 +31,7 @@ describe('DropdownButton', () => {
|
||||
));
|
||||
|
||||
const button = await screen.findByRole('button', { name: 'Click me!' });
|
||||
button.click();
|
||||
await userEvent.click(button);
|
||||
|
||||
await screen.findByRole('menuitem', { name: 'Hey there!' });
|
||||
});
|
||||
@@ -45,10 +46,10 @@ describe('DropdownButton', () => {
|
||||
));
|
||||
|
||||
const button = await screen.findByRole('button', { name: 'Click me!' });
|
||||
button.click();
|
||||
await userEvent.click(button);
|
||||
|
||||
const menuitem = await screen.findByRole('menuitem', { name: 'Hey there!' });
|
||||
menuitem.click();
|
||||
await userEvent.click(menuitem);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
|
||||
@@ -19,6 +19,7 @@ import React from 'react';
|
||||
import { screen, render } from 'wrappedTestingLibrary';
|
||||
import Immutable from 'immutable';
|
||||
import { defaultUser } from 'defaultMockValues';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { adminUser } from 'fixtures/users';
|
||||
import MockAction from 'helpers/mocking/MockAction';
|
||||
@@ -61,7 +62,7 @@ describe('<URLWhiteListFormModal>', () => {
|
||||
|
||||
expect(addButton).toBeInTheDocument();
|
||||
|
||||
addButton.click();
|
||||
await userEvent.click(addButton);
|
||||
|
||||
expect(await screen.findByText('Whitelist URLs')).toBeInTheDocument();
|
||||
expect(screen.getByDisplayValue('http://graylog.com')).toBeInTheDocument();
|
||||
@@ -110,7 +111,7 @@ describe('<URLWhiteListFormModal>', () => {
|
||||
|
||||
expect(addButton).toBeInTheDocument();
|
||||
|
||||
addButton.click();
|
||||
await userEvent.click(addButton);
|
||||
|
||||
expect(await screen.findByText('Whitelist URLs')).toBeInTheDocument();
|
||||
expect(screen.getByDisplayValue('http://localhost(:\\d+)?')).toBeInTheDocument();
|
||||
|
||||
@@ -64,14 +64,14 @@ describe('UrlWhitelistForm', () => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('should show allow list toggle and url table', () => {
|
||||
it('should show allow list toggle and url table', async () => {
|
||||
const onUpdate = jest.fn();
|
||||
|
||||
render(<UrlWhiteListForm urls={config.entries}
|
||||
disabled={config.disabled}
|
||||
onUpdate={onUpdate} />);
|
||||
|
||||
expect(screen.getByRole('checkbox', { name: /disable whitelist/i })).toBeInTheDocument();
|
||||
await screen.findByRole('checkbox', { name: /disable whitelist/i });
|
||||
|
||||
config.entries.forEach(({ title }) => {
|
||||
expect(screen.getByDisplayValue(title)).toBeInTheDocument();
|
||||
@@ -117,8 +117,8 @@ describe('UrlWhitelistForm', () => {
|
||||
disabled={config.disabled}
|
||||
onUpdate={onUpdate} />);
|
||||
|
||||
const row = screen.getByRole('row', { name: /3/i, exact: true });
|
||||
const select = within(row).getByText(/exact match/i);
|
||||
const row = await screen.findByRole('row', { name: /3/i });
|
||||
const select = await within(row).findByText(/exact match/i);
|
||||
|
||||
await selectEvent.openMenu(select);
|
||||
await selectEvent.select(select, 'Regex');
|
||||
@@ -138,16 +138,16 @@ describe('UrlWhitelistForm', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should add a new row to the form', () => {
|
||||
it('should add a new row to the form', async () => {
|
||||
const onUpdate = jest.fn();
|
||||
|
||||
render(<UrlWhiteListForm urls={config.entries}
|
||||
disabled={config.disabled}
|
||||
onUpdate={onUpdate} />);
|
||||
|
||||
expect(screen.queryByRole('cell', { name: String(config.entries.length + 1) })).not.toBeInTheDocument();
|
||||
const button = (await screen.findAllByRole('button', { name: /add url/i }))[0];
|
||||
|
||||
const button = screen.getAllByRole('button', { name: /add url/i })[0];
|
||||
expect(screen.queryByRole('cell', { name: String(config.entries.length + 1) })).not.toBeInTheDocument();
|
||||
|
||||
expect(button).toBeInTheDocument();
|
||||
|
||||
@@ -155,7 +155,9 @@ describe('UrlWhitelistForm', () => {
|
||||
|
||||
expect(screen.getByRole('cell', { name: String(config.entries.length + 1) })).toBeInTheDocument();
|
||||
|
||||
expect(onUpdate).toHaveBeenCalledTimes(1);
|
||||
await waitFor(() => {
|
||||
expect(onUpdate).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete a row', async () => {
|
||||
@@ -167,7 +169,7 @@ describe('UrlWhitelistForm', () => {
|
||||
|
||||
expect(screen.getByDisplayValue(config.entries[0].title)).toBeInTheDocument();
|
||||
|
||||
const row = screen.getByRole('row', { name: /1/i, exact: true });
|
||||
const row = screen.getByRole('row', { name: /1/i });
|
||||
const deleteButton = within(row).getByRole('button', { name: /delete entry/i });
|
||||
|
||||
expect(deleteButton).toBeInTheDocument();
|
||||
@@ -244,10 +246,11 @@ describe('UrlWhitelistForm', () => {
|
||||
disabled={config.disabled}
|
||||
onUpdate={onUpdate} />);
|
||||
|
||||
const row = screen.getByRole('row', { name: /3/i, exact: true });
|
||||
const row = screen.getByRole('row', { name: /3/i });
|
||||
const select = within(row).getByText(/exact match/i);
|
||||
|
||||
await selectEvent.openMenu(select);
|
||||
|
||||
await selectEvent.select(select, 'Regex');
|
||||
|
||||
await screen.findByText(/not a valid java regular expression/i);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import { render, fireEvent, waitFor, screen } from 'wrappedTestingLibrary';
|
||||
import { act, render, fireEvent, waitFor, screen } from 'wrappedTestingLibrary';
|
||||
import selectEvent from 'react-select-event';
|
||||
import { List } from 'immutable';
|
||||
|
||||
@@ -79,7 +79,11 @@ describe('<SettingsSection />', () => {
|
||||
await screen.findByText('Hours');
|
||||
|
||||
fireEvent.change(timeoutAmountInput, { target: { value: '40' } });
|
||||
await selectEvent.select(timeoutUnitSelect, 'Days');
|
||||
|
||||
await act(async () => {
|
||||
await selectEvent.select(timeoutUnitSelect, 'Days');
|
||||
});
|
||||
|
||||
await selectEvent.select(timezoneSelect, 'Vancouver');
|
||||
fireEvent.click(submitButton);
|
||||
|
||||
|
||||
@@ -19,9 +19,11 @@ import * as React from 'react';
|
||||
import { render, screen } from 'wrappedTestingLibrary';
|
||||
import { createMemoryRouter, RouterProvider } from 'react-router-dom';
|
||||
import DefaultProviders from 'DefaultProviders';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import Routes from 'routing/Routes';
|
||||
import { usePluginExports } from 'views/test/testPlugins';
|
||||
import suppressConsole from 'helpers/suppressConsole';
|
||||
|
||||
import useLogout from './useLogout';
|
||||
|
||||
@@ -48,7 +50,7 @@ describe('useLogout', () => {
|
||||
await screen.findByText('Logged in');
|
||||
|
||||
const logoutButton = await screen.findByRole('button', { name: 'logout' });
|
||||
logoutButton.click();
|
||||
userEvent.click(logoutButton);
|
||||
|
||||
await screen.findByText('Logged out');
|
||||
});
|
||||
@@ -63,7 +65,7 @@ describe('useLogout', () => {
|
||||
await screen.findByText('Logged in');
|
||||
|
||||
const logoutButton = await screen.findByRole('button', { name: 'logout' });
|
||||
logoutButton.click();
|
||||
userEvent.click(logoutButton);
|
||||
|
||||
await screen.findByText('Logged out');
|
||||
|
||||
@@ -81,7 +83,10 @@ describe('useLogout', () => {
|
||||
await screen.findByText('Logged in');
|
||||
|
||||
const logoutButton = await screen.findByRole('button', { name: 'logout' });
|
||||
logoutButton.click();
|
||||
|
||||
suppressConsole(() => {
|
||||
userEvent.click(logoutButton);
|
||||
});
|
||||
|
||||
await screen.findByText('Logged out');
|
||||
|
||||
|
||||
@@ -14,16 +14,13 @@
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*/
|
||||
/// <reference types="jest-enzyme" />
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { mount } from 'wrappedEnzyme';
|
||||
import { act, render, screen } from 'wrappedTestingLibrary';
|
||||
import Reflux from 'reflux';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Map, List } from 'immutable';
|
||||
|
||||
import { asMock } from 'helpers/mocking/index';
|
||||
import type { Store } from 'stores/StoreTypes';
|
||||
|
||||
import {
|
||||
arrayOfMaps,
|
||||
@@ -39,12 +36,11 @@ import {
|
||||
import connect, { useStore } from '../connect';
|
||||
|
||||
const SimpleComponentWithoutStores = () => <span>Hello World!</span>;
|
||||
|
||||
const SimpleStore = Reflux.createStore<{ value: number }>({
|
||||
const createSimpleStore = () => Reflux.createStore<{ value: number }>({
|
||||
getInitialState() {
|
||||
return this.state;
|
||||
},
|
||||
setValue(value) {
|
||||
setValue(value: number) {
|
||||
this.state = { value };
|
||||
this.trigger(this.state);
|
||||
},
|
||||
@@ -55,6 +51,8 @@ const SimpleStore = Reflux.createStore<{ value: number }>({
|
||||
},
|
||||
});
|
||||
|
||||
const SimpleStore = createSimpleStore();
|
||||
|
||||
const SimpleComponentWithDummyStore = ({ simpleStore }) => {
|
||||
if (simpleStore && simpleStore.value) {
|
||||
return <span>Value is: {simpleStore.value}</span>;
|
||||
@@ -75,61 +73,72 @@ SimpleComponentWithDummyStore.defaultProps = {
|
||||
|
||||
describe('connect()', () => {
|
||||
beforeEach(() => {
|
||||
SimpleStore.reset();
|
||||
act(() => {
|
||||
SimpleStore.reset();
|
||||
});
|
||||
});
|
||||
|
||||
it('does not do anything if no stores are provided', () => {
|
||||
it('does not do anything if no stores are provided', async () => {
|
||||
const Component = connect(SimpleComponentWithoutStores, {});
|
||||
const wrapper = mount(<Component />);
|
||||
render(<Component />);
|
||||
|
||||
expect(wrapper).toHaveHTML('<span>Hello World!</span>');
|
||||
await screen.findByText('Hello World!');
|
||||
});
|
||||
|
||||
it('connects component to store without state', () => {
|
||||
it('connects component to store without state', async () => {
|
||||
const Component = connect(SimpleComponentWithDummyStore, { simpleStore: SimpleStore });
|
||||
const wrapper = mount(<Component />);
|
||||
render(<Component />);
|
||||
|
||||
expect(wrapper).toHaveText('No value.');
|
||||
await screen.findByText('No value.');
|
||||
});
|
||||
|
||||
it('connects component to store with state', () => {
|
||||
SimpleStore.setValue(42);
|
||||
const Component = connect(SimpleComponentWithDummyStore, { simpleStore: SimpleStore });
|
||||
const wrapper = mount(<Component />);
|
||||
it('connects component to store with state', async () => {
|
||||
act(() => {
|
||||
SimpleStore.setValue(42);
|
||||
});
|
||||
|
||||
expect(wrapper).toHaveText('Value is: 42');
|
||||
const Component = connect(SimpleComponentWithDummyStore, { simpleStore: SimpleStore });
|
||||
render(<Component />);
|
||||
|
||||
await screen.findByText('Value is: 42');
|
||||
});
|
||||
|
||||
it('reflects state changes in store', () => {
|
||||
it('reflects state changes in store', async () => {
|
||||
const Component = connect(SimpleComponentWithDummyStore, { simpleStore: SimpleStore });
|
||||
const wrapper = mount(<Component />);
|
||||
render(<Component />);
|
||||
|
||||
expect(wrapper).toHaveText('No value.');
|
||||
await screen.findByText('No value.');
|
||||
|
||||
SimpleStore.setValue(42);
|
||||
act(() => {
|
||||
SimpleStore.setValue(42);
|
||||
});
|
||||
|
||||
expect(wrapper).toHaveText('Value is: 42');
|
||||
await screen.findByText('Value is: 42');
|
||||
|
||||
SimpleStore.noop();
|
||||
|
||||
expect(wrapper).toHaveText('Value is: 42');
|
||||
await screen.findByText('Value is: 42');
|
||||
|
||||
SimpleStore.reset();
|
||||
act(() => {
|
||||
SimpleStore.reset();
|
||||
});
|
||||
|
||||
expect(wrapper).toHaveText('No value.');
|
||||
await screen.findByText('No value.');
|
||||
});
|
||||
|
||||
it('allows mangling of props before passing them', () => {
|
||||
it('allows mangling of props before passing them', async () => {
|
||||
const Component = connect(
|
||||
SimpleComponentWithDummyStore,
|
||||
{ simpleStore: SimpleStore },
|
||||
({ simpleStore }) => (simpleStore && { simpleStore: { value: simpleStore.value * 2 } }),
|
||||
);
|
||||
const wrapper = mount(<Component />);
|
||||
render(<Component />);
|
||||
|
||||
SimpleStore.setValue(42);
|
||||
act(() => {
|
||||
SimpleStore.setValue(42);
|
||||
});
|
||||
|
||||
expect(wrapper).toHaveText('Value is: 84');
|
||||
await screen.findByText('Value is: 84');
|
||||
});
|
||||
|
||||
it('adds meaningful name to wrapper component', () => {
|
||||
@@ -144,22 +153,26 @@ describe('connect()', () => {
|
||||
expect(Component.displayName).toEqual('ConnectStoresWrapper[Unknown/Anonymous] stores=simpleStore');
|
||||
});
|
||||
|
||||
it('types store props as optional', () => {
|
||||
it('types store props as optional', async () => {
|
||||
const Component = connect(() => <span>hello!</span>, { simpleStore: SimpleStore });
|
||||
mount(<Component />);
|
||||
render(<Component />);
|
||||
await screen.findByText('hello!');
|
||||
});
|
||||
|
||||
it('types mapped props as optional', () => {
|
||||
it('types mapped props as optional', async () => {
|
||||
const Component = connect(
|
||||
() => <span>hello!</span>,
|
||||
{ simpleStore: SimpleStore },
|
||||
({ simpleStore }) => (simpleStore && { storeValue: simpleStore.value }),
|
||||
);
|
||||
mount(<Component />);
|
||||
render(<Component />);
|
||||
await screen.findByText('hello!');
|
||||
});
|
||||
|
||||
it('types props which have a default value (defaultProps) as optional', () => {
|
||||
const BaseComponent = ({ exampleProp }: { exampleProp: string }) => <span>{exampleProp}</span>;
|
||||
it('types props which have a default value (defaultProps) as optional', async () => {
|
||||
const BaseComponent = ({ exampleProp }: {
|
||||
exampleProp: string
|
||||
}) => <span>{exampleProp}</span>;
|
||||
|
||||
BaseComponent.defaultProps = {
|
||||
exampleProp: 'hello!',
|
||||
@@ -170,50 +183,38 @@ describe('connect()', () => {
|
||||
};
|
||||
|
||||
const Component = connect(BaseComponent, { simpleStore: SimpleStore });
|
||||
mount(<Component />);
|
||||
render(<Component />);
|
||||
|
||||
await screen.findByText('hello!');
|
||||
});
|
||||
|
||||
describe('generates `shouldComponentUpdate`', () => {
|
||||
const Component: React.ComponentType<{ someProp?: any, foo: number }> = jest.fn(() => <span>Hello!</span>);
|
||||
const SimplestStore: Store<number> = ({
|
||||
getInitialState: jest.fn(() => 42),
|
||||
listen: jest.fn(() => () => {}),
|
||||
});
|
||||
|
||||
afterEach(() => { jest.clearAllMocks(); });
|
||||
|
||||
it('comparing empty values properly', () => {
|
||||
it('comparing empty values properly', async () => {
|
||||
const ComponentClass = connect(Component, {});
|
||||
|
||||
const wrapper = mount(<ComponentClass foo={42} />);
|
||||
const { rerender } = render(<ComponentClass foo={42} />);
|
||||
|
||||
wrapper.setProps({});
|
||||
// @ts-expect-error
|
||||
rerender(<ComponentClass />);
|
||||
|
||||
expect(Component).toHaveBeenCalledTimes(1);
|
||||
await screen.findByText('Hello!');
|
||||
});
|
||||
|
||||
const verifyShouldComponentUpdate = ({ initial, next, result }) => {
|
||||
asMock(SimplestStore.getInitialState).mockReturnValue(initial);
|
||||
const ComponentClass = connect(Component, { foo: SimplestStore });
|
||||
SimpleStore.setValue(initial);
|
||||
const ComponentClass = connect(Component, { foo: SimpleStore });
|
||||
|
||||
mount(<ComponentClass />);
|
||||
const update = asMock(SimplestStore.listen).mock.calls[0][0];
|
||||
render(<ComponentClass />);
|
||||
|
||||
asMock(Component).mockClear();
|
||||
|
||||
update(next);
|
||||
|
||||
if (result) {
|
||||
expect(Component).toHaveBeenCalled();
|
||||
} else {
|
||||
expect(Component).not.toHaveBeenCalled();
|
||||
}
|
||||
|
||||
const wrapper = mount(<ComponentClass someProp={initial} />);
|
||||
|
||||
asMock(Component).mockClear();
|
||||
|
||||
wrapper.setProps({ someProp: next });
|
||||
act(() => {
|
||||
SimpleStore.setValue(next);
|
||||
});
|
||||
|
||||
if (result) {
|
||||
expect(Component).toHaveBeenCalled();
|
||||
@@ -222,6 +223,7 @@ describe('connect()', () => {
|
||||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line jest/expect-expect
|
||||
it.each`
|
||||
initial | next | result | description
|
||||
${undefined} | ${undefined} | ${false} | ${'equal undefined values'}
|
||||
@@ -268,51 +270,51 @@ describe('useStore', () => {
|
||||
act(() => SimpleStore.reset());
|
||||
});
|
||||
|
||||
it('renders state from store', () => {
|
||||
const wrapper = mount(<SimpleComponent />);
|
||||
it('renders state from store', async () => {
|
||||
render(<SimpleComponent />);
|
||||
|
||||
expect(wrapper).toHaveText('No value.');
|
||||
await screen.findByText('No value.');
|
||||
});
|
||||
|
||||
it('connects component to store with state', () => {
|
||||
it('connects component to store with state', async () => {
|
||||
act(() => SimpleStore.setValue(42));
|
||||
const wrapper = mount(<SimpleComponent />);
|
||||
render(<SimpleComponent />);
|
||||
|
||||
expect(wrapper).toHaveText('Value is: 42');
|
||||
await screen.findByText('Value is: 42');
|
||||
});
|
||||
|
||||
it('reflects state changes from store', () => {
|
||||
const wrapper = mount(<SimpleComponent />);
|
||||
it('reflects state changes from store', async () => {
|
||||
render(<SimpleComponent />);
|
||||
|
||||
expect(wrapper).toHaveText('No value.');
|
||||
await screen.findByText('No value.');
|
||||
|
||||
act(() => SimpleStore.setValue(42));
|
||||
|
||||
expect(wrapper).toHaveText('Value is: 42');
|
||||
await screen.findByText('Value is: 42');
|
||||
|
||||
act(() => SimpleStore.noop());
|
||||
|
||||
expect(wrapper).toHaveText('Value is: 42');
|
||||
await screen.findByText('Value is: 42');
|
||||
|
||||
act(() => SimpleStore.reset());
|
||||
|
||||
expect(wrapper).toHaveText('No value.');
|
||||
await screen.findByText('No value.');
|
||||
});
|
||||
|
||||
it('allows mangling of props before passing them', () => {
|
||||
it('allows mangling of props before passing them', async () => {
|
||||
const Component = () => {
|
||||
const { value } = useStore(SimpleStore, ({ value: v } = { value: 0 }) => ({ value: v * 2 })) || {};
|
||||
|
||||
return <span>{value ? `Value is: ${value}` : 'No value.'}</span>;
|
||||
};
|
||||
|
||||
const wrapper = mount(<Component />);
|
||||
render(<Component />);
|
||||
|
||||
expect(wrapper).toHaveText('No value.');
|
||||
await screen.findByText('No value.');
|
||||
|
||||
act(() => SimpleStore.setValue(42));
|
||||
|
||||
expect(wrapper).toHaveText('Value is: 84');
|
||||
await screen.findByText('Value is: 84');
|
||||
});
|
||||
|
||||
it('does not rerender component if state does not change', () => {
|
||||
@@ -325,7 +327,7 @@ describe('useStore', () => {
|
||||
return <span>{value ? `Value is: ${value}` : 'No value.'}</span>;
|
||||
};
|
||||
|
||||
mount(<SimpleComponentWithRenderCounter />);
|
||||
render(<SimpleComponentWithRenderCounter />);
|
||||
|
||||
const beforeFirstSet = renderCount;
|
||||
|
||||
@@ -340,7 +342,7 @@ describe('useStore', () => {
|
||||
expect(renderCount).toEqual(beforeSecondSet);
|
||||
});
|
||||
|
||||
it('does not reregister if props mapper is provided as arrow function', () => {
|
||||
it('does not reregister if props mapper is provided as arrow function', async () => {
|
||||
const listenSpy = jest.spyOn(SimpleStore, 'listen');
|
||||
|
||||
const ComponentWithPropsMapper = ({ propsMapper }: { propsMapper: (state: { value: number }) => ({ value: number }) }) => {
|
||||
@@ -349,14 +351,14 @@ describe('useStore', () => {
|
||||
return <span>{value ? `Value is: ${value}` : 'No value.'}</span>;
|
||||
};
|
||||
|
||||
const wrapper = mount(<ComponentWithPropsMapper propsMapper={(x) => x} />);
|
||||
const { rerender } = render(<ComponentWithPropsMapper propsMapper={(x) => x} />);
|
||||
|
||||
expect(wrapper).toHaveText('No value.');
|
||||
await screen.findByText('No value.');
|
||||
|
||||
wrapper.setProps({ propsMapper: ({ value: v } = { value: 0 }) => ({ value: v * 2 }) });
|
||||
rerender(<ComponentWithPropsMapper propsMapper={({ value: v } = { value: 0 }) => ({ value: v * 2 })} />);
|
||||
act(() => SimpleStore.setValue(42));
|
||||
|
||||
expect(wrapper).toHaveText('Value is: 84');
|
||||
await screen.findByText('Value is: 84');
|
||||
|
||||
expect(listenSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import { render, screen, waitFor } from 'wrappedTestingLibrary';
|
||||
import { act, render, screen, waitFor } from 'wrappedTestingLibrary';
|
||||
import * as Immutable from 'immutable';
|
||||
import selectEvent from 'react-select-event';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
@@ -84,7 +84,10 @@ const visualizationSelect = async () => screen.findByLabelText('Select visualiza
|
||||
const selectOption = async (ariaLabel: string, option: string) => {
|
||||
const select = await screen.findByLabelText(ariaLabel);
|
||||
await selectEvent.openMenu(select);
|
||||
await selectEvent.select(select, option, selectEventConfig);
|
||||
|
||||
await act(async () => {
|
||||
await selectEvent.select(select, option, selectEventConfig);
|
||||
});
|
||||
};
|
||||
|
||||
describe('AggregationWizard/Core Visualizations', () => {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import { render, screen, waitFor } from 'wrappedTestingLibrary';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { MenuItem } from 'components/bootstrap';
|
||||
|
||||
@@ -33,7 +34,7 @@ describe('ActionDropdown', () => {
|
||||
|
||||
expect(screen.queryByText('Foo')).not.toBeInTheDocument();
|
||||
|
||||
triggerButton.click();
|
||||
await userEvent.click(triggerButton);
|
||||
|
||||
await screen.findByRole('menuitem', { name: 'Foo' });
|
||||
});
|
||||
@@ -53,13 +54,11 @@ describe('ActionDropdown', () => {
|
||||
|
||||
expect(screen.queryByText('Foo')).not.toBeInTheDocument();
|
||||
|
||||
triggerButton.click();
|
||||
await userEvent.click(triggerButton);
|
||||
|
||||
await screen.findByRole('menuitem', { name: 'Foo' });
|
||||
|
||||
expect(onClick).not.toHaveBeenCalled();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole('menuitem', { name: 'Foo' })).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('closes menu when MenuItem is clicked', async () => {
|
||||
@@ -73,10 +72,10 @@ describe('ActionDropdown', () => {
|
||||
|
||||
const triggerButton = await screen.findByText('Trigger!');
|
||||
|
||||
triggerButton.click();
|
||||
await userEvent.click(triggerButton);
|
||||
|
||||
const menuItem = await screen.findByRole('menuitem', { name: 'Foo' });
|
||||
menuItem.click();
|
||||
await userEvent.click(menuItem);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onSelect).toHaveBeenCalled();
|
||||
@@ -100,10 +99,10 @@ describe('ActionDropdown', () => {
|
||||
|
||||
const triggerButton = await screen.findByText('Trigger!');
|
||||
|
||||
triggerButton.click();
|
||||
await userEvent.click(triggerButton);
|
||||
|
||||
const menuItem = await screen.findByRole('menuitem', { name: 'Foo' });
|
||||
menuItem.click();
|
||||
await userEvent.click(menuItem);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onSelect).toHaveBeenCalled();
|
||||
@@ -128,10 +127,10 @@ describe('ActionDropdown', () => {
|
||||
|
||||
const triggerButton = await screen.findByText('Trigger!');
|
||||
|
||||
triggerButton.click();
|
||||
await userEvent.click(triggerButton);
|
||||
|
||||
const menuItem = await screen.findByRole('menuitem', { name: 'Foo' });
|
||||
menuItem.click();
|
||||
await userEvent.click(menuItem);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onSelect).toHaveBeenCalled();
|
||||
|
||||
@@ -69,11 +69,11 @@ describe('BigDisplayModeConfiguration', () => {
|
||||
<BigDisplayModeConfiguration view={view} {...props} />
|
||||
));
|
||||
|
||||
it('disables menu item if `disabled` prop is `true`', () => {
|
||||
const { getByText, queryByText } = render(<SUT disabled />);
|
||||
const menuItem = getByText('Full Screen');
|
||||
it('disables menu item if `disabled` prop is `true`', async () => {
|
||||
const { queryByText, findByText } = render(<SUT disabled />);
|
||||
const menuItem = await findByText('Full Screen');
|
||||
|
||||
fireEvent.submit(menuItem);
|
||||
fireEvent.click(menuItem);
|
||||
|
||||
expect(queryByText('Configuring Full Screen')).toBeNull();
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import * as Immutable from 'immutable';
|
||||
import { render, fireEvent, waitFor, screen } from 'wrappedTestingLibrary';
|
||||
import { act, render, fireEvent, waitFor, screen } from 'wrappedTestingLibrary';
|
||||
|
||||
import QueryTitleEditModal from './QueryTitleEditModal';
|
||||
|
||||
@@ -25,7 +25,9 @@ describe('QueryTitleEditModal', () => {
|
||||
|
||||
const openModal = (modalRef, currentTitle = 'CurrentTitle') => {
|
||||
if (modalRef) {
|
||||
modalRef.open(currentTitle);
|
||||
act(() => {
|
||||
modalRef.open(currentTitle);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -35,9 +35,11 @@ const defaultProps = {
|
||||
describe('AbsoluteDateInput', () => {
|
||||
beforeAll(() => { jest.clearAllMocks(); });
|
||||
|
||||
it('renders with minimal props', () => {
|
||||
it('renders with minimal props', async () => {
|
||||
render(<AbsoluteDateInput {...defaultProps} />);
|
||||
|
||||
await screen.findByRole('button', { name: /insert current date/i });
|
||||
|
||||
expect(screen).not.toBeNull();
|
||||
});
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import { fireEvent, render, screen } from 'wrappedTestingLibrary';
|
||||
import { render, screen } from 'wrappedTestingLibrary';
|
||||
import { Formik, Form } from 'formik';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import TabAbsoluteTimeRange from './TabAbsoluteTimeRange';
|
||||
|
||||
@@ -39,7 +40,7 @@ const renderWithForm = (element) => render((
|
||||
));
|
||||
|
||||
describe('TabAbsoluteTimeRange', () => {
|
||||
it('renders Accordions that work', () => {
|
||||
it('renders Accordions that work', async () => {
|
||||
renderWithForm((
|
||||
<TabAbsoluteTimeRange {...defaultProps} />
|
||||
));
|
||||
@@ -52,9 +53,8 @@ describe('TabAbsoluteTimeRange', () => {
|
||||
expect(accordionItemCal.getAttribute('aria-expanded')).toEqual('true');
|
||||
expect(accordionItemTime.getAttribute('aria-expanded')).toEqual('false');
|
||||
|
||||
fireEvent.click(accordionItemTime);
|
||||
userEvent.click(accordionItemTime);
|
||||
|
||||
expect(accordionItemCal.getAttribute('aria-expanded')).toEqual('false');
|
||||
expect(accordionItemTime.getAttribute('aria-expanded')).toEqual('true');
|
||||
await screen.findByText(/Date should be formatted as/i);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -74,7 +74,7 @@ describe('CopyToDashboardForm', () => {
|
||||
fireEvent.click(submitButton);
|
||||
};
|
||||
|
||||
it('should render the modal minimal', () => {
|
||||
it('should render the modal minimal', async () => {
|
||||
asMock(useDashboards).mockReturnValue({
|
||||
data: {
|
||||
list: [],
|
||||
@@ -96,33 +96,40 @@ describe('CopyToDashboardForm', () => {
|
||||
refetch: () => {},
|
||||
});
|
||||
|
||||
const { baseElement } = render(<SUT />);
|
||||
render(<SUT />);
|
||||
|
||||
expect(baseElement).not.toBeNull();
|
||||
await screen.findByText(/no dashboards found/i);
|
||||
});
|
||||
|
||||
it('should render the modal with entries', () => {
|
||||
const { baseElement } = render(<SUT />);
|
||||
it('should render the modal with entries', async () => {
|
||||
render(<SUT />);
|
||||
|
||||
expect(baseElement).not.toBeNull();
|
||||
await screen.findByText('view 1');
|
||||
await screen.findByText('view 2');
|
||||
});
|
||||
|
||||
it('should handle onCancel', () => {
|
||||
it('should handle onCancel', async () => {
|
||||
const onCancel = jest.fn();
|
||||
const { getByText } = render(<SUT onCancel={onCancel} />);
|
||||
const cancelButton = getByText('Cancel');
|
||||
|
||||
fireEvent.click(cancelButton);
|
||||
|
||||
expect(onCancel).toHaveBeenCalledTimes(1);
|
||||
await waitFor(() => {
|
||||
expect(onCancel).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not handle onSubmit without selection', () => {
|
||||
it('should not handle onSubmit without selection', async () => {
|
||||
const onSubmit = jest.fn(() => Promise.resolve());
|
||||
|
||||
render(<SUT />);
|
||||
|
||||
submitModal();
|
||||
const submitButton = await screen.findByRole('button', { name: /submit/i, hidden: true });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(submitButton).toBeDisabled();
|
||||
});
|
||||
|
||||
expect(onSubmit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -27,9 +27,10 @@ describe('FieldSortIcon', () => {
|
||||
const currentSort = new SortConfig(SortConfig.PIVOT_TYPE, 'timestamp', Direction.Descending);
|
||||
const config = new MessagesWidgetConfig(['timestamp', 'source'], true, true, [], [currentSort]);
|
||||
|
||||
it('should set descending sort on click, if field sort is not defined', () => {
|
||||
it('should set descending sort on click, if field sort is not defined', async () => {
|
||||
const onSortChangeStub = jest.fn(() => Promise.resolve());
|
||||
const { getByTitle } = render(<FieldSortIcon config={config} fieldName="source" onSortChange={onSortChangeStub} setLoadingState={() => {}} />);
|
||||
const setLoadingState = jest.fn();
|
||||
const { getByTitle } = render(<FieldSortIcon config={config} fieldName="source" onSortChange={onSortChangeStub} setLoadingState={setLoadingState} />);
|
||||
|
||||
const sortIcon = getByTitle('Sort source Descending');
|
||||
|
||||
@@ -39,6 +40,10 @@ describe('FieldSortIcon', () => {
|
||||
|
||||
expect(onSortChangeStub).toHaveBeenCalledTimes(1);
|
||||
expect(onSortChangeStub).toHaveBeenCalledWith(expectedSort);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(setLoadingState).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should set ascending sort on click, if field sort is descending', () => {
|
||||
@@ -55,12 +60,13 @@ describe('FieldSortIcon', () => {
|
||||
expect(onSortChangeStub).toHaveBeenCalledWith(expectedSort);
|
||||
});
|
||||
|
||||
it('should set descending sort on click, if field sort is descending', () => {
|
||||
it('should set descending sort on click, if field sort is descending', async () => {
|
||||
const initialSort = new SortConfig(SortConfig.PIVOT_TYPE, 'source', Direction.Ascending);
|
||||
const initialConfig = new MessagesWidgetConfig(['timestamp', 'source'], true, true, [], [initialSort]);
|
||||
const onSortChangeStub = jest.fn(() => Promise.resolve());
|
||||
const setLoadingState = jest.fn();
|
||||
|
||||
const { getByTitle } = render(<FieldSortIcon config={initialConfig} fieldName="source" onSortChange={onSortChangeStub} setLoadingState={() => {}} />);
|
||||
const { getByTitle } = render(<FieldSortIcon config={initialConfig} fieldName="source" onSortChange={onSortChangeStub} setLoadingState={setLoadingState} />);
|
||||
|
||||
const sortIcon = getByTitle('Sort source Descending');
|
||||
|
||||
@@ -70,6 +76,10 @@ describe('FieldSortIcon', () => {
|
||||
|
||||
expect(onSortChangeStub).toHaveBeenCalledTimes(1);
|
||||
expect(onSortChangeStub).toHaveBeenCalledWith(expectedSort);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(setLoadingState).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should set loading state while changing sort', async () => {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import { render, screen, waitFor } from 'wrappedTestingLibrary';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { MenuItem } from 'components/bootstrap';
|
||||
|
||||
@@ -33,7 +34,7 @@ describe('WidgetActionDropdown', () => {
|
||||
|
||||
expect(screen.queryByText('Foo')).not.toBeInTheDocument();
|
||||
|
||||
menuButton.click();
|
||||
await userEvent.click(menuButton);
|
||||
|
||||
await screen.findByRole('menuitem', { name: 'Foo' });
|
||||
});
|
||||
@@ -48,10 +49,10 @@ describe('WidgetActionDropdown', () => {
|
||||
));
|
||||
|
||||
const menuButton = await screen.findByRole('button', { name: /open actions dropdown/i });
|
||||
menuButton.click();
|
||||
await userEvent.click(menuButton);
|
||||
|
||||
const fooAction = await screen.findByRole('menuitem', { name: 'Foo' });
|
||||
fooAction.click();
|
||||
await userEvent.click(fooAction);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('Foo')).not.toBeInTheDocument();
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*/
|
||||
/// <reference types="jest-enzyme" />
|
||||
import type * as React from 'react';
|
||||
import type { ReactWrapper, ShallowWrapper } from 'enzyme';
|
||||
import { configure, mount, shallow } from 'enzyme';
|
||||
|
||||
Reference in New Issue
Block a user