diff --git a/graylog2-web-interface/packages/jest-preset-graylog/package.json b/graylog2-web-interface/packages/jest-preset-graylog/package.json
index 81c69ef7c6ae..2536b7553b21 100644
--- a/graylog2-web-interface/packages/jest-preset-graylog/package.json
+++ b/graylog2-web-interface/packages/jest-preset-graylog/package.json
@@ -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"
}
}
diff --git a/graylog2-web-interface/src/components/authentication/AuthenticatorsEdit/HTTPHeaderAuthConfigSection.test.tsx b/graylog2-web-interface/src/components/authentication/AuthenticatorsEdit/HTTPHeaderAuthConfigSection.test.tsx
index dc46330274d7..0abbf3941543 100644
--- a/graylog2-web-interface/src/components/authentication/AuthenticatorsEdit/HTTPHeaderAuthConfigSection.test.tsx
+++ b/graylog2-web-interface/src/components/authentication/AuthenticatorsEdit/HTTPHeaderAuthConfigSection.test.tsx
@@ -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('', () => {
it('should display loading indicator while loading', async () => {
jest.useFakeTimers();
+ asMock(HTTPHeaderAuthConfigActions.load).mockImplementationOnce(() => new Promise(() => {}));
render();
diff --git a/graylog2-web-interface/src/components/bootstrap/DropdownButton.test.tsx b/graylog2-web-interface/src/components/bootstrap/DropdownButton.test.tsx
index aef6bdc92125..34b0daeb3a74 100644
--- a/graylog2-web-interface/src/components/bootstrap/DropdownButton.test.tsx
+++ b/graylog2-web-interface/src/components/bootstrap/DropdownButton.test.tsx
@@ -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();
diff --git a/graylog2-web-interface/src/components/common/URLWhiteListFormModal.test.tsx b/graylog2-web-interface/src/components/common/URLWhiteListFormModal.test.tsx
index c531ae93a8eb..b5566eb31944 100644
--- a/graylog2-web-interface/src/components/common/URLWhiteListFormModal.test.tsx
+++ b/graylog2-web-interface/src/components/common/URLWhiteListFormModal.test.tsx
@@ -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('', () => {
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('', () => {
expect(addButton).toBeInTheDocument();
- addButton.click();
+ await userEvent.click(addButton);
expect(await screen.findByText('Whitelist URLs')).toBeInTheDocument();
expect(screen.getByDisplayValue('http://localhost(:\\d+)?')).toBeInTheDocument();
diff --git a/graylog2-web-interface/src/components/configurations/UrlWhiteListForm.test.tsx b/graylog2-web-interface/src/components/configurations/UrlWhiteListForm.test.tsx
index e8adb97bc2ea..38e93daac8c9 100644
--- a/graylog2-web-interface/src/components/configurations/UrlWhiteListForm.test.tsx
+++ b/graylog2-web-interface/src/components/configurations/UrlWhiteListForm.test.tsx
@@ -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();
- 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();
- 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);
diff --git a/graylog2-web-interface/src/components/users/UserEdit/SettingsSection.test.tsx b/graylog2-web-interface/src/components/users/UserEdit/SettingsSection.test.tsx
index 2e6b823ab9e1..0a74d24db86b 100644
--- a/graylog2-web-interface/src/components/users/UserEdit/SettingsSection.test.tsx
+++ b/graylog2-web-interface/src/components/users/UserEdit/SettingsSection.test.tsx
@@ -15,7 +15,7 @@
* .
*/
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('', () => {
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);
diff --git a/graylog2-web-interface/src/hooks/useLogout.test.tsx b/graylog2-web-interface/src/hooks/useLogout.test.tsx
index 7a22c420d0f0..81394e6cb49f 100644
--- a/graylog2-web-interface/src/hooks/useLogout.test.tsx
+++ b/graylog2-web-interface/src/hooks/useLogout.test.tsx
@@ -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');
diff --git a/graylog2-web-interface/src/stores/__tests__/connect.test.tsx b/graylog2-web-interface/src/stores/__tests__/connect.test.tsx
index f528d1ee950c..0ce36f14fbb8 100644
--- a/graylog2-web-interface/src/stores/__tests__/connect.test.tsx
+++ b/graylog2-web-interface/src/stores/__tests__/connect.test.tsx
@@ -14,16 +14,13 @@
* along with this program. If not, see
* .
*/
-///
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 = () => Hello World!;
-
-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 Value is: {simpleStore.value};
@@ -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();
+ render();
- expect(wrapper).toHaveHTML('Hello World!');
+ 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();
+ render();
- expect(wrapper).toHaveText('No value.');
+ await screen.findByText('No value.');
});
- it('connects component to store with state', () => {
- SimpleStore.setValue(42);
+ it('connects component to store with state', async () => {
+ act(() => {
+ SimpleStore.setValue(42);
+ });
+
const Component = connect(SimpleComponentWithDummyStore, { simpleStore: SimpleStore });
- const wrapper = mount();
+ render();
- expect(wrapper).toHaveText('Value is: 42');
+ 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();
+ render();
- 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();
+ render();
- 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(() => hello!, { simpleStore: SimpleStore });
- mount();
+ render();
+ await screen.findByText('hello!');
});
- it('types mapped props as optional', () => {
+ it('types mapped props as optional', async () => {
const Component = connect(
() => hello!,
{ simpleStore: SimpleStore },
({ simpleStore }) => (simpleStore && { storeValue: simpleStore.value }),
);
- mount();
+ render();
+ await screen.findByText('hello!');
});
- it('types props which have a default value (defaultProps) as optional', () => {
- const BaseComponent = ({ exampleProp }: { exampleProp: string }) => {exampleProp};
+ it('types props which have a default value (defaultProps) as optional', async () => {
+ const BaseComponent = ({ exampleProp }: {
+ exampleProp: string
+ }) => {exampleProp};
BaseComponent.defaultProps = {
exampleProp: 'hello!',
@@ -170,50 +183,38 @@ describe('connect()', () => {
};
const Component = connect(BaseComponent, { simpleStore: SimpleStore });
- mount();
+ render();
+
+ await screen.findByText('hello!');
});
describe('generates `shouldComponentUpdate`', () => {
const Component: React.ComponentType<{ someProp?: any, foo: number }> = jest.fn(() => Hello!);
- const SimplestStore: Store = ({
- 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();
+ const { rerender } = render();
- wrapper.setProps({});
+ // @ts-expect-error
+ rerender();
- expect(Component).toHaveBeenCalledTimes(1);
+ await screen.findByText('Hello!');
});
const verifyShouldComponentUpdate = ({ initial, next, result }) => {
- asMock(SimplestStore.getInitialState).mockReturnValue(initial);
- const ComponentClass = connect(Component, { foo: SimplestStore });
-
- mount();
- const update = asMock(SimplestStore.listen).mock.calls[0][0];
-
- asMock(Component).mockClear();
-
- update(next);
-
- if (result) {
- expect(Component).toHaveBeenCalled();
- } else {
- expect(Component).not.toHaveBeenCalled();
- }
+ SimpleStore.setValue(initial);
+ const ComponentClass = connect(Component, { foo: SimpleStore });
- const wrapper = mount();
+ render();
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();
+ it('renders state from store', async () => {
+ render();
- 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();
+ render();
- expect(wrapper).toHaveText('Value is: 42');
+ await screen.findByText('Value is: 42');
});
- it('reflects state changes from store', () => {
- const wrapper = mount();
+ it('reflects state changes from store', async () => {
+ render();
- 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 {value ? `Value is: ${value}` : 'No value.'};
};
- const wrapper = mount();
+ render();
- 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 {value ? `Value is: ${value}` : 'No value.'};
};
- mount();
+ render();
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 {value ? `Value is: ${value}` : 'No value.'};
};
- const wrapper = mount( x} />);
+ const { rerender } = render( x} />);
- expect(wrapper).toHaveText('No value.');
+ await screen.findByText('No value.');
- wrapper.setProps({ propsMapper: ({ value: v } = { value: 0 }) => ({ value: v * 2 }) });
+ rerender( ({ value: v * 2 })} />);
act(() => SimpleStore.setValue(42));
- expect(wrapper).toHaveText('Value is: 84');
+ await screen.findByText('Value is: 84');
expect(listenSpy).toHaveBeenCalledTimes(1);
});
diff --git a/graylog2-web-interface/src/views/components/aggregationwizard/__tests__/AggregationWizard.corevisualizations.test.tsx b/graylog2-web-interface/src/views/components/aggregationwizard/__tests__/AggregationWizard.corevisualizations.test.tsx
index 10c88dc969ae..e8a13507d3a2 100644
--- a/graylog2-web-interface/src/views/components/aggregationwizard/__tests__/AggregationWizard.corevisualizations.test.tsx
+++ b/graylog2-web-interface/src/views/components/aggregationwizard/__tests__/AggregationWizard.corevisualizations.test.tsx
@@ -15,7 +15,7 @@
* .
*/
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', () => {
diff --git a/graylog2-web-interface/src/views/components/common/ActionDropdown.test.tsx b/graylog2-web-interface/src/views/components/common/ActionDropdown.test.tsx
index c2df12dd7d04..1eff3b5e20b8 100644
--- a/graylog2-web-interface/src/views/components/common/ActionDropdown.test.tsx
+++ b/graylog2-web-interface/src/views/components/common/ActionDropdown.test.tsx
@@ -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);
- expect(onClick).not.toHaveBeenCalled();
+ await screen.findByRole('menuitem', { name: 'Foo' });
- await waitFor(() => {
- expect(screen.queryByRole('menuitem', { name: 'Foo' })).not.toBeInTheDocument();
- });
+ expect(onClick).not.toHaveBeenCalled();
});
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();
diff --git a/graylog2-web-interface/src/views/components/dashboard/BigDisplayModeConfiguration.test.tsx b/graylog2-web-interface/src/views/components/dashboard/BigDisplayModeConfiguration.test.tsx
index 12683afb2cc6..76fb9d93ff67 100644
--- a/graylog2-web-interface/src/views/components/dashboard/BigDisplayModeConfiguration.test.tsx
+++ b/graylog2-web-interface/src/views/components/dashboard/BigDisplayModeConfiguration.test.tsx
@@ -69,11 +69,11 @@ describe('BigDisplayModeConfiguration', () => {
));
- it('disables menu item if `disabled` prop is `true`', () => {
- const { getByText, queryByText } = render();
- const menuItem = getByText('Full Screen');
+ it('disables menu item if `disabled` prop is `true`', async () => {
+ const { queryByText, findByText } = render();
+ const menuItem = await findByText('Full Screen');
- fireEvent.submit(menuItem);
+ fireEvent.click(menuItem);
expect(queryByText('Configuring Full Screen')).toBeNull();
});
diff --git a/graylog2-web-interface/src/views/components/queries/QueryTitleEditModal.test.tsx b/graylog2-web-interface/src/views/components/queries/QueryTitleEditModal.test.tsx
index 4b495364aed7..4a7d243ebed8 100644
--- a/graylog2-web-interface/src/views/components/queries/QueryTitleEditModal.test.tsx
+++ b/graylog2-web-interface/src/views/components/queries/QueryTitleEditModal.test.tsx
@@ -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);
+ });
}
};
diff --git a/graylog2-web-interface/src/views/components/searchbar/time-range-filter/time-range-picker/AbsoluteDateInput.test.tsx b/graylog2-web-interface/src/views/components/searchbar/time-range-filter/time-range-picker/AbsoluteDateInput.test.tsx
index 3adcd8ebe622..815547fa61c2 100644
--- a/graylog2-web-interface/src/views/components/searchbar/time-range-filter/time-range-picker/AbsoluteDateInput.test.tsx
+++ b/graylog2-web-interface/src/views/components/searchbar/time-range-filter/time-range-picker/AbsoluteDateInput.test.tsx
@@ -35,9 +35,11 @@ const defaultProps = {
describe('AbsoluteDateInput', () => {
beforeAll(() => { jest.clearAllMocks(); });
- it('renders with minimal props', () => {
+ it('renders with minimal props', async () => {
render();
+ await screen.findByRole('button', { name: /insert current date/i });
+
expect(screen).not.toBeNull();
});
diff --git a/graylog2-web-interface/src/views/components/searchbar/time-range-filter/time-range-picker/TabAbsoluteTimeRange.test.tsx b/graylog2-web-interface/src/views/components/searchbar/time-range-filter/time-range-picker/TabAbsoluteTimeRange.test.tsx
index 28836e5d7d6f..b00c57627df2 100644
--- a/graylog2-web-interface/src/views/components/searchbar/time-range-filter/time-range-picker/TabAbsoluteTimeRange.test.tsx
+++ b/graylog2-web-interface/src/views/components/searchbar/time-range-filter/time-range-picker/TabAbsoluteTimeRange.test.tsx
@@ -15,8 +15,9 @@
* .
*/
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((
));
@@ -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);
});
});
diff --git a/graylog2-web-interface/src/views/components/widgets/CopyToDashboardForm.test.tsx b/graylog2-web-interface/src/views/components/widgets/CopyToDashboardForm.test.tsx
index 03222db4f6da..55a81341bb1d 100644
--- a/graylog2-web-interface/src/views/components/widgets/CopyToDashboardForm.test.tsx
+++ b/graylog2-web-interface/src/views/components/widgets/CopyToDashboardForm.test.tsx
@@ -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();
+ render();
- expect(baseElement).not.toBeNull();
+ await screen.findByText(/no dashboards found/i);
});
- it('should render the modal with entries', () => {
- const { baseElement } = render();
+ it('should render the modal with entries', async () => {
+ render();
- 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();
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();
- submitModal();
+ const submitButton = await screen.findByRole('button', { name: /submit/i, hidden: true });
+
+ await waitFor(() => {
+ expect(submitButton).toBeDisabled();
+ });
expect(onSubmit).not.toHaveBeenCalled();
});
diff --git a/graylog2-web-interface/src/views/components/widgets/FieldSortIcon.test.tsx b/graylog2-web-interface/src/views/components/widgets/FieldSortIcon.test.tsx
index 215b5c08dc6d..6d2eeca2be62 100644
--- a/graylog2-web-interface/src/views/components/widgets/FieldSortIcon.test.tsx
+++ b/graylog2-web-interface/src/views/components/widgets/FieldSortIcon.test.tsx
@@ -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( {}} />);
+ const setLoadingState = jest.fn();
+ const { getByTitle } = render();
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( {}} />);
+ const { getByTitle } = render();
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 () => {
diff --git a/graylog2-web-interface/src/views/components/widgets/WidgetActionDropdown.test.tsx b/graylog2-web-interface/src/views/components/widgets/WidgetActionDropdown.test.tsx
index cf49fddda64f..241578bf710d 100644
--- a/graylog2-web-interface/src/views/components/widgets/WidgetActionDropdown.test.tsx
+++ b/graylog2-web-interface/src/views/components/widgets/WidgetActionDropdown.test.tsx
@@ -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();
diff --git a/graylog2-web-interface/test/wrappedEnzyme.tsx b/graylog2-web-interface/test/wrappedEnzyme.tsx
index 8aed8647d42b..ae762dac0c43 100644
--- a/graylog2-web-interface/test/wrappedEnzyme.tsx
+++ b/graylog2-web-interface/test/wrappedEnzyme.tsx
@@ -14,6 +14,7 @@
* along with this program. If not, see
* .
*/
+///
import type * as React from 'react';
import type { ReactWrapper, ShallowWrapper } from 'enzyme';
import { configure, mount, shallow } from 'enzyme';