diff --git a/package.json b/package.json index b3cc652ef93..2d374394048 100644 --- a/package.json +++ b/package.json @@ -113,7 +113,7 @@ "@testing-library/jest-dom": "5.16.2", "@testing-library/react": "12.1.4", "@testing-library/react-hooks": "7.0.2", - "@testing-library/user-event": "13.5.0", + "@testing-library/user-event": "14.0.0", "@types/angular": "1.8.4", "@types/angular-route": "1.7.2", "@types/classnames": "2.3.0", diff --git a/packages/grafana-data/package.json b/packages/grafana-data/package.json index b4252fa7bb3..aeabae904ef 100644 --- a/packages/grafana-data/package.json +++ b/packages/grafana-data/package.json @@ -51,7 +51,7 @@ "@testing-library/jest-dom": "5.16.2", "@testing-library/react": "12.1.4", "@testing-library/react-hooks": "7.0.2", - "@testing-library/user-event": "13.5.0", + "@testing-library/user-event": "14.0.0", "@types/history": "4.7.11", "@types/jest": "27.4.1", "@types/jquery": "3.5.14", diff --git a/packages/grafana-runtime/package.json b/packages/grafana-runtime/package.json index 5f22ae36d8c..db4c9296007 100644 --- a/packages/grafana-runtime/package.json +++ b/packages/grafana-runtime/package.json @@ -40,7 +40,7 @@ "@rollup/plugin-node-resolve": "13.1.3", "@testing-library/dom": "8.13.0", "@testing-library/react": "12.1.4", - "@testing-library/user-event": "^13.5.0", + "@testing-library/user-event": "14.0.0", "@types/angular": "1.8.4", "@types/history": "4.7.11", "@types/jest": "27.4.1", diff --git a/packages/grafana-runtime/src/components/DataSourcePicker.test.tsx b/packages/grafana-runtime/src/components/DataSourcePicker.test.tsx index 6f18fe2141a..10a2cbd7b17 100644 --- a/packages/grafana-runtime/src/components/DataSourcePicker.test.tsx +++ b/packages/grafana-runtime/src/components/DataSourcePicker.test.tsx @@ -12,7 +12,7 @@ describe('DataSourcePicker', () => { const select = render(); const clearButton = select.getByLabelText('select-clear-value'); - userEvent.click(clearButton); + await userEvent.click(clearButton); expect(onClear).toHaveBeenCalled(); }); diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index d73b877eb70..a36db9237a8 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -118,7 +118,7 @@ "@testing-library/jest-dom": "5.16.2", "@testing-library/react": "12.1.4", "@testing-library/react-hooks": "7.0.2", - "@testing-library/user-event": "13.5.0", + "@testing-library/user-event": "14.0.0", "@types/classnames": "2.3.0", "@types/common-tags": "^1.8.0", "@types/d3": "7.1.0", diff --git a/packages/grafana-ui/src/components/Cascader/Cascader.test.tsx b/packages/grafana-ui/src/components/Cascader/Cascader.test.tsx index 048756a565f..b0f36e10610 100644 --- a/packages/grafana-ui/src/components/Cascader/Cascader.test.tsx +++ b/packages/grafana-ui/src/components/Cascader/Cascader.test.tsx @@ -1,7 +1,8 @@ import React from 'react'; import { Cascader, CascaderOption, CascaderProps } from './Cascader'; import { act, render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; +import userEvent, { PointerEventsCheckLevel } from '@testing-library/user-event'; +import { UserEvent } from '@testing-library/user-event/dist/types/setup'; const options = [ { @@ -45,78 +46,86 @@ describe('Cascader', () => { const placeholder = 'cascader-placeholder'; describe('options from state change', () => { + let user: UserEvent; + beforeEach(() => { jest.useFakeTimers('modern'); + // Need to use delay: null here to work with fakeTimers + // see https://github.com/testing-library/user-event/issues/833 + user = userEvent.setup({ delay: null }); }); - it('displays updated options', () => { + afterEach(() => { + jest.useRealTimers(); + }); + + it('displays updated options', async () => { render(); - act(() => { - userEvent.click(screen.getByPlaceholderText(placeholder)); - }); + await user.click(screen.getByPlaceholderText(placeholder)); expect(screen.getByText('Initial state option')).toBeInTheDocument(); expect(screen.queryByText('First')).not.toBeInTheDocument(); act(() => { jest.runAllTimers(); - userEvent.click(screen.getByPlaceholderText(placeholder)); }); + await user.click(screen.getByPlaceholderText(placeholder)); + expect(screen.queryByText('Initial state option')).not.toBeInTheDocument(); expect(screen.getByText('First')).toBeInTheDocument(); }); - it('filters updated results when searching', () => { + it('filters updated results when searching', async () => { render(); act(() => { jest.runAllTimers(); }); - userEvent.type(screen.getByPlaceholderText(placeholder), 'Third'); + await user.type(screen.getByPlaceholderText(placeholder), 'Third'); expect(screen.queryByText('Second')).not.toBeInTheDocument(); expect(screen.getByText('First / Third')).toBeInTheDocument(); }); }); - it('filters results when searching', () => { + it('filters results when searching', async () => { render(); - userEvent.type(screen.getByPlaceholderText(placeholder), 'Third'); + await userEvent.type(screen.getByPlaceholderText(placeholder), 'Third'); expect(screen.queryByText('Second')).not.toBeInTheDocument(); expect(screen.getByText('First / Third')).toBeInTheDocument(); }); - it('displays selected value with all levels when displayAllSelectedLevels is true and selecting a value from the search', () => { + it('displays selected value with all levels when displayAllSelectedLevels is true and selecting a value from the search', async () => { render( ); - userEvent.type(screen.getByPlaceholderText(placeholder), 'Third'); - userEvent.click(screen.getByText('First / Third')); + await userEvent.type(screen.getByPlaceholderText(placeholder), 'Third'); + await userEvent.click(screen.getByText('First / Third')); expect(screen.getByDisplayValue('First / Third')).toBeInTheDocument(); }); - it('displays all levels selected with default separator when displayAllSelectedLevels is true', () => { + it('displays all levels selected with default separator when displayAllSelectedLevels is true', async () => { render( {}} /> ); expect(screen.queryByDisplayValue('First/Second')).not.toBeInTheDocument(); - userEvent.click(screen.getByPlaceholderText(placeholder)); + await userEvent.click(screen.getByPlaceholderText(placeholder)); // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed - userEvent.click(screen.getByText('First'), undefined, { skipPointerEventsCheck: true }); - userEvent.click(screen.getByText('Second'), undefined, { skipPointerEventsCheck: true }); + await userEvent.click(screen.getByText('First'), { pointerEventsCheck: PointerEventsCheckLevel.Never }); + await userEvent.click(screen.getByText('Second'), { pointerEventsCheck: PointerEventsCheckLevel.Never }); expect(screen.getByDisplayValue('First/Second')).toBeInTheDocument(); }); - it('displays all levels selected with separator passed in when displayAllSelectedLevels is true', () => { + it('displays all levels selected with separator passed in when displayAllSelectedLevels is true', async () => { const separator = ','; render( @@ -131,34 +140,34 @@ describe('Cascader', () => { expect(screen.queryByDisplayValue('First/Second')).not.toBeInTheDocument(); - userEvent.click(screen.getByPlaceholderText(placeholder)); + await userEvent.click(screen.getByPlaceholderText(placeholder)); // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed - userEvent.click(screen.getByText('First'), undefined, { skipPointerEventsCheck: true }); - userEvent.click(screen.getByText('Second'), undefined, { skipPointerEventsCheck: true }); + await userEvent.click(screen.getByText('First'), { pointerEventsCheck: PointerEventsCheckLevel.Never }); + await userEvent.click(screen.getByText('Second'), { pointerEventsCheck: PointerEventsCheckLevel.Never }); expect(screen.getByDisplayValue(`First${separator}Second`)).toBeInTheDocument(); }); - it('displays last level selected when displayAllSelectedLevels is false', () => { + it('displays last level selected when displayAllSelectedLevels is false', async () => { render( ); - userEvent.click(screen.getByPlaceholderText(placeholder)); + await userEvent.click(screen.getByPlaceholderText(placeholder)); // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed - userEvent.click(screen.getByText('First'), undefined, { skipPointerEventsCheck: true }); - userEvent.click(screen.getByText('Second'), undefined, { skipPointerEventsCheck: true }); + await userEvent.click(screen.getByText('First'), { pointerEventsCheck: PointerEventsCheckLevel.Never }); + await userEvent.click(screen.getByText('Second'), { pointerEventsCheck: PointerEventsCheckLevel.Never }); expect(screen.getByDisplayValue('Second')).toBeInTheDocument(); }); - it('displays last level selected when displayAllSelectedLevels is not passed in', () => { + it('displays last level selected when displayAllSelectedLevels is not passed in', async () => { render(); - userEvent.click(screen.getByPlaceholderText(placeholder)); + await userEvent.click(screen.getByPlaceholderText(placeholder)); // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed - userEvent.click(screen.getByText('First'), undefined, { skipPointerEventsCheck: true }); - userEvent.click(screen.getByText('Second'), undefined, { skipPointerEventsCheck: true }); + await userEvent.click(screen.getByText('First'), { pointerEventsCheck: PointerEventsCheckLevel.Never }); + await userEvent.click(screen.getByText('Second'), { pointerEventsCheck: PointerEventsCheckLevel.Never }); expect(screen.getByDisplayValue('Second')).toBeInTheDocument(); }); diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx index 1c0ab924223..d4f22689b04 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { act, render, screen } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import { ColorPickerPopover } from './ColorPickerPopover'; import { createTheme } from '@grafana/data'; import userEvent from '@testing-library/user-event'; @@ -7,24 +7,20 @@ import userEvent from '@testing-library/user-event'; describe('ColorPickerPopover', () => { const theme = createTheme(); - it('should be tabbable', () => { + it('should be tabbable', async () => { render( {}} />); const color = screen.getByRole('button', { name: 'dark-red color' }); const customTab = screen.getByRole('button', { name: 'Custom' }); - act(() => { - userEvent.tab(); - }); + await userEvent.tab(); expect(customTab).toHaveFocus(); - act(() => { - userEvent.tab(); - }); + await userEvent.tab(); expect(color).toHaveFocus(); }); describe('rendering', () => { - it('should render provided color as selected if color provided by name', () => { + it('should render provided color as selected if color provided by name', async () => { render( {}} />); const color = screen.getByRole('button', { name: 'green color' }); const colorSwatchWrapper = screen.getAllByTestId('data-testid-colorswatch'); @@ -32,9 +28,7 @@ describe('ColorPickerPopover', () => { expect(color).toBeInTheDocument(); expect(colorSwatchWrapper[0]).toBeInTheDocument(); - act(() => { - userEvent.click(colorSwatchWrapper[0]); - }); + await userEvent.click(colorSwatchWrapper[0]); expect(color).toHaveStyle('box-shadow: inset 0 0 0 2px #73BF69,inset 0 0 0 4px #000000'); }); }); @@ -42,23 +36,19 @@ describe('ColorPickerPopover', () => { describe('named colors support', () => { const onChangeSpy = jest.fn(); - it('should pass hex color value to onChange prop by default', () => { + it('should pass hex color value to onChange prop by default', async () => { render(); const color = screen.getByRole('button', { name: 'red color' }); - act(() => { - userEvent.click(color); - }); + await userEvent.click(color); expect(onChangeSpy).toBeCalledTimes(1); expect(onChangeSpy).toBeCalledWith(theme.visualization.getColorByName('red')); }); - it('should pass color name to onChange prop when named colors enabled', () => { + it('should pass color name to onChange prop when named colors enabled', async () => { render(); const color = screen.getByRole('button', { name: 'red color' }); - act(() => { - userEvent.click(color); - }); + await userEvent.click(color); expect(onChangeSpy).toBeCalledTimes(2); expect(onChangeSpy).toBeCalledWith(theme.visualization.getColorByName('red')); diff --git a/packages/grafana-ui/src/components/ConfirmButton/ConfirmButton.test.tsx b/packages/grafana-ui/src/components/ConfirmButton/ConfirmButton.test.tsx index c9e4d9cd4f3..3fd006dc194 100644 --- a/packages/grafana-ui/src/components/ConfirmButton/ConfirmButton.test.tsx +++ b/packages/grafana-ui/src/components/ConfirmButton/ConfirmButton.test.tsx @@ -6,7 +6,7 @@ import { ConfirmButton } from './ConfirmButton'; import { expect } from '../../../../../public/test/lib/common'; describe('ConfirmButton', () => { - it('should show confirm delete when clicked', () => { + it('should show confirm delete when clicked', async () => { const onConfirm = jest.fn(); render( @@ -17,18 +17,18 @@ describe('ConfirmButton', () => { // Confirm button should not be visible before clicking the Delete button expect(screen.queryByRole('button', { name: 'Confirm delete' })).not.toBeInTheDocument(); - userEvent.click(screen.getByRole('button', { name: 'Delete' })); + await userEvent.click(screen.getByRole('button', { name: 'Delete' })); // Confirm button should now be visible expect(screen.getByRole('button', { name: 'Confirm delete' })).toBeInTheDocument(); - userEvent.click(screen.getByRole('button', { name: 'Confirm delete' })); + await userEvent.click(screen.getByRole('button', { name: 'Confirm delete' })); expect(onConfirm).toHaveBeenCalled(); // Confirm button should be visible if closeOnConfirm is false expect(screen.queryByRole('button', { name: 'Confirm delete' })).toBeInTheDocument(); }); - it('should hide confirm delete when closeOnConfirm is true', () => { + it('should hide confirm delete when closeOnConfirm is true', async () => { render( {}} closeOnConfirm={true}> Delete @@ -38,17 +38,17 @@ describe('ConfirmButton', () => { // Confirm button should not be visible before clicking the Delete button expect(screen.queryByRole('button', { name: 'Confirm delete' })).not.toBeInTheDocument(); - userEvent.click(screen.getByRole('button', { name: 'Delete' })); + await userEvent.click(screen.getByRole('button', { name: 'Delete' })); // Confirm button should now be visible expect(screen.getByRole('button', { name: 'Confirm delete' })).toBeInTheDocument(); - userEvent.click(screen.getByRole('button', { name: 'Confirm delete' })); + await userEvent.click(screen.getByRole('button', { name: 'Confirm delete' })); // Confirm button should not be visible if closeOnConfirm is true expect(screen.queryByRole('button', { name: 'Confirm delete' })).not.toBeInTheDocument(); }); - it('should show cancel when clicked', () => { + it('should show cancel when clicked', async () => { const onCancel = jest.fn(); render( {}}> @@ -59,11 +59,11 @@ describe('ConfirmButton', () => { // Cancel button should not be visible before clicking the Delete button expect(screen.queryByRole('button', { name: 'Cancel' })).not.toBeInTheDocument(); - userEvent.click(screen.getByRole('button', { name: 'Delete' })); + await userEvent.click(screen.getByRole('button', { name: 'Delete' })); // Cancel button should now be visible expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument(); - userEvent.click(screen.getByRole('button', { name: 'Cancel' })); + await userEvent.click(screen.getByRole('button', { name: 'Cancel' })); expect(onCancel).toHaveBeenCalled(); // Cancel button should not be visible after click diff --git a/packages/grafana-ui/src/components/DataSourceSettings/CustomHeadersSettings.test.tsx b/packages/grafana-ui/src/components/DataSourceSettings/CustomHeadersSettings.test.tsx index c7205d4fb09..75dcefc6e2e 100644 --- a/packages/grafana-ui/src/components/DataSourceSettings/CustomHeadersSettings.test.tsx +++ b/packages/grafana-ui/src/components/DataSourceSettings/CustomHeadersSettings.test.tsx @@ -54,13 +54,13 @@ function assertRowCount(configuredInputCount: number, passwordInputCount: number } describe('Render', () => { - it('should add a new header', () => { + it('should add a new header', async () => { setup(); const b = screen.getByRole('button', { name: 'Add header' }); expect(b).toBeInTheDocument(); assertRowCount(0, 0); - userEvent.click(b); + await userEvent.click(b); assertRowCount(0, 1); }); @@ -71,7 +71,7 @@ describe('Render', () => { expect(b.getAttribute('type')).toBe('button'); }); - it('should remove a header', () => { + it('should remove a header', async () => { const { onChange } = setup({ dataSourceConfig: { jsonData: { @@ -87,14 +87,14 @@ describe('Render', () => { assertRowCount(1, 0); - userEvent.click(b); + await userEvent.click(b); assertRowCount(0, 0); expect(onChange).toHaveBeenCalledTimes(1); expect(onChange.mock.calls[0][0].jsonData).toStrictEqual({}); }); - it('when removing a just-created header, it should clean up secureJsonData', () => { + it('when removing a just-created header, it should clean up secureJsonData', async () => { const { onChange } = setup({ dataSourceConfig: { jsonData: { @@ -109,7 +109,7 @@ describe('Render', () => { // we remove the row const removeButton = screen.getByRole('button', { name: 'Remove header' }); expect(removeButton).toBeInTheDocument(); - userEvent.click(removeButton); + await userEvent.click(removeButton); assertRowCount(0, 0); expect(onChange).toHaveBeenCalled(); @@ -119,7 +119,7 @@ describe('Render', () => { expect(lastCall[0].secureJsonData).not.toHaveProperty('httpHeaderValue1'); }); - it('should reset a header', () => { + it('should reset a header', async () => { setup({ dataSourceConfig: { jsonData: { @@ -135,7 +135,7 @@ describe('Render', () => { expect(b).toBeInTheDocument(); assertRowCount(1, 0); - userEvent.click(b); + await userEvent.click(b); assertRowCount(0, 1); }); }); diff --git a/packages/grafana-ui/src/components/SecretInput/SecretInput.test.tsx b/packages/grafana-ui/src/components/SecretInput/SecretInput.test.tsx index 010ed2fdf59..fb525e93a1b 100644 --- a/packages/grafana-ui/src/components/SecretInput/SecretInput.test.tsx +++ b/packages/grafana-ui/src/components/SecretInput/SecretInput.test.tsx @@ -33,7 +33,7 @@ describe('', () => { expect(screen.queryByRole('button', { name: RESET_BUTTON_TEXT })).toBeInTheDocument(); }); - it('should be possible to reset a configured secret', () => { + it('should be possible to reset a configured secret', async () => { const onReset = jest.fn(); render( {}} onReset={onReset} placeholder={PLACEHOLDER_TEXT} />); @@ -43,12 +43,12 @@ describe('', () => { expect(screen.queryByRole('button', { name: RESET_BUTTON_TEXT })).toBeInTheDocument(); // Click on "Reset" - userEvent.click(screen.getByRole('button', { name: RESET_BUTTON_TEXT })); + await userEvent.click(screen.getByRole('button', { name: RESET_BUTTON_TEXT })); expect(onReset).toHaveBeenCalledTimes(1); }); - it('should be possible to change the value of the secret', () => { + it('should be possible to change the value of the secret', async () => { const onChange = jest.fn(); render( {}} placeholder={PLACEHOLDER_TEXT} />); @@ -57,7 +57,7 @@ describe('', () => { expect(input).toHaveValue(''); - userEvent.type(input, 'Foo'); + await userEvent.type(input, 'Foo'); expect(onChange).toHaveBeenCalled(); expect(input).toHaveValue('Foo'); diff --git a/packages/grafana-ui/src/components/Select/SelectBase.test.tsx b/packages/grafana-ui/src/components/Select/SelectBase.test.tsx index 81033a3de78..680d07ffc2c 100644 --- a/packages/grafana-ui/src/components/Select/SelectBase.test.tsx +++ b/packages/grafana-ui/src/components/Select/SelectBase.test.tsx @@ -22,9 +22,9 @@ describe('SelectBase', () => { render(); }); - it('renders empty options information', () => { + it('renders empty options information', async () => { render(); - userEvent.click(screen.getByText(/choose/i)); + await userEvent.click(screen.getByText(/choose/i)); expect(screen.queryByText(/no options found/i)).toBeVisible(); }); @@ -54,7 +54,7 @@ describe('SelectBase', () => { render(); expect(screen.queryByText('Test label')).toBeInTheDocument(); - userEvent.click(screen.getByText('clear value')); + await userEvent.click(screen.getByText('clear value')); expect(screen.queryByText('Test label')).not.toBeInTheDocument(); }); @@ -186,9 +186,9 @@ describe('SelectBase', () => { }); describe('options', () => { - it('renders menu with provided options', () => { + it('renders menu with provided options', async () => { render(); - userEvent.click(screen.getByText(/choose/i)); + await userEvent.click(screen.getByText(/choose/i)); const menuOptions = screen.getAllByLabelText('Select option'); expect(menuOptions).toHaveLength(2); }); diff --git a/packages/grafana-ui/src/components/Table/Table.test.tsx b/packages/grafana-ui/src/components/Table/Table.test.tsx index eeb86e323f7..91d8bab44e9 100644 --- a/packages/grafana-ui/src/components/Table/Table.test.tsx +++ b/packages/grafana-ui/src/components/Table/Table.test.tsx @@ -159,11 +159,11 @@ describe('Table', () => { }); describe('when sorting with column header', () => { - it('then correct rows should be rendered', () => { + it('then correct rows should be rendered', async () => { getTestContext(); - userEvent.click(within(getColumnHeader(/temperature/)).getByText(/temperature/i)); - userEvent.click(within(getColumnHeader(/temperature/)).getByText(/temperature/i)); + await userEvent.click(within(getColumnHeader(/temperature/)).getByText(/temperature/i)); + await userEvent.click(within(getColumnHeader(/temperature/)).getByText(/temperature/i)); const rows = within(getTable()).getAllByRole('row'); expect(rows).toHaveLength(5); diff --git a/public/app/core/components/ForgottenPassword/ChangePasswordPage.test.tsx b/public/app/core/components/ForgottenPassword/ChangePasswordPage.test.tsx index 5021e7cf081..0c558f6eadd 100644 --- a/public/app/core/components/ForgottenPassword/ChangePasswordPage.test.tsx +++ b/public/app/core/components/ForgottenPassword/ChangePasswordPage.test.tsx @@ -48,11 +48,11 @@ describe('ChangePassword Page', () => { expect(await screen.findByText('New Password is required')).toBeInTheDocument(); expect(screen.getByText('Confirmed Password is required')).toBeInTheDocument(); - userEvent.type(screen.getByLabelText('New password'), 'admin'); - userEvent.type(screen.getByLabelText('Confirm new password'), 'a'); + await userEvent.type(screen.getByLabelText('New password'), 'admin'); + await userEvent.type(screen.getByLabelText('Confirm new password'), 'a'); await waitFor(() => expect(screen.getByText('Passwords must match!')).toBeInTheDocument()); - userEvent.type(screen.getByLabelText('Confirm new password'), 'dmin'); + await userEvent.type(screen.getByLabelText('Confirm new password'), 'dmin'); await waitFor(() => expect(screen.queryByText('Passwords must match!')).not.toBeInTheDocument()); }); it('should navigate to default url if change password is successful', async () => { @@ -64,8 +64,8 @@ describe('ChangePassword Page', () => { postMock.mockResolvedValueOnce({ message: 'Logged in' }); render(); - userEvent.type(screen.getByLabelText('New password'), 'test'); - userEvent.type(screen.getByLabelText('Confirm new password'), 'test'); + await userEvent.type(screen.getByLabelText('New password'), 'test'); + await userEvent.type(screen.getByLabelText('Confirm new password'), 'test'); fireEvent.click(screen.getByRole('button', { name: 'Submit' })); await waitFor(() => expect(postMock).toHaveBeenCalledWith('/api/user/password/reset', { diff --git a/public/app/core/components/ForgottenPassword/SendResetMailPage.test.tsx b/public/app/core/components/ForgottenPassword/SendResetMailPage.test.tsx index f9bb024ae40..ff50fde844f 100644 --- a/public/app/core/components/ForgottenPassword/SendResetMailPage.test.tsx +++ b/public/app/core/components/ForgottenPassword/SendResetMailPage.test.tsx @@ -41,14 +41,14 @@ describe('VerifyEmail Page', () => { fireEvent.click(screen.getByRole('button', { name: 'Send reset email' })); expect(await screen.findByText('Email or username is required')).toBeInTheDocument(); - userEvent.type(screen.getByRole('textbox', { name: /User Enter your information/i }), 'test@gmail.com'); + await userEvent.type(screen.getByRole('textbox', { name: /User Enter your information/i }), 'test@gmail.com'); await waitFor(() => expect(screen.queryByText('Email is invalid')).not.toBeInTheDocument()); }); it('should show success meessage if reset-password is successful', async () => { postMock.mockResolvedValueOnce({ message: 'Email sent' }); render(); - userEvent.type(screen.getByRole('textbox', { name: /User Enter your information/i }), 'test@gmail.com'); + await userEvent.type(screen.getByRole('textbox', { name: /User Enter your information/i }), 'test@gmail.com'); fireEvent.click(screen.getByRole('button', { name: 'Send reset email' })); await waitFor(() => expect(postMock).toHaveBeenCalledWith('/api/user/password/send-reset-email', { diff --git a/public/app/core/components/Login/LoginPage.test.tsx b/public/app/core/components/Login/LoginPage.test.tsx index 3d5161182df..3791f5dac87 100644 --- a/public/app/core/components/Login/LoginPage.test.tsx +++ b/public/app/core/components/Login/LoginPage.test.tsx @@ -56,7 +56,7 @@ describe('Login Page', () => { fireEvent.click(screen.getByRole('button', { name: 'Login button' })); expect(await screen.findByText('Email or username is required')).toBeInTheDocument(); - userEvent.type(screen.getByRole('textbox', { name: 'Username input field' }), 'admin'); + await userEvent.type(screen.getByRole('textbox', { name: 'Username input field' }), 'admin'); await waitFor(() => expect(screen.queryByText('Email or username is required')).not.toBeInTheDocument()); }); it('should pass validation checks for password field', async () => { @@ -65,7 +65,7 @@ describe('Login Page', () => { fireEvent.click(screen.getByRole('button', { name: 'Login button' })); expect(await screen.findByText('Password is required')).toBeInTheDocument(); - userEvent.type(screen.getByLabelText('Password input field'), 'admin'); + await userEvent.type(screen.getByLabelText('Password input field'), 'admin'); await waitFor(() => expect(screen.queryByText('Password is required')).not.toBeInTheDocument()); }); it('should navigate to default url if credentials is valid', async () => { @@ -77,8 +77,8 @@ describe('Login Page', () => { postMock.mockResolvedValueOnce({ message: 'Logged in' }); render(); - userEvent.type(screen.getByLabelText('Username input field'), 'admin'); - userEvent.type(screen.getByLabelText('Password input field'), 'test'); + await userEvent.type(screen.getByLabelText('Username input field'), 'admin'); + await userEvent.type(screen.getByLabelText('Password input field'), 'test'); fireEvent.click(screen.getByLabelText('Login button')); await waitFor(() => expect(postMock).toHaveBeenCalledWith('/login', { password: 'test', user: 'admin' })); diff --git a/public/app/core/components/NavBar/NavBarItem.test.tsx b/public/app/core/components/NavBar/NavBarItem.test.tsx index 9425298a4ab..dd11a26d8ac 100644 --- a/public/app/core/components/NavBar/NavBarItem.test.tsx +++ b/public/app/core/components/NavBar/NavBarItem.test.tsx @@ -1,11 +1,21 @@ import React from 'react'; -import { act, render, screen, waitFor } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { BrowserRouter } from 'react-router-dom'; import { locationUtil } from '@grafana/data'; import { config, setLocationService } from '@grafana/runtime'; import TestProvider from '../../../../test/helpers/TestProvider'; +// Need to mock createBrowserHistory here to avoid errors +jest.mock('history', () => ({ + ...jest.requireActual('history'), + createBrowserHistory: () => ({ + listen: jest.fn(), + location: {}, + createHref: jest.fn(), + }), +})); + import NavBarItem, { Props } from './NavBarItem'; const onClickMock = jest.fn(); @@ -21,7 +31,7 @@ const defaults: Props = { }, }; -function getTestContext(overrides: Partial = {}, subUrl = '') { +async function getTestContext(overrides: Partial = {}, subUrl = '') { jest.clearAllMocks(); config.appSubUrl = subUrl; locationUtil.initialize({ config, getTimeRangeForUrl: jest.fn(), getVariablesUrlParams: jest.fn() }); @@ -38,33 +48,34 @@ function getTestContext(overrides: Partial = {}, subUrl = '') { ); + // Need to click this first to set the correct selection range + // see https://github.com/testing-library/user-event/issues/901#issuecomment-1087192424 + await userEvent.click(document.body); return { rerender, pushMock }; } describe('NavBarItem', () => { describe('when url property is not set', () => { - it('then it renders the menu trigger as a button', () => { - getTestContext(); + it('then it renders the menu trigger as a button', async () => { + await getTestContext(); expect(screen.getAllByRole('button')).toHaveLength(1); }); describe('and clicking on the menu trigger button', () => { - it('then the onClick handler should be called', () => { - getTestContext(); + it('then the onClick handler should be called', async () => { + await getTestContext(); - act(() => { - userEvent.click(screen.getByRole('button')); - }); + await userEvent.click(screen.getByRole('button')); expect(onClickMock).toHaveBeenCalledTimes(1); }); }); describe('and hovering over the menu trigger button', () => { - it('then the menu items should be visible', () => { - getTestContext(); + it('then the menu items should be visible', async () => { + await getTestContext(); - userEvent.hover(screen.getByRole('button')); + await userEvent.hover(screen.getByRole('button')); expect(screen.getByRole('menuitem', { name: 'Parent Node' })).toBeInTheDocument(); expect(screen.getByText('Child Node 1')).toBeInTheDocument(); @@ -73,10 +84,10 @@ describe('NavBarItem', () => { }); describe('and tabbing to the menu trigger button', () => { - it('then the menu items should be visible', () => { - getTestContext(); + it('then the menu items should be visible', async () => { + await getTestContext(); - userEvent.tab(); + await userEvent.tab(); expect(screen.getByText('Parent Node')).toBeInTheDocument(); expect(screen.getByText('Child Node 1')).toBeInTheDocument(); @@ -85,16 +96,16 @@ describe('NavBarItem', () => { }); describe('and pressing arrow right on the menu trigger button', () => { - it('then the correct menu item should receive focus', () => { - getTestContext(); + it('then the correct menu item should receive focus', async () => { + await getTestContext(); - userEvent.tab(); + await userEvent.tab(); expect(screen.getAllByRole('menuitem')).toHaveLength(3); expect(screen.getByRole('menuitem', { name: 'Parent Node' })).toHaveAttribute('tabIndex', '-1'); expect(screen.getAllByRole('menuitem')[1]).toHaveAttribute('tabIndex', '-1'); expect(screen.getAllByRole('menuitem')[2]).toHaveAttribute('tabIndex', '-1'); - userEvent.keyboard('{arrowright}'); + await userEvent.keyboard('{ArrowRight}'); expect(screen.getAllByRole('menuitem')).toHaveLength(3); expect(screen.getAllByRole('menuitem')[0]).toHaveAttribute('tabIndex', '0'); expect(screen.getAllByRole('menuitem')[1]).toHaveAttribute('tabIndex', '-1'); @@ -104,18 +115,18 @@ describe('NavBarItem', () => { }); describe('when url property is set', () => { - it('then it renders the menu trigger as a link', () => { - getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } }); + it('then it renders the menu trigger as a link', async () => { + await getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } }); expect(screen.getAllByRole('link')).toHaveLength(1); expect(screen.getByRole('link')).toHaveAttribute('href', 'https://www.grafana.com'); }); describe('and hovering over the menu trigger link', () => { - it('then the menu items should be visible', () => { - getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } }); + it('then the menu items should be visible', async () => { + await getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } }); - userEvent.hover(screen.getByRole('link')); + await userEvent.hover(screen.getByRole('link')); expect(screen.getByText('Parent Node')).toBeInTheDocument(); expect(screen.getByText('Child Node 1')).toBeInTheDocument(); @@ -124,10 +135,10 @@ describe('NavBarItem', () => { }); describe('and tabbing to the menu trigger link', () => { - it('then the menu items should be visible', () => { - getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } }); + it('then the menu items should be visible', async () => { + await getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } }); - userEvent.tab(); + await userEvent.tab(); expect(screen.getByText('Parent Node')).toBeInTheDocument(); expect(screen.getByText('Child Node 1')).toBeInTheDocument(); @@ -136,17 +147,17 @@ describe('NavBarItem', () => { }); describe('and pressing arrow right on the menu trigger link', () => { - it('then the correct menu item should receive focus', () => { - getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } }); + it('then the correct menu item should receive focus', async () => { + await getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } }); - userEvent.tab(); + await userEvent.tab(); expect(screen.getAllByRole('link')[0]).toHaveFocus(); expect(screen.getAllByRole('menuitem')).toHaveLength(3); expect(screen.getAllByRole('menuitem')[0]).toHaveAttribute('tabIndex', '-1'); expect(screen.getAllByRole('menuitem')[1]).toHaveAttribute('tabIndex', '-1'); expect(screen.getAllByRole('menuitem')[2]).toHaveAttribute('tabIndex', '-1'); - userEvent.keyboard('{arrowright}'); + await userEvent.keyboard('{ArrowRight}'); expect(screen.getAllByRole('link')[0]).not.toHaveFocus(); expect(screen.getAllByRole('menuitem')).toHaveLength(3); expect(screen.getAllByRole('menuitem')[0]).toHaveAttribute('tabIndex', '0'); @@ -156,18 +167,18 @@ describe('NavBarItem', () => { }); describe('and pressing arrow left on a menu item', () => { - it('then the nav bar item should receive focus', () => { - getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } }); + it('then the nav bar item should receive focus', async () => { + await getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } }); - userEvent.tab(); - userEvent.keyboard('{arrowright}'); + await userEvent.tab(); + await userEvent.keyboard('{ArrowRight}'); expect(screen.getAllByRole('link')[0]).not.toHaveFocus(); expect(screen.getAllByRole('menuitem')).toHaveLength(3); expect(screen.getAllByRole('menuitem')[0]).toHaveAttribute('tabIndex', '0'); expect(screen.getAllByRole('menuitem')[1]).toHaveAttribute('tabIndex', '-1'); expect(screen.getAllByRole('menuitem')[2]).toHaveAttribute('tabIndex', '-1'); - userEvent.keyboard('{arrowleft}'); + await userEvent.keyboard('{ArrowLeft}'); expect(screen.getAllByRole('link')[0]).toHaveFocus(); expect(screen.getAllByRole('menuitem')).toHaveLength(3); expect(screen.getAllByRole('menuitem')[0]).toHaveAttribute('tabIndex', '-1'); @@ -178,7 +189,7 @@ describe('NavBarItem', () => { describe('when appSubUrl is configured and user clicks on menuitem link', () => { it('then location service should be called with correct url', async () => { - const { pushMock } = getTestContext( + const { pushMock } = await getTestContext( { link: { ...defaults.link, @@ -189,15 +200,13 @@ describe('NavBarItem', () => { '/grafana' ); - userEvent.hover(screen.getByRole('link')); + await userEvent.hover(screen.getByRole('link')); await waitFor(() => { expect(screen.getByText('Parent Node')).toBeInTheDocument(); expect(screen.getByText('New')).toBeInTheDocument(); }); - act(() => { - userEvent.click(screen.getByText('New')); - }); + await userEvent.click(screen.getByText('New')); await waitFor(() => { expect(pushMock).toHaveBeenCalledTimes(1); expect(pushMock).toHaveBeenCalledWith('/dashboard/new'); @@ -207,7 +216,7 @@ describe('NavBarItem', () => { describe('when appSubUrl is not configured and user clicks on menuitem link', () => { it('then location service should be called with correct url', async () => { - const { pushMock } = getTestContext({ + const { pushMock } = await getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com', @@ -215,15 +224,13 @@ describe('NavBarItem', () => { }, }); - userEvent.hover(screen.getByRole('link')); + await userEvent.hover(screen.getByRole('link')); await waitFor(() => { expect(screen.getByText('Parent Node')).toBeInTheDocument(); expect(screen.getByText('New')).toBeInTheDocument(); }); - act(() => { - userEvent.click(screen.getByText('New')); - }); + await userEvent.click(screen.getByText('New')); await waitFor(() => { expect(pushMock).toHaveBeenCalledTimes(1); expect(pushMock).toHaveBeenCalledWith('/grafana/dashboard/new'); diff --git a/public/app/core/components/NavBar/NavBarMenu.test.tsx b/public/app/core/components/NavBar/NavBarMenu.test.tsx index 10e4569e239..ce87a514863 100644 --- a/public/app/core/components/NavBar/NavBarMenu.test.tsx +++ b/public/app/core/components/NavBar/NavBarMenu.test.tsx @@ -23,10 +23,10 @@ describe('NavBarMenu', () => { expect(closeButton).toBeInTheDocument(); }); - it('clicking the close button calls the onClose callback', () => { + it('clicking the close button calls the onClose callback', async () => { const closeButton = screen.getByRole('button', { name: 'Close navigation menu' }); expect(closeButton).toBeInTheDocument(); - userEvent.click(closeButton); + await userEvent.click(closeButton); expect(mockOnClose).toHaveBeenCalled(); }); }); diff --git a/public/app/core/components/Select/ReadonlyFolderPicker/ReadonlyFolderPicker.test.tsx b/public/app/core/components/Select/ReadonlyFolderPicker/ReadonlyFolderPicker.test.tsx index d92d74df741..9a1ad8feb5d 100644 --- a/public/app/core/components/Select/ReadonlyFolderPicker/ReadonlyFolderPicker.test.tsx +++ b/public/app/core/components/Select/ReadonlyFolderPicker/ReadonlyFolderPicker.test.tsx @@ -76,7 +76,7 @@ describe('ReadonlyFolderPicker', () => { expect(within(selectors.container.get()).getByRole('combobox')).toBeInTheDocument(); getFoldersAsOptionsSpy.mockClear(); - userEvent.type(within(selectors.container.get()).getByRole('combobox'), 'A'); + await userEvent.type(within(selectors.container.get()).getByRole('combobox'), 'A'); await waitFor(() => expect(getFoldersAsOptionsSpy).toHaveBeenCalledTimes(1)); expect(getFoldersAsOptionsSpy).toHaveBeenCalledWith({ diff --git a/public/app/core/components/Signup/SignupPage.test.tsx b/public/app/core/components/Signup/SignupPage.test.tsx index e08bc005f0a..1a7be5066a2 100644 --- a/public/app/core/components/Signup/SignupPage.test.tsx +++ b/public/app/core/components/Signup/SignupPage.test.tsx @@ -60,10 +60,10 @@ describe('Signup Page', () => { fireEvent.click(screen.getByRole('button', { name: 'Submit' })); expect(await screen.findByText('Email is required')).toBeInTheDocument(); - userEvent.type(screen.getByRole('textbox', { name: 'Email' }), 'test'); + await userEvent.type(screen.getByRole('textbox', { name: 'Email' }), 'test'); await waitFor(() => expect(screen.queryByText('Email is invalid')).toBeInTheDocument()); - userEvent.type(screen.getByRole('textbox', { name: 'Email' }), 'test@gmail.com'); + await userEvent.type(screen.getByRole('textbox', { name: 'Email' }), 'test@gmail.com'); await waitFor(() => expect(screen.queryByText('Email is invalid')).not.toBeInTheDocument()); }); it('should pass validation checks for password and confirm password field', async () => { @@ -73,11 +73,11 @@ describe('Signup Page', () => { expect(await screen.findByText('Password is required')).toBeInTheDocument(); expect(await screen.findByText('Confirmed password is required')).toBeInTheDocument(); - userEvent.type(screen.getByLabelText('Password'), 'admin'); - userEvent.type(screen.getByLabelText('Confirm password'), 'a'); + await userEvent.type(screen.getByLabelText('Password'), 'admin'); + await userEvent.type(screen.getByLabelText('Confirm password'), 'a'); await waitFor(() => expect(screen.queryByText('Passwords must match!')).toBeInTheDocument()); - userEvent.type(screen.getByLabelText('Confirm password'), 'dmin'); + await userEvent.type(screen.getByLabelText('Confirm password'), 'dmin'); await waitFor(() => expect(screen.queryByText('Passwords must match!')).not.toBeInTheDocument()); }); it('should navigate to default url if signup is successful', async () => { @@ -89,10 +89,10 @@ describe('Signup Page', () => { postMock.mockResolvedValueOnce({ message: 'Logged in' }); render(); - userEvent.type(screen.getByRole('textbox', { name: 'Your name' }), 'test-user'); - userEvent.type(screen.getByRole('textbox', { name: 'Email' }), 'test@gmail.com'); - userEvent.type(screen.getByLabelText('Password'), 'admin'); - userEvent.type(screen.getByLabelText('Confirm password'), 'admin'); + await userEvent.type(screen.getByRole('textbox', { name: 'Your name' }), 'test-user'); + await userEvent.type(screen.getByRole('textbox', { name: 'Email' }), 'test@gmail.com'); + await userEvent.type(screen.getByLabelText('Password'), 'admin'); + await userEvent.type(screen.getByLabelText('Confirm password'), 'admin'); fireEvent.click(screen.getByRole('button', { name: 'Submit' })); await waitFor(() => diff --git a/public/app/core/components/Signup/VerifyEmailPage.test.tsx b/public/app/core/components/Signup/VerifyEmailPage.test.tsx index f03435676b7..637f43ee75a 100644 --- a/public/app/core/components/Signup/VerifyEmailPage.test.tsx +++ b/public/app/core/components/Signup/VerifyEmailPage.test.tsx @@ -44,17 +44,17 @@ describe('VerifyEmail Page', () => { fireEvent.click(screen.getByRole('button', { name: 'Send verification email' })); expect(await screen.findByText('Email is required')).toBeInTheDocument(); - userEvent.type(screen.getByRole('textbox', { name: /Email/i }), 'test'); + await userEvent.type(screen.getByRole('textbox', { name: /Email/i }), 'test'); await waitFor(() => expect(screen.queryByText('Email is invalid')).toBeInTheDocument()); - userEvent.type(screen.getByRole('textbox', { name: /Email/i }), 'test@gmail.com'); + await userEvent.type(screen.getByRole('textbox', { name: /Email/i }), 'test@gmail.com'); await waitFor(() => expect(screen.queryByText('Email is invalid')).not.toBeInTheDocument()); }); it('should show complete signup if email-verification is successful', async () => { postMock.mockResolvedValueOnce({ message: 'SignUpCreated' }); render(); - userEvent.type(screen.getByRole('textbox', { name: /Email/i }), 'test@gmail.com'); + await userEvent.type(screen.getByRole('textbox', { name: /Email/i }), 'test@gmail.com'); fireEvent.click(screen.getByRole('button', { name: 'Send verification email' })); await waitFor(() => diff --git a/public/app/core/specs/OrgSwitcher.test.tsx b/public/app/core/specs/OrgSwitcher.test.tsx index 829fa3ac9c0..b99b44de1da 100644 --- a/public/app/core/specs/OrgSwitcher.test.tsx +++ b/public/app/core/specs/OrgSwitcher.test.tsx @@ -53,7 +53,7 @@ describe('OrgSwitcher', () => { it('should switch orgId in call to backend', async () => { const row = screen.getByRole('row', { name: /org 2 admin switch to/i }); const switchToButton = within(row).getByText(/switch to/i); - userEvent.click(switchToButton); + await userEvent.click(switchToButton); await waitFor(() => expect(setUserOrgSpy).toBeCalledWith({ orgId: 2, name: 'Org 2', role: 'Admin' })); }); @@ -63,7 +63,7 @@ describe('OrgSwitcher', () => { const row = screen.getByRole('row', { name: /org 2 admin switch to/i }); const switchToButton = within(row).getByText(/switch to/i); - userEvent.click(switchToButton); + await userEvent.click(switchToButton); await waitFor(() => expect(window.location.href).toEqual('/subUrl/?orgId=2')); }); diff --git a/public/app/features/alerting/components/UnifiedAlertingPromotion.test.tsx b/public/app/features/alerting/components/UnifiedAlertingPromotion.test.tsx index 1a5df7e6527..3fa5f2a330a 100644 --- a/public/app/features/alerting/components/UnifiedAlertingPromotion.test.tsx +++ b/public/app/features/alerting/components/UnifiedAlertingPromotion.test.tsx @@ -14,12 +14,12 @@ describe('Unified Alerting promotion', () => { expect(screen.queryByText('Try out the Grafana 8 alerting!')).toBeInTheDocument(); }); - it('should be hidden if dismissed', () => { + it('should be hidden if dismissed', async () => { const promotion = render(); expect(window.localStorage.getItem(LOCAL_STORAGE_KEY)).toBe('true'); const dismissButton = promotion.getByRole('button'); - userEvent.click(dismissButton); + await userEvent.click(dismissButton); expect(screen.queryByText('Try out the Grafana 8 alerting!')).not.toBeInTheDocument(); expect(window.localStorage.getItem(LOCAL_STORAGE_KEY)).toBe('false'); diff --git a/public/app/features/alerting/unified/AlertGroups.test.tsx b/public/app/features/alerting/unified/AlertGroups.test.tsx index 765af678612..40fecd2d7bf 100644 --- a/public/app/features/alerting/unified/AlertGroups.test.tsx +++ b/public/app/features/alerting/unified/AlertGroups.test.tsx @@ -83,10 +83,10 @@ describe('AlertGroups', () => { expect(groups[0]).toHaveTextContent('No grouping'); expect(groups[1]).toHaveTextContent('severity=warningregion=US-Central'); - userEvent.click(ui.groupCollapseToggle.get(groups[0])); + await userEvent.click(ui.groupCollapseToggle.get(groups[0])); expect(ui.groupTable.get()).toBeDefined(); - userEvent.click(ui.collapseToggle.get(ui.groupTable.get())); + await userEvent.click(ui.collapseToggle.get(ui.groupTable.get())); expect(ui.silenceButton.get(ui.groupTable.get())).toBeDefined(); expect(ui.sourceButton.get(ui.groupTable.get())).toBeDefined(); }); @@ -118,7 +118,7 @@ describe('AlertGroups', () => { expect(groups[1]).toHaveTextContent('region=EMEA'); expect(groups[2]).toHaveTextContent('region=APAC'); - userEvent.type(groupByInput, 'appName{enter}'); + await userEvent.type(groupByInput, 'appName{enter}'); await waitFor(() => expect(groupByWrapper).toHaveTextContent('appName')); @@ -130,10 +130,10 @@ describe('AlertGroups', () => { expect(groups[1]).toHaveTextContent('appName=auth'); expect(groups[2]).toHaveTextContent('appName=frontend'); - userEvent.click(ui.clearButton.get()); + await userEvent.click(ui.clearButton.get()); await waitFor(() => expect(groupByWrapper).not.toHaveTextContent('appName')); - userEvent.type(groupByInput, 'env{enter}'); + await userEvent.type(groupByInput, 'env{enter}'); await waitFor(() => expect(groupByWrapper).toHaveTextContent('env')); groups = ui.group.getAll(); @@ -142,10 +142,10 @@ describe('AlertGroups', () => { expect(groups[0]).toHaveTextContent('env=production'); expect(groups[1]).toHaveTextContent('env=staging'); - userEvent.click(ui.clearButton.get()); + await userEvent.click(ui.clearButton.get()); await waitFor(() => expect(groupByWrapper).not.toHaveTextContent('env')); - userEvent.type(groupByInput, 'uniqueLabel{enter}'); + await userEvent.type(groupByInput, 'uniqueLabel{enter}'); await waitFor(() => expect(groupByWrapper).toHaveTextContent('uniqueLabel')); groups = ui.group.getAll(); diff --git a/public/app/features/alerting/unified/AlertsFolderView.test.tsx b/public/app/features/alerting/unified/AlertsFolderView.test.tsx index 940bd63d141..39793a96677 100644 --- a/public/app/features/alerting/unified/AlertsFolderView.test.tsx +++ b/public/app/features/alerting/unified/AlertsFolderView.test.tsx @@ -116,7 +116,7 @@ describe('AlertsFolderView tests', () => { expect(ui.ruleList.row.queryAll()).toHaveLength(0); }); - it('Should filter alert rules by the name, case insensitive', () => { + it('Should filter alert rules by the name, case insensitive', async () => { // Arrange const store = configureStore(); const folder = mockFolder(); @@ -143,14 +143,14 @@ describe('AlertsFolderView tests', () => { ); - userEvent.type(ui.filter.name.get(), 'cpu'); + await userEvent.type(ui.filter.name.get(), 'cpu'); // Assert expect(ui.ruleList.row.queryAll()).toHaveLength(1); expect(ui.ruleList.row.get()).toHaveTextContent('CPU Alert'); }); - it('Should filter alert rule by labels', () => { + it('Should filter alert rule by labels', async () => { // Arrange const store = configureStore(); const folder = mockFolder(); @@ -180,7 +180,7 @@ describe('AlertsFolderView tests', () => { ); - userEvent.type(ui.filter.label.get(), 'severity=critical'); + await userEvent.type(ui.filter.label.get(), 'severity=critical'); // Assert expect(ui.ruleList.row.queryAll()).toHaveLength(1); diff --git a/public/app/features/alerting/unified/AmRoutes.test.tsx b/public/app/features/alerting/unified/AmRoutes.test.tsx index 85a561165ba..414f90b6cf4 100644 --- a/public/app/features/alerting/unified/AmRoutes.test.tsx +++ b/public/app/features/alerting/unified/AmRoutes.test.tsx @@ -273,24 +273,24 @@ describe('AmRoutes', () => { // open root route for editing const rootRouteContainer = await ui.rootRouteContainer.find(); - userEvent.click(ui.editButton.get(rootRouteContainer)); + await userEvent.click(ui.editButton.get(rootRouteContainer)); // configure receiver & group by const receiverSelect = await ui.receiverSelect.find(); await clickSelectOption(receiverSelect, 'critical'); const groupSelect = ui.groupSelect.get(); - userEvent.type(byRole('combobox').get(groupSelect), 'namespace{enter}'); + await userEvent.type(byRole('combobox').get(groupSelect), 'namespace{enter}'); // configure timing intervals - userEvent.click(byText('Timing options').get(rootRouteContainer)); + await userEvent.click(byText('Timing options').get(rootRouteContainer)); await updateTiming(ui.groupWaitContainer.get(), '1', 'Minutes'); await updateTiming(ui.groupIntervalContainer.get(), '4', 'Minutes'); await updateTiming(ui.groupRepeatContainer.get(), '5', 'Hours'); //save - userEvent.click(ui.saveButton.get(rootRouteContainer)); + await userEvent.click(ui.saveButton.get(rootRouteContainer)); // wait for it to go out of edit mode await waitFor(() => expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument()); @@ -333,17 +333,17 @@ describe('AmRoutes', () => { // open root route for editing const rootRouteContainer = await ui.rootRouteContainer.find(); - userEvent.click(ui.editButton.get(rootRouteContainer)); + await userEvent.click(ui.editButton.get(rootRouteContainer)); // configure receiver & group by const receiverSelect = await ui.receiverSelect.find(); await clickSelectOption(receiverSelect, 'default'); const groupSelect = ui.groupSelect.get(); - userEvent.type(byRole('combobox').get(groupSelect), 'severity{enter}'); - userEvent.type(byRole('combobox').get(groupSelect), 'namespace{enter}'); + await userEvent.type(byRole('combobox').get(groupSelect), 'severity{enter}'); + await userEvent.type(byRole('combobox').get(groupSelect), 'namespace{enter}'); //save - userEvent.click(ui.saveButton.get(rootRouteContainer)); + await userEvent.click(ui.saveButton.get(rootRouteContainer)); // wait for it to go out of edit mode await waitFor(() => expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument()); @@ -426,8 +426,8 @@ describe('AmRoutes', () => { // Toggle a save to test new object_matchers const rootRouteContainer = await ui.rootRouteContainer.find(); - userEvent.click(ui.editButton.get(rootRouteContainer)); - userEvent.click(ui.saveButton.get(rootRouteContainer)); + await userEvent.click(ui.editButton.get(rootRouteContainer)); + await userEvent.click(ui.saveButton.get(rootRouteContainer)); await waitFor(() => expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument()); @@ -497,8 +497,8 @@ describe('AmRoutes', () => { // Toggle a save to test new object_matchers const rootRouteContainer = await ui.rootRouteContainer.find(); - userEvent.click(ui.editButton.get(rootRouteContainer)); - userEvent.click(ui.saveButton.get(rootRouteContainer)); + await userEvent.click(ui.editButton.get(rootRouteContainer)); + await userEvent.click(ui.saveButton.get(rootRouteContainer)); await waitFor(() => expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument()); @@ -599,7 +599,7 @@ describe('AmRoutes', () => { await renderAmRoutes(dataSources.am.name); const rows = await ui.row.findAll(); expect(rows).toHaveLength(1); - userEvent.click(ui.editRouteButton.get(rows[0])); + await userEvent.click(ui.editRouteButton.get(rows[0])); const muteTimingSelect = ui.muteTimingSelect.get(); await clickSelectOption(muteTimingSelect, 'default-mute'); @@ -608,7 +608,7 @@ describe('AmRoutes', () => { const savePolicyButton = ui.savePolicyButton.get(); expect(savePolicyButton).toBeInTheDocument(); - userEvent.click(savePolicyButton); + await userEvent.click(savePolicyButton); await waitFor(() => expect(savePolicyButton).not.toBeInTheDocument()); @@ -637,14 +637,14 @@ describe('AmRoutes', () => { }); const clickSelectOption = async (selectElement: HTMLElement, optionText: string): Promise => { - userEvent.click(byRole('combobox').get(selectElement)); + await userEvent.click(byRole('combobox').get(selectElement)); await selectOptionInTest(selectElement, optionText); }; const updateTiming = async (selectElement: HTMLElement, value: string, timeUnit: string): Promise => { const input = byRole('textbox').get(selectElement); const select = byRole('combobox').get(selectElement); - userEvent.type(input, value); - userEvent.click(select); + await userEvent.type(input, value); + await userEvent.click(select); await selectOptionInTest(selectElement, timeUnit); }; diff --git a/public/app/features/alerting/unified/MuteTimings.test.tsx b/public/app/features/alerting/unified/MuteTimings.test.tsx index d5248e923c8..0a8c494d2c4 100644 --- a/public/app/features/alerting/unified/MuteTimings.test.tsx +++ b/public/app/features/alerting/unified/MuteTimings.test.tsx @@ -118,11 +118,11 @@ describe('Mute timings', () => { await waitFor(() => expect(mocks.api.fetchAlertManagerConfig).toHaveBeenCalled()); expect(ui.nameField.get()).toBeInTheDocument(); - userEvent.type(ui.nameField.get(), 'maintenance period'); - userEvent.type(ui.startsAt.get(), '22:00'); - userEvent.type(ui.endsAt.get(), '24:00'); - userEvent.type(ui.days.get(), '-1'); - userEvent.type(ui.months.get(), 'january, july'); + await userEvent.type(ui.nameField.get(), 'maintenance period'); + await userEvent.type(ui.startsAt.get(), '22:00'); + await userEvent.type(ui.endsAt.get(), '24:00'); + await userEvent.type(ui.days.get(), '-1'); + await userEvent.type(ui.months.get(), 'january, july'); fireEvent.submit(ui.form.get()); @@ -163,17 +163,17 @@ describe('Mute timings', () => { expect(ui.nameField.get()).toHaveValue(muteTimeInterval.name); expect(ui.months.get()).toHaveValue(muteTimeInterval.time_intervals[0].months?.join(', ')); - userEvent.clear(ui.startsAt.getAll()?.[0]); - userEvent.clear(ui.endsAt.getAll()?.[0]); - userEvent.clear(ui.weekdays.get()); - userEvent.clear(ui.days.get()); - userEvent.clear(ui.months.get()); - userEvent.clear(ui.years.get()); + await userEvent.clear(ui.startsAt.getAll()?.[0]); + await userEvent.clear(ui.endsAt.getAll()?.[0]); + await userEvent.clear(ui.weekdays.get()); + await userEvent.clear(ui.days.get()); + await userEvent.clear(ui.months.get()); + await userEvent.clear(ui.years.get()); - userEvent.type(ui.weekdays.get(), 'monday'); - userEvent.type(ui.days.get(), '-7:-1'); - userEvent.type(ui.months.get(), '3, 6, 9, 12'); - userEvent.type(ui.years.get(), '2021:2024'); + await userEvent.type(ui.weekdays.get(), 'monday'); + await userEvent.type(ui.days.get(), '-7:-1'); + await userEvent.type(ui.months.get(), '3, 6, 9, 12'); + await userEvent.type(ui.years.get(), '2021:2024'); fireEvent.submit(ui.form.get()); @@ -243,8 +243,8 @@ describe('Mute timings', () => { expect(ui.nameField.get()).toBeInTheDocument(); expect(ui.nameField.get()).toHaveValue(muteTimeInterval.name); - userEvent.clear(ui.nameField.get()); - userEvent.type(ui.nameField.get(), 'Lunch breaks'); + await userEvent.clear(ui.nameField.get()); + await userEvent.type(ui.nameField.get(), 'Lunch breaks'); fireEvent.submit(ui.form.get()); diff --git a/public/app/features/alerting/unified/Receivers.test.tsx b/public/app/features/alerting/unified/Receivers.test.tsx index 0b335b3c5f1..0f5c6af28bf 100644 --- a/public/app/features/alerting/unified/Receivers.test.tsx +++ b/public/app/features/alerting/unified/Receivers.test.tsx @@ -4,7 +4,7 @@ import { Router } from 'react-router-dom'; import Receivers from './Receivers'; import React from 'react'; import { locationService, setDataSourceSrv } from '@grafana/runtime'; -import { act, render, waitFor } from '@testing-library/react'; +import { render, waitFor } from '@testing-library/react'; import { getAllDataSources } from './utils/config'; import { updateAlertManagerConfig, fetchAlertManagerConfig, fetchStatus, testReceivers } from './api/alertmanager'; import { @@ -116,7 +116,7 @@ const ui = { }; const clickSelectOption = async (selectElement: HTMLElement, optionText: string): Promise => { - userEvent.click(byRole('combobox').get(selectElement)); + await userEvent.click(byRole('combobox').get(selectElement)); await selectOptionInTest(selectElement, optionText); }; @@ -194,29 +194,25 @@ describe('Receivers', () => { await renderReceivers(); // go to new contact point page - await act(async () => { - userEvent.click(await ui.newContactPointButton.find()); - }); + await userEvent.click(await ui.newContactPointButton.find()); await byRole('heading', { name: /create contact point/i }).find(); expect(locationService.getLocation().pathname).toEqual('/alerting/notifications/receivers/new'); - await act(async () => { - // type in a name for the new receiver - userEvent.type(ui.inputs.name.get(), 'my new receiver'); + // type in a name for the new receiver + await userEvent.type(ui.inputs.name.get(), 'my new receiver'); - // enter some email - const email = ui.inputs.email.addresses.get(); - userEvent.clear(email); - userEvent.type(email, 'tester@grafana.com'); + // enter some email + const email = ui.inputs.email.addresses.get(); + await userEvent.clear(email); + await userEvent.type(email, 'tester@grafana.com'); - // try to test the contact point - userEvent.click(await ui.testContactPointButton.find()); - }); + // try to test the contact point + await userEvent.click(await ui.testContactPointButton.find()); await waitFor(() => expect(ui.testContactPointModal.get()).toBeInTheDocument(), { timeout: 1000 }); - userEvent.click(ui.customContactPointOption.get()); + await userEvent.click(ui.customContactPointOption.get()); await waitFor(() => expect(ui.contactPointAnnotationSelect(0).get()).toBeInTheDocument()); // enter custom annotations and labels @@ -224,7 +220,7 @@ describe('Receivers', () => { await userEvent.type(ui.contactPointAnnotationValue(0).get(), 'Test contact point'); await userEvent.type(ui.contactPointLabelKey(0).get(), 'foo'); await userEvent.type(ui.contactPointLabelValue(0).get(), 'bar'); - userEvent.click(ui.testContactPoint.get()); + await userEvent.click(ui.testContactPoint.get()); await waitFor(() => expect(mocks.api.testReceivers).toHaveBeenCalled()); @@ -260,7 +256,7 @@ describe('Receivers', () => { expect(locationService.getLocation().pathname).toEqual('/alerting/notifications/receivers/new'); // type in a name for the new receiver - userEvent.type(byPlaceholderText('Name').get(), 'my new receiver'); + await userEvent.type(byPlaceholderText('Name').get(), 'my new receiver'); // check that default email form is rendered await ui.inputs.email.addresses.find(); @@ -274,13 +270,10 @@ describe('Receivers', () => { const urlInput = ui.inputs.hipchat.url.get(); const apiKeyInput = ui.inputs.hipchat.apiKey.get(); - userEvent.type(urlInput, 'http://hipchat'); - userEvent.type(apiKeyInput, 'foobarbaz'); + await userEvent.type(urlInput, 'http://hipchat'); + await userEvent.type(apiKeyInput, 'foobarbaz'); - // it seems react-hook-form does some async state updates after submit - await act(async () => { - userEvent.click(await ui.saveContactButton.find()); - }); + await userEvent.click(await ui.saveContactButton.find()); // see that we're back to main page and proper api calls have been made await ui.receiversTable.find(); @@ -350,13 +343,13 @@ describe('Receivers', () => { // modify webhook url const slackContainer = ui.channelFormContainer.get(); await userEvent.click(byText('Optional Slack settings').get(slackContainer)); - userEvent.type(ui.inputs.slack.webhookURL.get(slackContainer), 'http://newgreaturl'); + await userEvent.type(ui.inputs.slack.webhookURL.get(slackContainer), 'http://newgreaturl'); // add confirm button to action await userEvent.click(byText(/Actions \(1\)/i).get(slackContainer)); await userEvent.click(await byTestId('items.1.settings.actions.0.confirm.add-button').find()); const confirmSubform = byTestId('items.1.settings.actions.0.confirm.container').get(); - userEvent.type(byLabelText('Text').get(confirmSubform), 'confirm this'); + await userEvent.type(byLabelText('Text').get(confirmSubform), 'confirm this'); // delete a field await userEvent.click(byText(/Fields \(2\)/i).get(slackContainer)); @@ -366,12 +359,9 @@ describe('Receivers', () => { // add another channel await userEvent.click(ui.newContactPointTypeButton.get()); await clickSelectOption(await byTestId('items.2.type').find(), 'Webhook'); - userEvent.type(await ui.inputs.webhook.URL.find(), 'http://webhookurl'); + await userEvent.type(await ui.inputs.webhook.URL.find(), 'http://webhookurl'); - // it seems react-hook-form does some async state updates after submit - await act(async () => { - await userEvent.click(ui.saveContactButton.get()); - }); + await userEvent.click(ui.saveContactButton.get()); // see that we're back to main page and proper api calls have been made await ui.receiversTable.find(); @@ -438,7 +428,7 @@ describe('Receivers', () => { const receiverRows = receiversTable.querySelectorAll('tbody tr'); expect(receiverRows[0]).toHaveTextContent('cloud-receiver'); expect(byTestId('edit').query(receiverRows[0])).not.toBeInTheDocument(); - userEvent.click(byTestId('view').get(receiverRows[0])); + await userEvent.click(byTestId('view').get(receiverRows[0])); // check that form is open await byRole('heading', { name: /contact point/i }).find(); diff --git a/public/app/features/alerting/unified/RuleEditor.test.tsx b/public/app/features/alerting/unified/RuleEditor.test.tsx index fc860ea5669..dc1fa82d5ef 100644 --- a/public/app/features/alerting/unified/RuleEditor.test.tsx +++ b/public/app/features/alerting/unified/RuleEditor.test.tsx @@ -9,7 +9,7 @@ import { byLabelText, byRole, byTestId, byText } from 'testing-library-selector' import { selectOptionInTest } from '@grafana/ui'; import { contextSrv } from 'app/core/services/context_srv'; import { mockDataSource, MockDataSourceSrv } from './mocks'; -import userEvent from '@testing-library/user-event'; +import userEvent, { PointerEventsCheckLevel } from '@testing-library/user-event'; import { DataSourceInstanceSettings } from '@grafana/data'; import { getAllDataSources } from './utils/config'; import { fetchRulerRules, fetchRulerRulesGroup, fetchRulerRulesNamespace, setRulerRuleGroup } from './api/ruler'; @@ -144,30 +144,30 @@ describe('RuleEditor', () => { await waitFor(() => expect(mocks.searchFolders).toHaveBeenCalled()); await waitFor(() => expect(mocks.api.fetchBuildInfo).toHaveBeenCalled()); - userEvent.type(await ui.inputs.name.find(), 'my great new rule'); - userEvent.click(await ui.buttons.lotexAlert.get()); + await userEvent.type(await ui.inputs.name.find(), 'my great new rule'); + await userEvent.click(await ui.buttons.lotexAlert.get()); const dataSourceSelect = ui.inputs.dataSource.get(); - userEvent.click(byRole('combobox').get(dataSourceSelect)); + await userEvent.click(byRole('combobox').get(dataSourceSelect)); await clickSelectOption(dataSourceSelect, 'Prom (default)'); await waitFor(() => expect(mocks.api.fetchRulerRules).toHaveBeenCalled()); await clickSelectOption(ui.inputs.namespace.get(), 'namespace2'); await clickSelectOption(ui.inputs.group.get(), 'group2'); - userEvent.type(ui.inputs.expr.get(), 'up == 1'); + await userEvent.type(ui.inputs.expr.get(), 'up == 1'); - userEvent.type(ui.inputs.annotationValue(0).get(), 'some summary'); - userEvent.type(ui.inputs.annotationValue(1).get(), 'some description'); + await userEvent.type(ui.inputs.annotationValue(0).get(), 'some summary'); + await userEvent.type(ui.inputs.annotationValue(1).get(), 'some description'); // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed - userEvent.click(ui.buttons.addLabel.get(), undefined, { skipPointerEventsCheck: true }); + await userEvent.click(ui.buttons.addLabel.get(), { pointerEventsCheck: PointerEventsCheckLevel.Never }); - userEvent.type(ui.inputs.labelKey(0).get(), 'severity'); - userEvent.type(ui.inputs.labelValue(0).get(), 'warn'); - userEvent.type(ui.inputs.labelKey(1).get(), 'team'); - userEvent.type(ui.inputs.labelValue(1).get(), 'the a-team'); + await userEvent.type(ui.inputs.labelKey(0).get(), 'severity'); + await userEvent.type(ui.inputs.labelValue(0).get(), 'warn'); + await userEvent.type(ui.inputs.labelKey(1).get(), 'team'); + await userEvent.type(ui.inputs.labelValue(1).get(), 'the a-team'); // save and check what was sent to backend - userEvent.click(ui.buttons.save.get()); + await userEvent.click(ui.buttons.save.get()); await waitFor(() => expect(mocks.api.setRulerRuleGroup).toHaveBeenCalled()); expect(mocks.api.setRulerRuleGroup).toHaveBeenCalledWith( { dataSourceName: 'Prom', apiVersion: 'legacy' }, @@ -244,27 +244,27 @@ describe('RuleEditor', () => { await waitFor(() => expect(mocks.searchFolders).toHaveBeenCalled()); await waitFor(() => expect(mocks.api.fetchBuildInfo).toHaveBeenCalled()); - userEvent.type(await ui.inputs.name.find(), 'my great new rule'); + await userEvent.type(await ui.inputs.name.find(), 'my great new rule'); const folderInput = await ui.inputs.folder.find(); await clickSelectOption(folderInput, 'Folder A'); const groupInput = screen.getByRole('textbox', { name: /^Group/ }); - userEvent.type(groupInput, 'my group'); + await userEvent.type(groupInput, 'my group'); - userEvent.type(ui.inputs.annotationValue(0).get(), 'some summary'); - userEvent.type(ui.inputs.annotationValue(1).get(), 'some description'); + await userEvent.type(ui.inputs.annotationValue(0).get(), 'some summary'); + await userEvent.type(ui.inputs.annotationValue(1).get(), 'some description'); // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed - userEvent.click(ui.buttons.addLabel.get(), undefined, { skipPointerEventsCheck: true }); + await userEvent.click(ui.buttons.addLabel.get(), { pointerEventsCheck: PointerEventsCheckLevel.Never }); - userEvent.type(ui.inputs.labelKey(0).get(), 'severity'); - userEvent.type(ui.inputs.labelValue(0).get(), 'warn'); - userEvent.type(ui.inputs.labelKey(1).get(), 'team'); - userEvent.type(ui.inputs.labelValue(1).get(), 'the a-team'); + await userEvent.type(ui.inputs.labelKey(0).get(), 'severity'); + await userEvent.type(ui.inputs.labelValue(0).get(), 'warn'); + await userEvent.type(ui.inputs.labelKey(1).get(), 'team'); + await userEvent.type(ui.inputs.labelValue(1).get(), 'the a-team'); // save and check what was sent to backend - userEvent.click(ui.buttons.save.get()); + await userEvent.click(ui.buttons.save.get()); await waitFor(() => expect(mocks.api.setRulerRuleGroup).toHaveBeenCalled()); expect(mocks.api.setRulerRuleGroup).toHaveBeenCalledWith( { dataSourceName: GRAFANA_RULES_SOURCE_NAME, apiVersion: 'legacy' }, @@ -336,27 +336,27 @@ describe('RuleEditor', () => { await renderRuleEditor(); await waitFor(() => expect(mocks.searchFolders).toHaveBeenCalled()); await waitFor(() => expect(mocks.api.fetchBuildInfo).toHaveBeenCalled()); - userEvent.type(await ui.inputs.name.find(), 'my great new recording rule'); - userEvent.click(await ui.buttons.lotexRecordingRule.get()); + await userEvent.type(await ui.inputs.name.find(), 'my great new recording rule'); + await userEvent.click(await ui.buttons.lotexRecordingRule.get()); const dataSourceSelect = ui.inputs.dataSource.get(); - userEvent.click(byRole('combobox').get(dataSourceSelect)); + await userEvent.click(byRole('combobox').get(dataSourceSelect)); await clickSelectOption(dataSourceSelect, 'Prom (default)'); await waitFor(() => expect(mocks.api.fetchRulerRules).toHaveBeenCalled()); await clickSelectOption(ui.inputs.namespace.get(), 'namespace2'); await clickSelectOption(ui.inputs.group.get(), 'group2'); - userEvent.type(ui.inputs.expr.get(), 'up == 1'); + await userEvent.type(ui.inputs.expr.get(), 'up == 1'); // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed - userEvent.click(ui.buttons.addLabel.get(), undefined, { skipPointerEventsCheck: true }); + await userEvent.click(ui.buttons.addLabel.get(), { pointerEventsCheck: PointerEventsCheckLevel.Never }); - userEvent.type(ui.inputs.labelKey(1).get(), 'team'); - userEvent.type(ui.inputs.labelValue(1).get(), 'the a-team'); + await userEvent.type(ui.inputs.labelKey(1).get(), 'team'); + await userEvent.type(ui.inputs.labelValue(1).get(), 'the a-team'); // try to save, find out that recording rule name is invalid - userEvent.click(ui.buttons.save.get()); + await userEvent.click(ui.buttons.save.get()); await waitFor(() => expect( byText( @@ -367,11 +367,11 @@ describe('RuleEditor', () => { expect(mocks.api.setRulerRuleGroup).not.toBeCalled(); // fix name and re-submit - userEvent.type(await ui.inputs.name.find(), '{selectall}{del}my:great:new:recording:rule'); - userEvent.click(ui.buttons.save.get()); + await userEvent.clear(await ui.inputs.name.find()); + await userEvent.type(await ui.inputs.name.find(), 'my:great:new:recording:rule'); // save and check what was sent to backend - userEvent.click(ui.buttons.save.get()); + await userEvent.click(ui.buttons.save.get()); await waitFor(() => expect(mocks.api.setRulerRuleGroup).toHaveBeenCalled()); expect(mocks.api.setRulerRuleGroup).toHaveBeenCalledWith( { dataSourceName: 'Prom', apiVersion: 'legacy' }, @@ -458,15 +458,15 @@ describe('RuleEditor', () => { // add an annotation await clickSelectOption(ui.inputs.annotationKey(2).get(), /Add new/); - userEvent.type(byRole('textbox').get(ui.inputs.annotationKey(2).get()), 'custom'); - userEvent.type(ui.inputs.annotationValue(2).get(), 'value'); + await userEvent.type(byRole('textbox').get(ui.inputs.annotationKey(2).get()), 'custom'); + await userEvent.type(ui.inputs.annotationValue(2).get(), 'value'); //add a label - userEvent.type(ui.inputs.labelKey(2).get(), 'custom'); - userEvent.type(ui.inputs.labelValue(2).get(), 'value'); + await userEvent.type(ui.inputs.labelKey(2).get(), 'custom'); + await userEvent.type(ui.inputs.labelValue(2).get(), 'value'); // save and check what was sent to backend - userEvent.click(ui.buttons.save.get()); + await userEvent.click(ui.buttons.save.get()); await waitFor(() => expect(mocks.api.setRulerRuleGroup).toHaveBeenCalled()); expect(mocks.api.setRulerRuleGroup).toHaveBeenCalledWith( @@ -614,11 +614,11 @@ describe('RuleEditor', () => { await waitFor(() => expect(mocks.searchFolders).toHaveBeenCalled()); await ui.inputs.name.find(); - userEvent.click(await ui.buttons.lotexAlert.get()); + await userEvent.click(await ui.buttons.lotexAlert.get()); // check that only rules sources that have ruler available are there const dataSourceSelect = ui.inputs.dataSource.get(); - userEvent.click(byRole('combobox').get(dataSourceSelect)); + await userEvent.click(byRole('combobox').get(dataSourceSelect)); expect(await byText('loki with ruler').query()).toBeInTheDocument(); expect(byText('cortex with ruler').query()).toBeInTheDocument(); expect(byText('loki with local rule store').query()).not.toBeInTheDocument(); @@ -629,6 +629,6 @@ describe('RuleEditor', () => { }); const clickSelectOption = async (selectElement: HTMLElement, optionText: Matcher): Promise => { - userEvent.click(byRole('combobox').get(selectElement)); + await userEvent.click(byRole('combobox').get(selectElement)); await selectOptionInTest(selectElement, optionText as string); }; diff --git a/public/app/features/alerting/unified/RuleList.test.tsx b/public/app/features/alerting/unified/RuleList.test.tsx index 72cdcc1a3a8..3a198bc403c 100644 --- a/public/app/features/alerting/unified/RuleList.test.tsx +++ b/public/app/features/alerting/unified/RuleList.test.tsx @@ -202,7 +202,7 @@ describe('RuleList', () => { expect(errors).not.toHaveTextContent( 'Failed to load rules state from Prometheus-broken: this datasource is broken' ); - userEvent.click(ui.moreErrorsButton.get()); + await userEvent.click(ui.moreErrorsButton.get()); expect(errors).toHaveTextContent('Failed to load rules state from Prometheus-broken: this datasource is broken'); }); @@ -293,7 +293,7 @@ describe('RuleList', () => { // expand second group to see rules table expect(ui.rulesTable.query()).not.toBeInTheDocument(); - userEvent.click(ui.groupCollapseToggle.get(groups[1])); + await userEvent.click(ui.groupCollapseToggle.get(groups[1])); const table = await ui.rulesTable.find(groups[1]); // check that rule rows are rendered properly @@ -315,7 +315,7 @@ describe('RuleList', () => { expect(byText('Labels').query()).not.toBeInTheDocument(); // expand alert details - userEvent.click(ui.ruleCollapseToggle.get(ruleRows[1])); + await userEvent.click(ui.ruleCollapseToggle.get(ruleRows[1])); const ruleDetails = ui.expandedContent.get(ruleRows[1]); @@ -334,17 +334,17 @@ describe('RuleList', () => { expect(instanceRows![1]).toHaveTextContent('Firingfoo=bazseverity=error2021-03-18 13:47:05'); // expand details of an instance - userEvent.click(ui.ruleCollapseToggle.get(instanceRows![0])); + await userEvent.click(ui.ruleCollapseToggle.get(instanceRows![0])); const alertDetails = byTestId('expanded-content').get(instanceRows[0]); expect(alertDetails).toHaveTextContent('Value2e+10'); expect(alertDetails).toHaveTextContent('messagefirst alert message'); // collapse everything again - userEvent.click(ui.ruleCollapseToggle.get(instanceRows![0])); + await userEvent.click(ui.ruleCollapseToggle.get(instanceRows![0])); expect(byTestId('expanded-content').query(instanceRows[0])).not.toBeInTheDocument(); - userEvent.click(ui.ruleCollapseToggle.getAll(ruleRows[1])[0]); - userEvent.click(ui.groupCollapseToggle.get(groups[1])); + await userEvent.click(ui.ruleCollapseToggle.getAll(ruleRows[1])[0]); + await userEvent.click(ui.groupCollapseToggle.get(groups[1])); expect(ui.rulesTable.query()).not.toBeInTheDocument(); }); @@ -456,33 +456,36 @@ describe('RuleList', () => { expect(groups).toHaveLength(2); const filterInput = ui.rulesFilterInput.get(); - userEvent.type(filterInput, '{{foo="bar"}'); + await userEvent.type(filterInput, '{{foo="bar"}'); // Input is debounced so wait for it to be visible await waitFor(() => expect(filterInput).toHaveValue('{foo="bar"}')); // Group doesn't contain matching labels await waitFor(() => expect(ui.ruleGroup.queryAll()).toHaveLength(1)); - userEvent.click(ui.groupCollapseToggle.get(groups[0])); + await userEvent.click(ui.groupCollapseToggle.get(groups[0])); const ruleRows = ui.ruleRow.getAll(groups[0]); expect(ruleRows).toHaveLength(1); - userEvent.click(ui.ruleCollapseToggle.get(ruleRows[0])); + await userEvent.click(ui.ruleCollapseToggle.get(ruleRows[0])); const ruleDetails = ui.expandedContent.get(ruleRows[0]); expect(ruleDetails).toHaveTextContent('Labelsseverity=warningfoo=bar'); // Check for different label matchers - userEvent.type(filterInput, '{selectall}{del}{{foo!="bar",foo!="baz"}'); + await userEvent.clear(filterInput); + await userEvent.type(filterInput, '{{foo!="bar",foo!="baz"}'); // Group doesn't contain matching labels await waitFor(() => expect(ui.ruleGroup.queryAll()).toHaveLength(1)); await waitFor(() => expect(ui.ruleGroup.get()).toHaveTextContent('group-2')); - userEvent.type(filterInput, '{selectall}{del}{{foo=~"b.+"}'); + await userEvent.clear(filterInput); + await userEvent.type(filterInput, '{{foo=~"b.+"}'); await waitFor(() => expect(ui.ruleGroup.queryAll()).toHaveLength(2)); - userEvent.type(filterInput, '{selectall}{del}{{region="US"}'); + await userEvent.clear(filterInput); + await userEvent.type(filterInput, '{{region="US"}'); await waitFor(() => expect(ui.ruleGroup.queryAll()).toHaveLength(1)); await waitFor(() => expect(ui.ruleGroup.get()).toHaveTextContent('group-2')); }); @@ -521,7 +524,7 @@ describe('RuleList', () => { expect(groups).toHaveLength(3); // open edit dialog - userEvent.click(ui.editCloudGroupIcon.get(groups[0])); + await userEvent.click(ui.editCloudGroupIcon.get(groups[0])); expect(ui.editGroupModal.namespaceInput.get()).toHaveValue('namespace1'); expect(ui.editGroupModal.ruleGroupInput.get()).toHaveValue('group1'); @@ -531,16 +534,16 @@ describe('RuleList', () => { testCase('rename both lotex namespace and group', async () => { // make changes to form - userEvent.clear(ui.editGroupModal.namespaceInput.get()); - userEvent.type(ui.editGroupModal.namespaceInput.get(), 'super namespace'); + await userEvent.clear(ui.editGroupModal.namespaceInput.get()); + await userEvent.type(ui.editGroupModal.namespaceInput.get(), 'super namespace'); - userEvent.clear(ui.editGroupModal.ruleGroupInput.get()); - userEvent.type(ui.editGroupModal.ruleGroupInput.get(), 'super group'); + await userEvent.clear(ui.editGroupModal.ruleGroupInput.get()); + await userEvent.type(ui.editGroupModal.ruleGroupInput.get(), 'super group'); - userEvent.type(ui.editGroupModal.intervalInput.get(), '5m'); + await userEvent.type(ui.editGroupModal.intervalInput.get(), '5m'); // submit, check that appropriate calls were made - userEvent.click(ui.editGroupModal.saveButton.get()); + await userEvent.click(ui.editGroupModal.saveButton.get()); await waitFor(() => expect(ui.editGroupModal.namespaceInput.query()).not.toBeInTheDocument()); @@ -572,12 +575,12 @@ describe('RuleList', () => { testCase('rename just the lotex group', async () => { // make changes to form - userEvent.clear(ui.editGroupModal.ruleGroupInput.get()); - userEvent.type(ui.editGroupModal.ruleGroupInput.get(), 'super group'); - userEvent.type(ui.editGroupModal.intervalInput.get(), '5m'); + await userEvent.clear(ui.editGroupModal.ruleGroupInput.get()); + await userEvent.type(ui.editGroupModal.ruleGroupInput.get(), 'super group'); + await userEvent.type(ui.editGroupModal.intervalInput.get(), '5m'); // submit, check that appropriate calls were made - userEvent.click(ui.editGroupModal.saveButton.get()); + await userEvent.click(ui.editGroupModal.saveButton.get()); await waitFor(() => expect(ui.editGroupModal.namespaceInput.query()).not.toBeInTheDocument()); @@ -604,10 +607,10 @@ describe('RuleList', () => { testCase('edit lotex group eval interval, no renaming', async () => { // make changes to form - userEvent.type(ui.editGroupModal.intervalInput.get(), '5m'); + await userEvent.type(ui.editGroupModal.intervalInput.get(), '5m'); // submit, check that appropriate calls were made - userEvent.click(ui.editGroupModal.saveButton.get()); + await userEvent.click(ui.editGroupModal.saveButton.get()); await waitFor(() => expect(ui.editGroupModal.namespaceInput.query()).not.toBeInTheDocument()); diff --git a/public/app/features/alerting/unified/Silences.test.tsx b/public/app/features/alerting/unified/Silences.test.tsx index 13d91f7449e..1b3b01bd9b1 100644 --- a/public/app/features/alerting/unified/Silences.test.tsx +++ b/public/app/features/alerting/unified/Silences.test.tsx @@ -12,7 +12,7 @@ import { DataSourceType } from './utils/datasource'; import { parseMatchers } from './utils/alertmanager'; import { AlertState, MatcherOperator } from 'app/plugins/datasource/alertmanager/types'; import { byLabelText, byPlaceholderText, byRole, byTestId, byText } from 'testing-library-selector'; -import userEvent from '@testing-library/user-event'; +import userEvent, { PointerEventsCheckLevel } from '@testing-library/user-event'; import { contextSrv } from 'app/core/services/context_srv'; import { AccessControlAction } from 'app/types'; @@ -171,7 +171,8 @@ describe('Silences', () => { await waitFor(() => expect(mocks.api.fetchAlerts).toHaveBeenCalled()); const queryBar = ui.queryBar.get(); - userEvent.paste(queryBar, 'foo=bar'); + await userEvent.click(queryBar); + await userEvent.paste('foo=bar'); await waitFor(() => expect(ui.silenceRow.getAll()).toHaveLength(1)); }, @@ -254,40 +255,40 @@ describe('Silence edit', () => { const startDateString = dateTime(start).format('YYYY-MM-DD'); const endDateString = dateTime(end).format('YYYY-MM-DD'); - userEvent.clear(ui.editor.durationInput.get()); - userEvent.type(ui.editor.durationInput.get(), '1d'); + await userEvent.clear(ui.editor.durationInput.get()); + await userEvent.type(ui.editor.durationInput.get(), '1d'); await waitFor(() => expect(ui.editor.durationInput.query()).toHaveValue('1d')); await waitFor(() => expect(ui.editor.timeRange.get()).toHaveTextContent(startDateString)); await waitFor(() => expect(ui.editor.timeRange.get()).toHaveTextContent(endDateString)); - userEvent.type(ui.editor.matcherName.get(), 'foo'); - userEvent.type(ui.editor.matcherOperatorSelect.get(), '='); - userEvent.tab(); - userEvent.type(ui.editor.matcherValue.get(), 'bar'); + await userEvent.type(ui.editor.matcherName.get(), 'foo'); + await userEvent.type(ui.editor.matcherOperatorSelect.get(), '='); + await userEvent.tab(); + await userEvent.type(ui.editor.matcherValue.get(), 'bar'); // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed - userEvent.click(ui.editor.addMatcherButton.get(), undefined, { skipPointerEventsCheck: true }); - userEvent.type(ui.editor.matcherName.getAll()[1], 'bar'); - userEvent.type(ui.editor.matcherOperatorSelect.getAll()[1], '!='); - userEvent.tab(); - userEvent.type(ui.editor.matcherValue.getAll()[1], 'buzz'); + await userEvent.click(ui.editor.addMatcherButton.get(), { pointerEventsCheck: PointerEventsCheckLevel.Never }); + await userEvent.type(ui.editor.matcherName.getAll()[1], 'bar'); + await userEvent.type(ui.editor.matcherOperatorSelect.getAll()[1], '!='); + await userEvent.tab(); + await userEvent.type(ui.editor.matcherValue.getAll()[1], 'buzz'); // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed - userEvent.click(ui.editor.addMatcherButton.get(), undefined, { skipPointerEventsCheck: true }); - userEvent.type(ui.editor.matcherName.getAll()[2], 'region'); - userEvent.type(ui.editor.matcherOperatorSelect.getAll()[2], '=~'); - userEvent.tab(); - userEvent.type(ui.editor.matcherValue.getAll()[2], 'us-west-.*'); + await userEvent.click(ui.editor.addMatcherButton.get(), { pointerEventsCheck: PointerEventsCheckLevel.Never }); + await userEvent.type(ui.editor.matcherName.getAll()[2], 'region'); + await userEvent.type(ui.editor.matcherOperatorSelect.getAll()[2], '=~'); + await userEvent.tab(); + await userEvent.type(ui.editor.matcherValue.getAll()[2], 'us-west-.*'); // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed - userEvent.click(ui.editor.addMatcherButton.get(), undefined, { skipPointerEventsCheck: true }); - userEvent.type(ui.editor.matcherName.getAll()[3], 'env'); - userEvent.type(ui.editor.matcherOperatorSelect.getAll()[3], '!~'); - userEvent.tab(); - userEvent.type(ui.editor.matcherValue.getAll()[3], 'dev|staging'); + await userEvent.click(ui.editor.addMatcherButton.get(), { pointerEventsCheck: PointerEventsCheckLevel.Never }); + await userEvent.type(ui.editor.matcherName.getAll()[3], 'env'); + await userEvent.type(ui.editor.matcherOperatorSelect.getAll()[3], '!~'); + await userEvent.tab(); + await userEvent.type(ui.editor.matcherValue.getAll()[3], 'dev|staging'); - userEvent.click(ui.editor.submit.get()); + await userEvent.click(ui.editor.submit.get()); await waitFor(() => expect(mocks.api.createOrUpdateSilence).toHaveBeenCalledWith( diff --git a/public/app/features/alerting/unified/components/admin/AlertmanagerConfig.test.tsx b/public/app/features/alerting/unified/components/admin/AlertmanagerConfig.test.tsx index a4dbf55a55b..ed43711beaa 100644 --- a/public/app/features/alerting/unified/components/admin/AlertmanagerConfig.test.tsx +++ b/public/app/features/alerting/unified/components/admin/AlertmanagerConfig.test.tsx @@ -104,8 +104,8 @@ describe('Admin config', () => { await renderAdminPage(dataSources.alertManager.name); - userEvent.click(await ui.resetButton.find()); - userEvent.click(ui.confirmButton.get()); + await userEvent.click(await ui.resetButton.find()); + await userEvent.click(ui.confirmButton.get()); await waitFor(() => expect(mocks.api.deleteAlertManagerConfig).toHaveBeenCalled()); expect(ui.confirmButton.query()).not.toBeInTheDocument(); }); @@ -132,12 +132,12 @@ describe('Admin config', () => { await renderAdminPage(dataSources.alertManager.name); const input = await ui.configInput.find(); expect(input.value).toEqual(JSON.stringify(defaultConfig, null, 2)); - userEvent.clear(input); + await userEvent.clear(input); // What is this regex replace doing? in userEvent v13, '{' and '[' are special characters. // To get the literal character, you have to escape them by typing '{{' or '[['. // See https://github.com/testing-library/user-event/issues/584. - userEvent.type(input, JSON.stringify(newConfig, null, 2).replace(/[{[]/g, '$&$&')); - userEvent.click(ui.saveButton.get()); + await userEvent.type(input, JSON.stringify(newConfig, null, 2).replace(/[{[]/g, '$&$&')); + await userEvent.click(ui.saveButton.get()); await waitFor(() => expect(mocks.api.updateAlertManagerConfig).toHaveBeenCalled()); await waitFor(() => expect(mocks.api.fetchConfig).toHaveBeenCalledTimes(3)); expect(input.value).toEqual(JSON.stringify(newConfig, null, 2)); diff --git a/public/app/features/alerting/unified/components/rules/RulesGroup.test.tsx b/public/app/features/alerting/unified/components/rules/RulesGroup.test.tsx index a9eafa81ed3..79bde001d75 100644 --- a/public/app/features/alerting/unified/components/rules/RulesGroup.test.tsx +++ b/public/app/features/alerting/unified/components/rules/RulesGroup.test.tsx @@ -95,13 +95,13 @@ describe('Rules group tests', () => { expect(ui.editGroupButton.query()).not.toBeInTheDocument(); }); - it('Delete button click should display confirmation modal', () => { + it('Delete button click should display confirmation modal', async () => { // Arrange hasRulerMock.mockReturnValue(true); // Act renderRulesGroup(namespace, group); - userEvent.click(ui.deleteGroupButton.get()); + await userEvent.click(ui.deleteGroupButton.get()); // Assert expect(ui.confirmDeleteModal.header.get()).toBeInTheDocument(); diff --git a/public/app/features/api-keys/ApiKeysPage.test.tsx b/public/app/features/api-keys/ApiKeysPage.test.tsx index 4b33ab79096..eb97929c5a3 100644 --- a/public/app/features/api-keys/ApiKeysPage.test.tsx +++ b/public/app/features/api-keys/ApiKeysPage.test.tsx @@ -7,7 +7,7 @@ import { setSearchQuery } from './state/reducers'; import { mockToolkitActionCreator } from '../../../test/core/redux/mocks'; import { getMultipleMockKeys } from './__mocks__/apiKeysMock'; import { selectors } from '@grafana/e2e-selectors'; -import userEvent from '@testing-library/user-event'; +import userEvent, { PointerEventsCheckLevel } from '@testing-library/user-event'; import { silenceConsoleOutput } from '../../../test/core/utils/silenceConsoleOutput'; const setup = (propOverrides: Partial) => { @@ -100,7 +100,7 @@ describe('ApiKeysPage', () => { const apiKeys = getMultipleMockKeys(3); const { toggleIncludeExpiredMock } = setup({ apiKeys, apiKeysCount: apiKeys.length, hasFetched: true }); - toggleShowExpired(); + await toggleShowExpired(); expect(toggleIncludeExpiredMock).toHaveBeenCalledTimes(1); }); }); @@ -112,7 +112,7 @@ describe('ApiKeysPage', () => { setSearchQueryMock.mockClear(); expect(screen.getByPlaceholderText(/search keys/i)).toBeInTheDocument(); - userEvent.type(screen.getByPlaceholderText(/search keys/i), 'First'); + await userEvent.type(screen.getByPlaceholderText(/search keys/i), 'First'); expect(setSearchQueryMock).toHaveBeenCalledTimes(5); }); }); @@ -130,21 +130,21 @@ describe('ApiKeysPage', () => { deleteApiKeyMock.mockClear(); expect(within(firstRow).getByLabelText('Delete API key')).toBeInTheDocument(); - userEvent.click(within(firstRow).getByLabelText('Delete API key')); + await userEvent.click(within(firstRow).getByLabelText('Delete API key')); expect(within(firstRow).getByRole('button', { name: /delete$/i })).toBeInTheDocument(); - userEvent.click(within(firstRow).getByRole('button', { name: /delete$/i })); + await userEvent.click(within(firstRow).getByRole('button', { name: /delete$/i })); expect(deleteApiKeyMock).toHaveBeenCalledTimes(1); expect(deleteApiKeyMock).toHaveBeenCalledWith(1); - toggleShowExpired(); + await toggleShowExpired(); deleteApiKeyMock.mockClear(); expect(within(secondRow).getByLabelText('Delete API key')).toBeInTheDocument(); - userEvent.click(within(secondRow).getByLabelText('Delete API key')); + await userEvent.click(within(secondRow).getByLabelText('Delete API key')); expect(within(secondRow).getByRole('button', { name: /delete$/i })).toBeInTheDocument(); - userEvent.click(within(secondRow).getByRole('button', { name: /delete$/i }), undefined, { - skipPointerEventsCheck: true, + await userEvent.click(within(secondRow).getByRole('button', { name: /delete$/i }), { + pointerEventsCheck: PointerEventsCheckLevel.Never, }); expect(deleteApiKeyMock).toHaveBeenCalledTimes(1); expect(deleteApiKeyMock).toHaveBeenCalledWith(2); @@ -157,7 +157,7 @@ describe('ApiKeysPage', () => { const { addApiKeyMock } = setup({ apiKeys, apiKeysCount: apiKeys.length, hasFetched: true }); addApiKeyMock.mockClear(); - userEvent.click(screen.getByTestId(selectors.components.CallToActionCard.buttonV2('New API key'))); + await userEvent.click(screen.getByTestId(selectors.components.CallToActionCard.buttonV2('New API key'))); await addAndVerifyApiKey(addApiKeyMock); }); }); @@ -168,13 +168,13 @@ describe('ApiKeysPage', () => { const { addApiKeyMock } = setup({ apiKeys, apiKeysCount: apiKeys.length, hasFetched: true }); addApiKeyMock.mockClear(); - userEvent.click(screen.getByRole('button', { name: /add api key/i })); + await userEvent.click(screen.getByRole('button', { name: /add api key/i })); await addAndVerifyApiKey(addApiKeyMock); - toggleShowExpired(); + await toggleShowExpired(); addApiKeyMock.mockClear(); - userEvent.click(screen.getByRole('button', { name: /add api key/i })); + await userEvent.click(screen.getByRole('button', { name: /add api key/i })); await addAndVerifyApiKey(addApiKeyMock); }); }); @@ -185,20 +185,20 @@ describe('ApiKeysPage', () => { const { addApiKeyMock } = setup({ apiKeys, apiKeysCount: apiKeys.length, hasFetched: true }); addApiKeyMock.mockClear(); - userEvent.click(screen.getByRole('button', { name: /add api key/i })); - userEvent.type(screen.getByPlaceholderText(/name/i), 'Test'); - userEvent.type(screen.getByPlaceholderText(/1d/i), '60x'); + await userEvent.click(screen.getByRole('button', { name: /add api key/i })); + await userEvent.type(screen.getByPlaceholderText(/name/i), 'Test'); + await userEvent.type(screen.getByPlaceholderText(/1d/i), '60x'); expect(screen.queryByText(/not a valid duration/i)).not.toBeInTheDocument(); - userEvent.click(screen.getByRole('button', { name: /^add$/i })); + await userEvent.click(screen.getByRole('button', { name: /^add$/i })); expect(screen.getByText(/not a valid duration/i)).toBeInTheDocument(); expect(addApiKeyMock).toHaveBeenCalledTimes(0); }); }); }); -function toggleShowExpired() { +async function toggleShowExpired() { expect(screen.queryByLabelText(/include expired keys/i)).toBeInTheDocument(); - userEvent.click(screen.getByLabelText(/include expired keys/i)); + await userEvent.click(screen.getByLabelText(/include expired keys/i)); } async function addAndVerifyApiKey(addApiKeyMock: jest.Mock) { @@ -207,9 +207,9 @@ async function addAndVerifyApiKey(addApiKeyMock: jest.Mock) { expect(screen.getByPlaceholderText(/1d/i)).toBeInTheDocument(); expect(screen.getByRole('button', { name: /^add$/i })).toBeInTheDocument(); - userEvent.type(screen.getByPlaceholderText(/name/i), 'Test'); - userEvent.type(screen.getByPlaceholderText(/1d/i), '60s'); - userEvent.click(screen.getByRole('button', { name: /^add$/i })); + await userEvent.type(screen.getByPlaceholderText(/name/i), 'Test'); + await userEvent.type(screen.getByPlaceholderText(/1d/i), '60s'); + await userEvent.click(screen.getByRole('button', { name: /^add$/i })); expect(addApiKeyMock).toHaveBeenCalledTimes(1); expect(addApiKeyMock).toHaveBeenCalledWith({ name: 'Test', role: 'Viewer', secondsToLive: 60 }, expect.anything()); } diff --git a/public/app/features/dashboard/components/DashboardSettings/AnnotationsSettings.test.tsx b/public/app/features/dashboard/components/DashboardSettings/AnnotationsSettings.test.tsx index 07c3d3d2b76..bdd4a660c37 100644 --- a/public/app/features/dashboard/components/DashboardSettings/AnnotationsSettings.test.tsx +++ b/public/app/features/dashboard/components/DashboardSettings/AnnotationsSettings.test.tsx @@ -76,7 +76,7 @@ describe('AnnotationsSettings', () => { }; }); - test('it renders a header and cta if no annotations or only builtIn annotation', () => { + test('it renders a header and cta if no annotations or only builtIn annotation', async () => { render(); expect(screen.getByRole('heading', { name: /annotations/i })).toBeInTheDocument(); @@ -87,7 +87,7 @@ describe('AnnotationsSettings', () => { ).toBeInTheDocument(); expect(screen.queryByRole('link', { name: /annotations documentation/i })).toBeInTheDocument(); - userEvent.click(screen.getByRole('cell', { name: /annotations & alerts \(built\-in\)/i })); + await userEvent.click(screen.getByRole('cell', { name: /annotations & alerts \(built\-in\)/i })); const heading = screen.getByRole('heading', { name: /annotations edit/i, @@ -96,13 +96,13 @@ describe('AnnotationsSettings', () => { expect(heading).toBeInTheDocument(); - userEvent.clear(nameInput); - userEvent.type(nameInput, 'My Annotation'); + await userEvent.clear(nameInput); + await userEvent.type(nameInput, 'My Annotation'); expect(screen.queryByText(/grafana/i)).toBeInTheDocument(); expect(screen.getByRole('checkbox', { name: /hidden/i })).toBeChecked(); - userEvent.click(within(heading).getByText(/annotations/i)); + await userEvent.click(within(heading).getByText(/annotations/i)); expect(screen.getByRole('table')).toBeInTheDocument(); expect(screen.getByRole('row', { name: /my annotation \(built\-in\) grafana/i })).toBeInTheDocument(); @@ -111,8 +111,8 @@ describe('AnnotationsSettings', () => { ).toBeInTheDocument(); expect(screen.queryByRole('button', { name: /new query/i })).not.toBeInTheDocument(); - userEvent.click(screen.getAllByLabelText(/Delete query with title/)[0]); - userEvent.click(screen.getByRole('button', { name: 'Delete' })); + await userEvent.click(screen.getAllByLabelText(/Delete query with title/)[0]); + await userEvent.click(screen.getByRole('button', { name: 'Delete' })); expect(screen.queryAllByRole('row').length).toBe(0); expect( @@ -120,7 +120,7 @@ describe('AnnotationsSettings', () => { ).toBeInTheDocument(); }); - test('it renders a sortable table of annotations', () => { + test('it renders a sortable table of annotations', async () => { const annotationsList = [ ...dashboard.annotations.list, { @@ -164,9 +164,9 @@ describe('AnnotationsSettings', () => { expect(within(getTableBodyRows()[1]).queryByText(/annotation 2/i)).toBeInTheDocument(); expect(within(getTableBodyRows()[2]).queryByText(/annotation 3/i)).toBeInTheDocument(); - userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-down' })[0]); - userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-down' })[1]); - userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-up' })[0]); + await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-down' })[0]); + await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-down' })[1]); + await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-up' })[0]); // Checking if it has changed the sorting accordingly expect(within(getTableBodyRows()[0]).queryByText(/annotation 3/i)).toBeInTheDocument(); @@ -177,7 +177,7 @@ describe('AnnotationsSettings', () => { test('it renders a form for adding/editing annotations', async () => { render(); - userEvent.click(screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query'))); + await userEvent.click(screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query'))); const heading = screen.getByRole('heading', { name: /annotations edit/i, @@ -186,19 +186,19 @@ describe('AnnotationsSettings', () => { expect(heading).toBeInTheDocument(); - userEvent.clear(nameInput); - userEvent.type(nameInput, 'My Prometheus Annotation'); + await userEvent.clear(nameInput); + await userEvent.type(nameInput, 'My Prometheus Annotation'); - userEvent.click(screen.getByText(/testdata/i)); + await userEvent.click(screen.getByText(/testdata/i)); expect(await screen.findByText(/Prometheus/i)).toBeVisible(); expect(screen.queryAllByText(/testdata/i)).toHaveLength(2); - userEvent.click(screen.getByText(/prometheus/i)); + await userEvent.click(screen.getByText(/prometheus/i)); expect(screen.getByRole('checkbox', { name: /hidden/i })).not.toBeChecked(); - userEvent.click(within(heading).getByText(/annotations/i)); + await userEvent.click(within(heading).getByText(/annotations/i)); expect(within(screen.getAllByRole('rowgroup')[1]).getAllByRole('row').length).toBe(2); expect(screen.queryByRole('row', { name: /my prometheus annotation prometheus/i })).toBeInTheDocument(); @@ -207,14 +207,14 @@ describe('AnnotationsSettings', () => { screen.queryByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query')) ).not.toBeInTheDocument(); - userEvent.click(screen.getByRole('button', { name: /new query/i })); + await userEvent.click(screen.getByRole('button', { name: /new query/i })); - userEvent.click(within(screen.getByRole('heading', { name: /annotations edit/i })).getByText(/annotations/i)); + await userEvent.click(within(screen.getByRole('heading', { name: /annotations edit/i })).getByText(/annotations/i)); expect(within(screen.getAllByRole('rowgroup')[1]).getAllByRole('row').length).toBe(3); - userEvent.click(screen.getAllByLabelText(/Delete query with title/)[0]); - userEvent.click(screen.getByRole('button', { name: 'Delete' })); + await userEvent.click(screen.getAllByLabelText(/Delete query with title/)[0]); + await userEvent.click(screen.getByRole('button', { name: 'Delete' })); expect(within(screen.getAllByRole('rowgroup')[1]).getAllByRole('row').length).toBe(2); }); diff --git a/public/app/features/dashboard/components/DashboardSettings/AutoRefreshIntervals.test.tsx b/public/app/features/dashboard/components/DashboardSettings/AutoRefreshIntervals.test.tsx index 948dfd93c15..da52654bfb6 100644 --- a/public/app/features/dashboard/components/DashboardSettings/AutoRefreshIntervals.test.tsx +++ b/public/app/features/dashboard/components/DashboardSettings/AutoRefreshIntervals.test.tsx @@ -49,11 +49,11 @@ describe('AutoRefreshIntervals', () => { }); describe('when input loses focus and intervals are valid', () => { - it('then onRefreshIntervalChange should be called', () => { + it('then onRefreshIntervalChange should be called', async () => { const { props } = setupTestContext({ validateIntervalsFunc: () => null }); - userEvent.type(screen.getByRole('textbox'), ',30s'); - userEvent.tab(); + await userEvent.type(screen.getByRole('textbox'), ',30s'); + await userEvent.tab(); expect(screen.getByRole('textbox')).toHaveValue('1s,5s,10s,30s'); expect(props.onRefreshIntervalChange).toHaveBeenCalledTimes(1); @@ -62,11 +62,11 @@ describe('AutoRefreshIntervals', () => { }); describe('when input loses focus and intervals are invalid', () => { - it('then onRefreshIntervalChange should not be called', () => { + it('then onRefreshIntervalChange should not be called', async () => { const { props } = setupTestContext({ validateIntervalsFunc: () => 'Not valid' }); - userEvent.type(screen.getByRole('textbox'), ',30q'); - userEvent.tab(); + await userEvent.type(screen.getByRole('textbox'), ',30q'); + await userEvent.tab(); expect(screen.getByRole('textbox')).toHaveValue('1s,5s,10s,30q'); expect(props.onRefreshIntervalChange).toHaveBeenCalledTimes(0); @@ -74,14 +74,14 @@ describe('AutoRefreshIntervals', () => { }); describe('when input loses focus and previous intervals were invalid', () => { - it('then onRefreshIntervalChange should be called', () => { + it('then onRefreshIntervalChange should be called', async () => { const validateIntervalsFunc = jest.fn().mockReturnValueOnce('Not valid').mockReturnValue(null); const { props } = setupTestContext({ validateIntervalsFunc }); - userEvent.type(screen.getByRole('textbox'), ',30q'); - userEvent.tab(); - userEvent.type(screen.getByRole('textbox'), '{backspace}s'); - userEvent.tab(); + await userEvent.type(screen.getByRole('textbox'), ',30q'); + await userEvent.tab(); + await userEvent.type(screen.getByRole('textbox'), '{backspace}s'); + await userEvent.tab(); expect(screen.getByRole('textbox')).toHaveValue('1s,5s,10s,30s'); expect(props.onRefreshIntervalChange).toHaveBeenCalledTimes(1); diff --git a/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.test.tsx b/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.test.tsx index dbec19a2961..97bf20e1f50 100644 --- a/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.test.tsx +++ b/public/app/features/dashboard/components/DashboardSettings/GeneralSettings.test.tsx @@ -48,9 +48,9 @@ describe('General Settings', () => { describe('when timezone is changed', () => { it('should call update function', async () => { const { props } = setupTestContext({}); - userEvent.click(screen.getByTestId(selectors.components.TimeZonePicker.containerV2)); + await userEvent.click(screen.getByTestId(selectors.components.TimeZonePicker.containerV2)); const timeZonePicker = screen.getByTestId(selectors.components.TimeZonePicker.containerV2); - userEvent.click(byRole('combobox').get(timeZonePicker)); + await userEvent.click(byRole('combobox').get(timeZonePicker)); await selectOptionInTest(timeZonePicker, 'Browser Time'); expect(props.updateTimeZone).toHaveBeenCalledWith('browser'); expect(props.dashboard.timezone).toBe('browser'); diff --git a/public/app/features/dashboard/components/DashboardSettings/LinksSettings.test.tsx b/public/app/features/dashboard/components/DashboardSettings/LinksSettings.test.tsx index a2646974940..db38118db3a 100644 --- a/public/app/features/dashboard/components/DashboardSettings/LinksSettings.test.tsx +++ b/public/app/features/dashboard/components/DashboardSettings/LinksSettings.test.tsx @@ -84,7 +84,7 @@ describe('LinksSettings', () => { ).not.toBeInTheDocument(); }); - test('it rearranges the order of dashboard links', () => { + test('it rearranges the order of dashboard links', async () => { // @ts-ignore render(); @@ -103,9 +103,9 @@ describe('LinksSettings', () => { assertRowHasText(1, links[1].title); assertRowHasText(2, links[2].url); - userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-down' })[0]); - userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-down' })[1]); - userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-up' })[0]); + await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-down' })[0]); + await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-down' })[1]); + await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-up' })[0]); // Checking if it has changed the sorting accordingly assertRowHasText(0, links[2].url); @@ -113,35 +113,35 @@ describe('LinksSettings', () => { assertRowHasText(2, links[0].title); }); - test('it duplicates dashboard links', () => { + test('it duplicates dashboard links', async () => { // @ts-ignore render(); expect(getTableBodyRows().length).toBe(links.length); - userEvent.click(within(getTableBody()).getAllByRole('button', { name: /copy/i })[0]); + await userEvent.click(within(getTableBody()).getAllByRole('button', { name: /copy/i })[0]); expect(getTableBodyRows().length).toBe(links.length + 1); expect(within(getTableBody()).getAllByText(links[0].title).length).toBe(2); }); - test('it deletes dashboard links', () => { + test('it deletes dashboard links', async () => { // @ts-ignore render(); expect(getTableBodyRows().length).toBe(links.length); - userEvent.click(within(getTableBody()).getAllByLabelText(/Delete link with title/)[0]); - userEvent.click(within(getTableBody()).getByRole('button', { name: 'Delete' })); + await userEvent.click(within(getTableBody()).getAllByLabelText(/Delete link with title/)[0]); + await userEvent.click(within(getTableBody()).getByRole('button', { name: 'Delete' })); expect(getTableBodyRows().length).toBe(links.length - 1); expect(within(getTableBody()).queryByText(links[0].title)).not.toBeInTheDocument(); }); - test('it renders a form which modifies dashboard links', () => { + test('it renders a form which modifies dashboard links', async () => { // @ts-ignore render(); - userEvent.click(screen.getByRole('button', { name: /new/i })); + await userEvent.click(screen.getByRole('button', { name: /new/i })); expect(screen.queryByText('Type')).toBeInTheDocument(); expect(screen.queryByText('Title')).toBeInTheDocument(); @@ -151,29 +151,29 @@ describe('LinksSettings', () => { expect(screen.queryByText('Tooltip')).not.toBeInTheDocument(); expect(screen.queryByText('Icon')).not.toBeInTheDocument(); - userEvent.click(screen.getByText('Dashboards')); + await userEvent.click(screen.getByText('Dashboards')); expect(screen.queryAllByText('Dashboards')).toHaveLength(2); expect(screen.queryByText('Link')).toBeVisible(); - userEvent.click(screen.getByText('Link')); + await userEvent.click(screen.getByText('Link')); expect(screen.queryByText('URL')).toBeInTheDocument(); expect(screen.queryByText('Tooltip')).toBeInTheDocument(); expect(screen.queryByText('Icon')).toBeInTheDocument(); - userEvent.clear(screen.getByRole('textbox', { name: /title/i })); - userEvent.type(screen.getByRole('textbox', { name: /title/i }), 'New Dashboard Link'); - userEvent.click( + await userEvent.clear(screen.getByRole('textbox', { name: /title/i })); + await userEvent.type(screen.getByRole('textbox', { name: /title/i }), 'New Dashboard Link'); + await userEvent.click( within(screen.getByRole('heading', { name: /dashboard links edit/i })).getByText(/dashboard links/i) ); expect(getTableBodyRows().length).toBe(links.length + 1); expect(within(getTableBody()).queryByText('New Dashboard Link')).toBeInTheDocument(); - userEvent.click(screen.getAllByText(links[0].type)[0]); - userEvent.clear(screen.getByRole('textbox', { name: /title/i })); - userEvent.type(screen.getByRole('textbox', { name: /title/i }), 'The first dashboard link'); - userEvent.click( + await userEvent.click(screen.getAllByText(links[0].type)[0]); + await userEvent.clear(screen.getByRole('textbox', { name: /title/i })); + await userEvent.type(screen.getByRole('textbox', { name: /title/i }), 'The first dashboard link'); + await userEvent.click( within(screen.getByRole('heading', { name: /dashboard links edit/i })).getByText(/dashboard links/i) ); diff --git a/public/app/features/dashboard/components/DashboardSettings/VersionsSettings.test.tsx b/public/app/features/dashboard/components/DashboardSettings/VersionsSettings.test.tsx index 00788bbe65d..2c312ec181c 100644 --- a/public/app/features/dashboard/components/DashboardSettings/VersionsSettings.test.tsx +++ b/public/app/features/dashboard/components/DashboardSettings/VersionsSettings.test.tsx @@ -6,6 +6,7 @@ import userEvent from '@testing-library/user-event'; import { historySrv } from '../VersionHistory/HistorySrv'; import { VersionsSettings, VERSIONS_FETCH_LIMIT } from './VersionsSettings'; import { versions, diffs } from './__mocks__/versions'; +import { UserEvent } from '@testing-library/user-event/dist/types/setup'; jest.mock('../VersionHistory/HistorySrv'); @@ -28,8 +29,18 @@ describe('VersionSettings', () => { getRelativeTime: jest.fn(() => 'time ago'), }; + let user: UserEvent; + beforeEach(() => { + // Need to use delay: null here to work with fakeTimers + // see https://github.com/testing-library/user-event/issues/833 + user = userEvent.setup({ delay: null }); jest.resetAllMocks(); + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); }); test('renders a header and a loading indicator followed by results in a table', async () => { @@ -102,7 +113,9 @@ describe('VersionSettings', () => { historySrv.getHistoryList // @ts-ignore .mockImplementationOnce(() => Promise.resolve(versions.slice(0, VERSIONS_FETCH_LIMIT))) - .mockImplementationOnce(() => Promise.resolve(versions.slice(VERSIONS_FETCH_LIMIT, versions.length))); + .mockImplementationOnce( + () => new Promise((resolve) => setTimeout(() => resolve(versions.slice(VERSIONS_FETCH_LIMIT)), 1000)) + ); render(); @@ -113,14 +126,16 @@ describe('VersionSettings', () => { expect(within(screen.getAllByRole('rowgroup')[1]).getAllByRole('row').length).toBe(VERSIONS_FETCH_LIMIT); const showMoreButton = screen.getByRole('button', { name: /show more versions/i }); - userEvent.click(showMoreButton); + await user.click(showMoreButton); expect(historySrv.getHistoryList).toBeCalledTimes(2); - expect(screen.queryByText(/Fetching more entries/i)).toBeInTheDocument(); + expect(screen.getByText(/Fetching more entries/i)).toBeInTheDocument(); + jest.advanceTimersByTime(1000); - await waitFor(() => - expect(within(screen.getAllByRole('rowgroup')[1]).getAllByRole('row').length).toBe(versions.length) - ); + await waitFor(() => { + expect(screen.queryByText(/Fetching more entries/i)).not.toBeInTheDocument(); + expect(within(screen.getAllByRole('rowgroup')[1]).getAllByRole('row').length).toBe(versions.length); + }); }); test('selecting two versions and clicking compare button should render compare view', async () => { @@ -139,17 +154,17 @@ describe('VersionSettings', () => { const compareButton = screen.getByRole('button', { name: /compare versions/i }); const tableBody = screen.getAllByRole('rowgroup')[1]; - userEvent.click(within(tableBody).getAllByRole('checkbox')[0]); - userEvent.click(within(tableBody).getAllByRole('checkbox')[VERSIONS_FETCH_LIMIT - 1]); + await user.click(within(tableBody).getAllByRole('checkbox')[0]); + await user.click(within(tableBody).getAllByRole('checkbox')[VERSIONS_FETCH_LIMIT - 1]); expect(compareButton).toBeEnabled(); - userEvent.click(within(tableBody).getAllByRole('checkbox')[1]); + await user.click(within(tableBody).getAllByRole('checkbox')[1]); expect(compareButton).toBeDisabled(); - userEvent.click(within(tableBody).getAllByRole('checkbox')[1]); - userEvent.click(compareButton); + await user.click(within(tableBody).getAllByRole('checkbox')[1]); + await user.click(compareButton); await waitFor(() => expect(screen.getByRole('heading', { name: /versions comparing 2 11/i })).toBeInTheDocument()); @@ -170,7 +185,7 @@ describe('VersionSettings', () => { expect(queryByFullText('version changed')).toBeInTheDocument(); expect(screen.queryByText(/view json diff/i)).toBeInTheDocument(); - userEvent.click(screen.getByText(/view json diff/i)); + await user.click(screen.getByText(/view json diff/i)); await waitFor(() => expect(screen.getByRole('table')).toBeInTheDocument()); }); diff --git a/public/app/features/dashboard/components/PanelEditor/PanelNotSupported.test.tsx b/public/app/features/dashboard/components/PanelEditor/PanelNotSupported.test.tsx index 3140999328a..7c61a20373d 100644 --- a/public/app/features/dashboard/components/PanelEditor/PanelNotSupported.test.tsx +++ b/public/app/features/dashboard/components/PanelEditor/PanelNotSupported.test.tsx @@ -28,9 +28,9 @@ describe('PanelNotSupported', () => { }); describe('when the back to queries button is clicked', () => { - it('then correct action should be dispatched', () => { + it('then correct action should be dispatched', async () => { setupTestContext({}); - userEvent.click(screen.getByRole('button', { name: /go back to queries/i })); + await userEvent.click(screen.getByRole('button', { name: /go back to queries/i })); expect(locationService.getSearchObject().tab).toBe(PanelEditorTabId.Query); }); }); diff --git a/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.test.tsx b/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.test.tsx index da6ff1462fd..617d4f9f86b 100644 --- a/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.test.tsx +++ b/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.test.tsx @@ -33,13 +33,13 @@ describe('TransformationsEditor', () => { options: {}, }, ]); - const editors = screen.getAllByLabelText(/^Transformation editor/g); + const editors = screen.getAllByLabelText(/^Transformation editor/); expect(editors).toHaveLength(1); }); }); describe('when Add transformation clicked', () => { - it('renders transformations picker', () => { + it('renders transformations picker', async () => { const buttonLabel = 'Add transformation'; setup([ { @@ -49,7 +49,7 @@ describe('TransformationsEditor', () => { ]); const addTransformationButton = screen.getByText(buttonLabel); - userEvent.click(addTransformationButton); + await userEvent.click(addTransformationButton); const search = screen.getByLabelText(selectors.components.Transforms.searchInput); expect(search).toBeDefined(); @@ -58,7 +58,7 @@ describe('TransformationsEditor', () => { describe('actions', () => { describe('debug', () => { - it('should show/hide debugger', () => { + it('should show/hide debugger', async () => { setup([ { id: 'reduce', @@ -70,7 +70,7 @@ describe('TransformationsEditor', () => { expect(screen.queryByLabelText(debuggerSelector)).toBeNull(); const debugButton = screen.getByLabelText(selectors.components.QueryEditorRow.actionButton('Debug')); - userEvent.click(debugButton); + await userEvent.click(debugButton); expect(screen.getByLabelText(debuggerSelector)).toBeInTheDocument(); }); diff --git a/public/app/features/explore/AddToDashboard/index.test.tsx b/public/app/features/explore/AddToDashboard/index.test.tsx index fe265e71545..e3da2cf30e7 100644 --- a/public/app/features/explore/AddToDashboard/index.test.tsx +++ b/public/app/features/explore/AddToDashboard/index.test.tsx @@ -28,7 +28,7 @@ const setup = (children: ReactNode, queries: DataQuery[] = [{ refId: 'A' }]) => }; const openModal = async () => { - userEvent.click(screen.getByRole('button', { name: /add to dashboard/i })); + await userEvent.click(screen.getByRole('button', { name: /add to dashboard/i })); expect(await screen.findByRole('dialog', { name: 'Add panel to dashboard' })).toBeInTheDocument(); }; @@ -44,7 +44,7 @@ describe('AddToDashboardButton', () => { const button = await screen.findByRole('button', { name: /add to dashboard/i }); expect(button).toBeDisabled(); - userEvent.click(button); + await userEvent.click(button); expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); }); @@ -71,7 +71,7 @@ describe('AddToDashboardButton', () => { await openModal(); - userEvent.click(screen.getByRole('button', { name: /cancel/i })); + await userEvent.click(screen.getByRole('button', { name: /cancel/i })); expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); }); @@ -86,7 +86,7 @@ describe('AddToDashboardButton', () => { await openModal(); - userEvent.click(screen.getByRole('button', { name: /open dashboard$/i })); + await userEvent.click(screen.getByRole('button', { name: /open dashboard$/i })); await waitForAddToDashboardResponse(); @@ -105,7 +105,7 @@ describe('AddToDashboardButton', () => { await openModal(); - userEvent.click(screen.getByRole('button', { name: /open in new tab/i })); + await userEvent.click(screen.getByRole('button', { name: /open in new tab/i })); await waitForAddToDashboardResponse(); @@ -124,7 +124,7 @@ describe('AddToDashboardButton', () => { await openModal(); - userEvent.click(screen.getByRole('button', { name: /open in new tab/i })); + await userEvent.click(screen.getByRole('button', { name: /open in new tab/i })); await waitForAddToDashboardResponse(); @@ -138,7 +138,7 @@ describe('AddToDashboardButton', () => { await openModal(); - userEvent.click(screen.getByRole('button', { name: /open dashboard$/i })); + await userEvent.click(screen.getByRole('button', { name: /open dashboard$/i })); await waitForAddToDashboardResponse(); @@ -157,7 +157,7 @@ describe('AddToDashboardButton', () => { expect(screen.queryByRole('combobox', { name: /dashboard/ })).not.toBeInTheDocument(); - userEvent.click(screen.getByRole('radio', { name: /existing dashboard/i })); + await userEvent.click(screen.getByRole('radio', { name: /existing dashboard/i })); expect(screen.getByRole('combobox', { name: /dashboard/ })).toBeInTheDocument(); }); @@ -168,9 +168,9 @@ describe('AddToDashboardButton', () => { await openModal(); - userEvent.click(screen.getByRole('radio', { name: /existing dashboard/i })); + await userEvent.click(screen.getByRole('radio', { name: /existing dashboard/i })); - userEvent.click(screen.getByRole('button', { name: /open dashboard$/i })); + await userEvent.click(screen.getByRole('button', { name: /open dashboard$/i })); await waitForAddToDashboardResponse(); expect(locationService.push).not.toHaveBeenCalled(); @@ -203,16 +203,16 @@ describe('AddToDashboardButton', () => { await openModal(); - userEvent.click(screen.getByRole('radio', { name: /existing dashboard/i })); + await userEvent.click(screen.getByRole('radio', { name: /existing dashboard/i })); - userEvent.click(screen.getByRole('combobox', { name: /dashboard/i })); + await userEvent.click(screen.getByRole('combobox', { name: /dashboard/i })); await waitFor(async () => { await screen.findByLabelText('Select option'); }); - userEvent.click(screen.getByLabelText('Select option')); + await userEvent.click(screen.getByLabelText('Select option')); - userEvent.click(screen.getByRole('button', { name: /open in new tab/i })); + await userEvent.click(screen.getByRole('button', { name: /open in new tab/i })); await waitFor(async () => { expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); @@ -246,16 +246,16 @@ describe('AddToDashboardButton', () => { await openModal(); - userEvent.click(screen.getByRole('radio', { name: /existing dashboard/i })); + await userEvent.click(screen.getByRole('radio', { name: /existing dashboard/i })); - userEvent.click(screen.getByRole('combobox', { name: /dashboard/i })); + await userEvent.click(screen.getByRole('combobox', { name: /dashboard/i })); await waitFor(async () => { await screen.findByLabelText('Select option'); }); - userEvent.click(screen.getByLabelText('Select option')); + await userEvent.click(screen.getByLabelText('Select option')); - userEvent.click(screen.getByRole('button', { name: /open dashboard$/i })); + await userEvent.click(screen.getByRole('button', { name: /open dashboard$/i })); await waitFor(async () => { expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); @@ -281,7 +281,7 @@ describe('AddToDashboardButton', () => { await openModal(); expect(screen.queryByRole('alert')).not.toBeInTheDocument(); - userEvent.click(screen.getByRole('button', { name: /open in new tab/i })); + await userEvent.click(screen.getByRole('button', { name: /open in new tab/i })); await waitFor(async () => { expect(await screen.findByRole('alert')).toBeInTheDocument(); @@ -300,7 +300,7 @@ describe('AddToDashboardButton', () => { await openModal(); expect(screen.queryByRole('alert')).not.toBeInTheDocument(); - userEvent.click(screen.getByRole('button', { name: /open in new tab/i })); + await userEvent.click(screen.getByRole('button', { name: /open in new tab/i })); await waitFor(async () => { expect(await screen.findByRole('alert')).toBeInTheDocument(); @@ -328,16 +328,16 @@ describe('AddToDashboardButton', () => { await openModal(); expect(screen.queryByRole('alert')).not.toBeInTheDocument(); - userEvent.click(screen.getByRole('radio', { name: /existing dashboard/i })); + await userEvent.click(screen.getByRole('radio', { name: /existing dashboard/i })); - userEvent.click(screen.getByRole('combobox', { name: /dashboard/i })); + await userEvent.click(screen.getByRole('combobox', { name: /dashboard/i })); await waitFor(async () => { await screen.findByLabelText('Select option'); }); - userEvent.click(screen.getByLabelText('Select option')); + await userEvent.click(screen.getByLabelText('Select option')); - userEvent.click(screen.getByRole('button', { name: /open in new tab/i })); + await userEvent.click(screen.getByRole('button', { name: /open in new tab/i })); await waitFor(async () => { expect(await screen.findByRole('alert')).toBeInTheDocument(); @@ -352,7 +352,7 @@ describe('AddToDashboardButton', () => { await openModal(); expect(screen.queryByRole('alert')).not.toBeInTheDocument(); - userEvent.click(screen.getByRole('button', { name: /open in new tab/i })); + await userEvent.click(screen.getByRole('button', { name: /open in new tab/i })); await waitFor(async () => { expect(await screen.findByRole('alert')).toBeInTheDocument(); diff --git a/public/app/features/explore/TraceView/TraceView.test.tsx b/public/app/features/explore/TraceView/TraceView.test.tsx index 240d90bcb40..82718889de6 100644 --- a/public/app/features/explore/TraceView/TraceView.test.tsx +++ b/public/app/features/explore/TraceView/TraceView.test.tsx @@ -92,10 +92,10 @@ describe('TraceView', () => { renderTraceViewNew(); expect(screen.queryByText(/Tags/)).toBeFalsy(); const spanView = screen.getAllByText('', { selector: 'div[data-test-id="span-view"]' })[0]; - userEvent.click(spanView); + await userEvent.click(spanView); expect(screen.queryByText(/Tags/)).toBeTruthy(); - userEvent.click(spanView); + await userEvent.click(spanView); screen.debug(screen.queryAllByText(/Tags/)); expect(screen.queryByText(/Tags/)).toBeFalsy(); }); @@ -109,36 +109,36 @@ describe('TraceView', () => { expect(ticks()).toBe('0μs274.5μs549μs823.5μs1.1ms'); }); - it('correctly shows processes for each span', () => { + it('correctly shows processes for each span', async () => { renderTraceView(); let table: HTMLElement; expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(3); const firstSpan = screen.getAllByText('', { selector: 'div[data-test-id="span-view"]' })[0]; - userEvent.click(firstSpan); - userEvent.click(screen.getByText(/Process/)); + await userEvent.click(firstSpan); + await userEvent.click(screen.getByText(/Process/)); table = screen.getByText('', { selector: 'div[data-test-id="KeyValueTable"]' }); expect(table.innerHTML).toContain('client-uuid-1'); - userEvent.click(firstSpan); + await userEvent.click(firstSpan); const secondSpan = screen.getAllByText('', { selector: 'div[data-test-id="span-view"]' })[1]; - userEvent.click(secondSpan); - userEvent.click(screen.getByText(/Process/)); + await userEvent.click(secondSpan); + await userEvent.click(screen.getByText(/Process/)); table = screen.getByText('', { selector: 'div[data-test-id="KeyValueTable"]' }); expect(table.innerHTML).toContain('client-uuid-2'); - userEvent.click(secondSpan); + await userEvent.click(secondSpan); const thirdSpan = screen.getAllByText('', { selector: 'div[data-test-id="span-view"]' })[2]; - userEvent.click(thirdSpan); - userEvent.click(screen.getByText(/Process/)); + await userEvent.click(thirdSpan); + await userEvent.click(screen.getByText(/Process/)); table = screen.getByText('', { selector: 'div[data-test-id="KeyValueTable"]' }); expect(table.innerHTML).toContain('client-uuid-3'); }); - it('resets detail view for new trace with the identical spanID', () => { + it('resets detail view for new trace with the identical spanID', async () => { const { rerender } = render(getTraceView([frameOld])); const span = screen.getAllByText('', { selector: 'div[data-test-id="span-view"]' })[2]; - userEvent.click(span); + await userEvent.click(span); //Process is in detail view expect(screen.getByText(/Process/)).toBeInTheDocument(); diff --git a/public/app/features/explore/TraceView/TraceViewContainer.test.tsx b/public/app/features/explore/TraceView/TraceViewContainer.test.tsx index 68725bb429b..f9ef0719237 100644 --- a/public/app/features/explore/TraceView/TraceViewContainer.test.tsx +++ b/public/app/features/explore/TraceView/TraceViewContainer.test.tsx @@ -35,75 +35,75 @@ function renderTraceViewContainer(frames = [frameOld]) { } describe('TraceViewContainer', () => { - it('toggles children visibility', () => { + it('toggles children visibility', async () => { renderTraceViewContainer(); expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(3); - userEvent.click(screen.getAllByText('', { selector: 'span[data-test-id="SpanTreeOffset--indentGuide"]' })[0]); + await userEvent.click(screen.getAllByText('', { selector: 'span[data-test-id="SpanTreeOffset--indentGuide"]' })[0]); expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(1); - userEvent.click(screen.getAllByText('', { selector: 'span[data-test-id="SpanTreeOffset--indentGuide"]' })[0]); + await userEvent.click(screen.getAllByText('', { selector: 'span[data-test-id="SpanTreeOffset--indentGuide"]' })[0]); expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(3); }); - it('toggles collapses and expands one level of spans', () => { + it('toggles collapses and expands one level of spans', async () => { renderTraceViewContainer(); expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(3); - userEvent.click(screen.getByLabelText('Collapse +1')); + await userEvent.click(screen.getByLabelText('Collapse +1')); expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(2); - userEvent.click(screen.getByLabelText('Expand +1')); + await userEvent.click(screen.getByLabelText('Expand +1')); expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(3); }); - it('toggles collapses and expands all levels', () => { + it('toggles collapses and expands all levels', async () => { renderTraceViewContainer(); expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(3); - userEvent.click(screen.getByLabelText('Collapse All')); + await userEvent.click(screen.getByLabelText('Collapse All')); expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(1); - userEvent.click(screen.getByLabelText('Expand All')); + await userEvent.click(screen.getByLabelText('Expand All')); expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(3); }); - it('searches for spans', () => { + it('searches for spans', async () => { renderTraceViewContainer(); - userEvent.type(screen.getByPlaceholderText('Find...'), '1ed38015486087ca'); + await userEvent.type(screen.getByPlaceholderText('Find...'), '1ed38015486087ca'); expect( (screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[0].parentNode! as HTMLElement).className ).toContain('rowMatchingFilter'); }); - it('can select next/prev results', () => { + it('can select next/prev results', async () => { renderTraceViewContainer(); - userEvent.type(screen.getByPlaceholderText('Find...'), 'logproto'); + await userEvent.type(screen.getByPlaceholderText('Find...'), 'logproto'); const nextResultButton = screen.getByTestId('trace-page-search-bar-next-result-button'); const prevResultButton = screen.getByTestId('trace-page-search-bar-prev-result-button'); const suffix = screen.getByTestId('trace-page-search-bar-suffix'); - userEvent.click(nextResultButton); + await userEvent.click(nextResultButton); expect(suffix.textContent).toBe('1 of 2'); expect( (screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[1].parentNode! as HTMLElement).className ).toContain('rowFocused'); - userEvent.click(nextResultButton); + await userEvent.click(nextResultButton); expect(suffix.textContent).toBe('2 of 2'); expect( (screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[2].parentNode! as HTMLElement).className ).toContain('rowFocused'); - userEvent.click(nextResultButton); + await userEvent.click(nextResultButton); expect(suffix.textContent).toBe('1 of 2'); expect( (screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[1].parentNode! as HTMLElement).className ).toContain('rowFocused'); - userEvent.click(prevResultButton); + await userEvent.click(prevResultButton); expect(suffix.textContent).toBe('2 of 2'); expect( (screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[2].parentNode! as HTMLElement).className ).toContain('rowFocused'); - userEvent.click(prevResultButton); + await userEvent.click(prevResultButton); expect(suffix.textContent).toBe('1 of 2'); expect( (screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[1].parentNode! as HTMLElement).className ).toContain('rowFocused'); - userEvent.click(prevResultButton); + await userEvent.click(prevResultButton); expect(suffix.textContent).toBe('2 of 2'); expect( (screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[2].parentNode! as HTMLElement).className diff --git a/public/app/features/explore/Wrapper.test.tsx b/public/app/features/explore/Wrapper.test.tsx index d26b026620b..75c8f9576d6 100644 --- a/public/app/features/explore/Wrapper.test.tsx +++ b/public/app/features/explore/Wrapper.test.tsx @@ -217,7 +217,7 @@ describe('Wrapper', () => { }; setupExplore({ urlParams }); const closeButtons = await screen.findAllByTitle(/Close split pane/i); - userEvent.click(closeButtons[1]); + await userEvent.click(closeButtons[1]); await waitFor(() => { const logsPanels = screen.queryAllByTitle(/Close split pane/i); diff --git a/public/app/features/explore/spec/helper/interactions.ts b/public/app/features/explore/spec/helper/interactions.ts index f1ed4542cc4..46e806a00b9 100644 --- a/public/app/features/explore/spec/helper/interactions.ts +++ b/public/app/features/explore/spec/helper/interactions.ts @@ -11,23 +11,23 @@ export const changeDatasource = async (name: string) => { fireEvent.click(option); }; -export const inputQuery = (query: string, exploreId: ExploreId = ExploreId.left) => { +export const inputQuery = async (query: string, exploreId: ExploreId = ExploreId.left) => { const input = withinExplore(exploreId).getByRole('textbox', { name: 'query' }); - userEvent.clear(input); - userEvent.type(input, query); + await userEvent.clear(input); + await userEvent.type(input, query); }; -export const runQuery = (exploreId: ExploreId = ExploreId.left) => { +export const runQuery = async (exploreId: ExploreId = ExploreId.left) => { const explore = withinExplore(exploreId); const toolbar = within(explore.getByLabelText('Explore toolbar')); const button = toolbar.getByRole('button', { name: /run query/i }); - userEvent.click(button); + await userEvent.click(button); }; export const openQueryHistory = async (exploreId: ExploreId = ExploreId.left) => { const selector = withinExplore(exploreId); const button = selector.getByRole('button', { name: 'Rich history button' }); - userEvent.click(button); + await userEvent.click(button); expect( await selector.findByText('The history is local to your browser and is not shared with others.') ).toBeInTheDocument(); @@ -35,26 +35,26 @@ export const openQueryHistory = async (exploreId: ExploreId = ExploreId.left) => export const closeQueryHistory = async (exploreId: ExploreId = ExploreId.left) => { const closeButton = withinExplore(exploreId).getByRole('button', { name: 'Close query history' }); - userEvent.click(closeButton); + await userEvent.click(closeButton); }; export const switchToQueryHistoryTab = async ( name: 'Settings' | 'Query History', exploreId: ExploreId = ExploreId.left ) => { - userEvent.click(withinExplore(exploreId).getByRole('tab', { name: `Tab ${name}` })); + await userEvent.click(withinExplore(exploreId).getByRole('tab', { name: `Tab ${name}` })); }; -export const selectStarredTabFirst = (exploreId: ExploreId = ExploreId.left) => { +export const selectStarredTabFirst = async (exploreId: ExploreId = ExploreId.left) => { const checkbox = withinExplore(exploreId).getByRole('checkbox', { name: 'Change the default active tab from “Query history” to “Starred”', }); - userEvent.click(checkbox); + await userEvent.click(checkbox); }; -export const selectOnlyActiveDataSource = (exploreId: ExploreId = ExploreId.left) => { +export const selectOnlyActiveDataSource = async (exploreId: ExploreId = ExploreId.left) => { const checkbox = withinExplore(exploreId).getByLabelText(/Only show queries for data source currently active.*/); - userEvent.click(checkbox); + await userEvent.click(checkbox); }; export const starQueryHistory = (queryIndex: number, exploreId: ExploreId = ExploreId.left) => { @@ -65,8 +65,8 @@ export const deleteQueryHistory = (queryIndex: number, exploreId: ExploreId = Ex invokeAction(queryIndex, 'Delete query', exploreId); }; -const invokeAction = (queryIndex: number, actionAccessibleName: string, exploreId: ExploreId) => { +const invokeAction = async (queryIndex: number, actionAccessibleName: string, exploreId: ExploreId) => { const selector = withinExplore(exploreId); const buttons = selector.getAllByRole('button', { name: actionAccessibleName }); - userEvent.click(buttons[queryIndex]); + await userEvent.click(buttons[queryIndex]); }; diff --git a/public/app/features/explore/spec/queryHistory.test.tsx b/public/app/features/explore/spec/queryHistory.test.tsx index b62c8d2558d..291cc78cfbe 100644 --- a/public/app/features/explore/spec/queryHistory.test.tsx +++ b/public/app/features/explore/spec/queryHistory.test.tsx @@ -49,8 +49,8 @@ describe('Explore: Query History', () => { await waitForExplore(); // and a user runs a query and opens query history - inputQuery(USER_INPUT); - runQuery(); + await inputQuery(USER_INPUT); + await runQuery(); await openQueryHistory(); // the query that was run is in query history @@ -80,8 +80,8 @@ describe('Explore: Query History', () => { await waitForExplore(); await openQueryHistory(); - inputQuery('query #2'); - runQuery(); + await inputQuery('query #2'); + await runQuery(); await assertQueryHistory(['{"expr":"query #2"}', '{"expr":"query #1"}']); }); @@ -135,8 +135,8 @@ describe('Explore: Query History', () => { await switchToQueryHistoryTab('Settings'); // change settings - selectStarredTabFirst(); - selectOnlyActiveDataSource(); + await selectStarredTabFirst(); + await selectOnlyActiveDataSource(); await closeQueryHistory(); await openQueryHistory(); diff --git a/public/app/features/inspector/InspectDataTab.test.tsx b/public/app/features/inspector/InspectDataTab.test.tsx index 5e8459b9ea1..15e8d2591db 100644 --- a/public/app/features/inspector/InspectDataTab.test.tsx +++ b/public/app/features/inspector/InspectDataTab.test.tsx @@ -50,19 +50,19 @@ describe('InspectDataTab', () => { render(); expect(screen.getByText(/Data options/i)).toBeInTheDocument(); }); - it('should show available options', () => { + it('should show available options', async () => { render(); const dataOptions = screen.getByText(/Data options/i); - userEvent.click(dataOptions); + await userEvent.click(dataOptions); expect(screen.getByText(/Show data frame/i)).toBeInTheDocument(); expect(screen.getByText(/Download for Excel/i)).toBeInTheDocument(); }); - it('should show available dataFrame options', () => { + it('should show available dataFrame options', async () => { render(); const dataOptions = screen.getByText(/Data options/i); - userEvent.click(dataOptions); + await userEvent.click(dataOptions); const dataFrameInput = screen.getByRole('combobox', { name: /Select dataframe/i }); - userEvent.click(dataFrameInput); + await userEvent.click(dataFrameInput); expect(screen.getByText(/Second data frame/i)).toBeInTheDocument(); }); it('should show download logs button if logs data', () => { diff --git a/public/app/features/invites/SignupInvited.test.tsx b/public/app/features/invites/SignupInvited.test.tsx index e5ed872e224..c5202a15eac 100644 --- a/public/app/features/invites/SignupInvited.test.tsx +++ b/public/app/features/invites/SignupInvited.test.tsx @@ -103,7 +103,7 @@ describe('SignupInvitedPage', () => { it('then required fields should show error messages and nothing should be posted', async () => { const { postSpy } = await setupTestContext({ get: { email: '', invitedBy: '', name: '', username: '' } }); - userEvent.click(screen.getByRole('button', { name: /sign up/i })); + await userEvent.click(screen.getByRole('button', { name: /sign up/i })); await waitFor(() => expect(screen.getByText(/email is required/i)).toBeInTheDocument()); expect(screen.getByText(/username is required/i)).toBeInTheDocument(); @@ -116,8 +116,8 @@ describe('SignupInvitedPage', () => { it('then correct form data should be posted', async () => { const { postSpy } = await setupTestContext(); - userEvent.type(screen.getByPlaceholderText(/password/i), 'pass@word1'); - userEvent.click(screen.getByRole('button', { name: /sign up/i })); + await userEvent.type(screen.getByPlaceholderText(/password/i), 'pass@word1'); + await userEvent.click(screen.getByRole('button', { name: /sign up/i })); await waitFor(() => expect(postSpy).toHaveBeenCalledTimes(1)); expect(postSpy).toHaveBeenCalledWith('/api/user/invite/complete', { diff --git a/public/app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch.test.tsx b/public/app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch.test.tsx index 5c9c4ca4b06..04466176e03 100644 --- a/public/app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch.test.tsx +++ b/public/app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch.test.tsx @@ -75,6 +75,7 @@ async function getTestContext( await waitFor(() => expect(getLibraryPanelsSpy).toHaveBeenCalled()); expect(getLibraryPanelsSpy).toHaveBeenCalledTimes(1); + jest.clearAllMocks(); return { rerender, getLibraryPanelsSpy, getSpy, getAllPanelPluginMetaSpy }; } @@ -91,9 +92,8 @@ describe('LibraryPanelsSearch', () => { describe('and user searches for library panel by name or description', () => { it('should call api with correct params', async () => { const { getLibraryPanelsSpy } = await getTestContext(); - getLibraryPanelsSpy.mockClear(); - userEvent.type(screen.getByPlaceholderText(/search by name/i), 'a'); + await userEvent.type(screen.getByPlaceholderText(/search by name/i), 'a'); await waitFor(() => expect(getLibraryPanelsSpy).toHaveBeenCalled()); expect(getLibraryPanelsSpy).toHaveBeenCalledTimes(1); expect(getLibraryPanelsSpy).toHaveBeenCalledWith({ @@ -119,18 +119,18 @@ describe('LibraryPanelsSearch', () => { describe('and user changes sorting', () => { it('should call api with correct params', async () => { const { getLibraryPanelsSpy } = await getTestContext({ showSort: true }); - getLibraryPanelsSpy.mockClear(); - userEvent.type(screen.getByText(/sort \(default a–z\)/i), 'Desc{enter}'); - await waitFor(() => expect(getLibraryPanelsSpy).toHaveBeenCalledTimes(1)); - expect(getLibraryPanelsSpy).toHaveBeenCalledWith({ - searchString: '', - sortDirection: 'alpha-desc', - folderFilter: [], - page: 0, - typeFilter: [], - perPage: 40, - }); + await userEvent.type(screen.getByText(/sort \(default a–z\)/i), 'Desc{enter}'); + await waitFor(() => + expect(getLibraryPanelsSpy).toHaveBeenCalledWith({ + searchString: '', + sortDirection: 'alpha-desc', + folderFilter: [], + page: 0, + typeFilter: [], + perPage: 40, + }) + ); }); }); }); @@ -147,18 +147,18 @@ describe('LibraryPanelsSearch', () => { describe('and user changes panel filter', () => { it('should call api with correct params', async () => { const { getLibraryPanelsSpy } = await getTestContext({ showPanelFilter: true }); - getLibraryPanelsSpy.mockClear(); - userEvent.type(screen.getByRole('combobox', { name: /panel type filter/i }), 'Graph{enter}'); - userEvent.type(screen.getByRole('combobox', { name: /panel type filter/i }), 'Time Series{enter}'); - await waitFor(() => expect(getLibraryPanelsSpy).toHaveBeenCalledTimes(1)); - expect(getLibraryPanelsSpy).toHaveBeenCalledWith({ - searchString: '', - folderFilter: [], - page: 0, - typeFilter: ['graph', 'timeseries'], - perPage: 40, - }); + await userEvent.type(screen.getByRole('combobox', { name: /panel type filter/i }), 'Graph{enter}'); + await userEvent.type(screen.getByRole('combobox', { name: /panel type filter/i }), 'Time Series{enter}'); + await waitFor(() => + expect(getLibraryPanelsSpy).toHaveBeenCalledWith({ + searchString: '', + folderFilter: [], + page: 0, + typeFilter: ['graph', 'timeseries'], + perPage: 40, + }) + ); }); }); }); @@ -175,20 +175,20 @@ describe('LibraryPanelsSearch', () => { describe('and user changes folder filter', () => { it('should call api with correct params', async () => { const { getLibraryPanelsSpy } = await getTestContext({ showFolderFilter: true }); - getLibraryPanelsSpy.mockClear(); - userEvent.click(screen.getByRole('combobox', { name: /folder filter/i })); - userEvent.type(screen.getByRole('combobox', { name: /folder filter/i }), '{enter}', { + await userEvent.click(screen.getByRole('combobox', { name: /folder filter/i })); + await userEvent.type(screen.getByRole('combobox', { name: /folder filter/i }), '{enter}', { skipClick: true, }); - await waitFor(() => expect(getLibraryPanelsSpy).toHaveBeenCalledTimes(1)); - expect(getLibraryPanelsSpy).toHaveBeenCalledWith({ - searchString: '', - folderFilter: ['0'], - page: 0, - typeFilter: [], - perPage: 40, - }); + await waitFor(() => + expect(getLibraryPanelsSpy).toHaveBeenCalledWith({ + searchString: '', + folderFilter: ['0'], + page: 0, + typeFilter: [], + perPage: 40, + }) + ); }); }); }); diff --git a/public/app/features/playlist/PlaylistEditPage.test.tsx b/public/app/features/playlist/PlaylistEditPage.test.tsx index 2766c0fe6c4..b72ab02eb0a 100644 --- a/public/app/features/playlist/PlaylistEditPage.test.tsx +++ b/public/app/features/playlist/PlaylistEditPage.test.tsx @@ -69,10 +69,10 @@ describe('PlaylistEditPage', () => { const { putMock } = await getTestContext(); expect(locationService.getLocation().pathname).toEqual('/'); - userEvent.clear(screen.getByRole('textbox', { name: /playlist name/i })); - userEvent.type(screen.getByRole('textbox', { name: /playlist name/i }), 'A Name'); - userEvent.clear(screen.getByRole('textbox', { name: /playlist interval/i })); - userEvent.type(screen.getByRole('textbox', { name: /playlist interval/i }), '10s'); + await userEvent.clear(screen.getByRole('textbox', { name: /playlist name/i })); + await userEvent.type(screen.getByRole('textbox', { name: /playlist name/i }), 'A Name'); + await userEvent.clear(screen.getByRole('textbox', { name: /playlist interval/i })); + await userEvent.type(screen.getByRole('textbox', { name: /playlist interval/i }), '10s'); fireEvent.submit(screen.getByRole('button', { name: /save/i })); await waitFor(() => expect(putMock).toHaveBeenCalledTimes(1)); expect(putMock).toHaveBeenCalledWith('/api/playlists/1', { diff --git a/public/app/features/playlist/PlaylistForm.test.tsx b/public/app/features/playlist/PlaylistForm.test.tsx index b401838b698..2786ae0006a 100644 --- a/public/app/features/playlist/PlaylistForm.test.tsx +++ b/public/app/features/playlist/PlaylistForm.test.tsx @@ -96,11 +96,11 @@ describe('PlaylistForm', () => { }); describe('when deleting a playlist item', () => { - it('then the item should be removed and other items should be correct', () => { + it('then the item should be removed and other items should be correct', async () => { getTestContext(playlist); expect(rows()).toHaveLength(3); - userEvent.click(within(rows()[2]).getByRole('button', { name: /delete playlist item/i })); + await userEvent.click(within(rows()[2]).getByRole('button', { name: /delete playlist item/i })); expect(rows()).toHaveLength(2); expectCorrectRow({ index: 0, type: 'id', title: 'first item', first: true }); expectCorrectRow({ index: 1, type: 'id', title: 'middle item', last: true }); @@ -108,10 +108,10 @@ describe('PlaylistForm', () => { }); describe('when moving a playlist item up', () => { - it('then the item should be removed and other items should be correct', () => { + it('then the item should be removed and other items should be correct', async () => { getTestContext(playlist); - userEvent.click(within(rows()[2]).getByRole('button', { name: /move playlist item order up/i })); + await userEvent.click(within(rows()[2]).getByRole('button', { name: /move playlist item order up/i })); expectCorrectRow({ index: 0, type: 'id', title: 'first item', first: true }); expectCorrectRow({ index: 1, type: 'tag', title: 'last item' }); expectCorrectRow({ index: 2, type: 'id', title: 'middle item', last: true }); @@ -119,10 +119,10 @@ describe('PlaylistForm', () => { }); describe('when moving a playlist item down', () => { - it('then the item should be removed and other items should be correct', () => { + it('then the item should be removed and other items should be correct', async () => { getTestContext(playlist); - userEvent.click(within(rows()[0]).getByRole('button', { name: /move playlist item order down/i })); + await userEvent.click(within(rows()[0]).getByRole('button', { name: /move playlist item order down/i })); expectCorrectRow({ index: 0, type: 'id', title: 'middle item', first: true }); expectCorrectRow({ index: 1, type: 'id', title: 'first item' }); expectCorrectRow({ index: 2, type: 'tag', title: 'last item', last: true }); @@ -152,7 +152,7 @@ describe('PlaylistForm', () => { it('then an alert should appear and nothing should be submitted', async () => { const { onSubmitMock } = getTestContext(playlist); - userEvent.clear(screen.getByRole('textbox', { name: /playlist interval/i })); + await userEvent.clear(screen.getByRole('textbox', { name: /playlist interval/i })); fireEvent.submit(screen.getByRole('button', { name: /save/i })); expect(await screen.findAllByRole('alert')).toHaveLength(1); expect(onSubmitMock).not.toHaveBeenCalled(); diff --git a/public/app/features/playlist/PlaylistNewPage.test.tsx b/public/app/features/playlist/PlaylistNewPage.test.tsx index 487bd152607..c7011b41414 100644 --- a/public/app/features/playlist/PlaylistNewPage.test.tsx +++ b/public/app/features/playlist/PlaylistNewPage.test.tsx @@ -66,7 +66,7 @@ describe('PlaylistNewPage', () => { const { backendSrvMock } = getTestContext(); expect(locationService.getLocation().pathname).toEqual('/'); - userEvent.type(screen.getByRole('textbox', { name: /playlist name/i }), 'A Name'); + await userEvent.type(screen.getByRole('textbox', { name: /playlist name/i }), 'A Name'); fireEvent.submit(screen.getByRole('button', { name: /save/i })); await waitFor(() => expect(backendSrvMock).toHaveBeenCalledTimes(1)); expect(backendSrvMock).toHaveBeenCalledWith('/api/playlists', { diff --git a/public/app/features/plugins/admin/pages/Browse.test.tsx b/public/app/features/plugins/admin/pages/Browse.test.tsx index 0e9cbc4d86a..b1c4e5cab7e 100644 --- a/public/app/features/plugins/admin/pages/Browse.test.tsx +++ b/public/app/features/plugins/admin/pages/Browse.test.tsx @@ -349,7 +349,7 @@ describe('Browse list of plugins', () => { expect(listOption).not.toBeChecked(); // Switch to "list" view - userEvent.click(listOption); + await userEvent.click(listOption); expect(gridOption).not.toBeChecked(); expect(listOption).toBeChecked(); diff --git a/public/app/features/plugins/admin/pages/PluginDetails.test.tsx b/public/app/features/plugins/admin/pages/PluginDetails.test.tsx index 8d0e8d367f0..6f24844251f 100644 --- a/public/app/features/plugins/admin/pages/PluginDetails.test.tsx +++ b/public/app/features/plugins/admin/pages/PluginDetails.test.tsx @@ -447,14 +447,14 @@ describe('Plugin details page', () => { await waitFor(() => expect(queryByText(PluginTabLabels.OVERVIEW)).toBeInTheDocument()); // Open the confirmation modal - userEvent.click(getByRole('button', { name: /uninstall/i })); + await userEvent.click(getByRole('button', { name: /uninstall/i })); expect(queryByText('Uninstall Akumuli')).toBeInTheDocument(); expect(queryByText('Are you sure you want to uninstall this plugin?')).toBeInTheDocument(); expect(api.uninstallPlugin).toHaveBeenCalledTimes(0); // Confirm the uninstall - userEvent.click(getByRole('button', { name: /confirm/i })); + await userEvent.click(getByRole('button', { name: /confirm/i })); expect(api.uninstallPlugin).toHaveBeenCalledTimes(1); expect(api.uninstallPlugin).toHaveBeenCalledWith(id); @@ -635,7 +635,7 @@ describe('Plugin details page', () => { await waitFor(() => queryByText('Uninstall')); // Click on "Enable" - userEvent.click(getByRole('button', { name: /enable/i })); + await userEvent.click(getByRole('button', { name: /enable/i })); // Check if the API request was initiated expect(api.updatePluginSettings).toHaveBeenCalledTimes(1); @@ -675,7 +675,7 @@ describe('Plugin details page', () => { await waitFor(() => queryByText('Uninstall')); // Click on "Disable" - userEvent.click(getByRole('button', { name: /disable/i })); + await userEvent.click(getByRole('button', { name: /disable/i })); // Check if the API request was initiated expect(api.updatePluginSettings).toHaveBeenCalledTimes(1); diff --git a/public/app/features/profile/ChangePasswordPage.test.tsx b/public/app/features/profile/ChangePasswordPage.test.tsx index eebd890fa11..3fc936793b6 100644 --- a/public/app/features/profile/ChangePasswordPage.test.tsx +++ b/public/app/features/profile/ChangePasswordPage.test.tsx @@ -83,9 +83,9 @@ describe('ChangePasswordPage', () => { it('should call changePassword if change password is valid', async () => { const { props } = await getTestContext(); - userEvent.type(screen.getByLabelText('Old password'), 'test'); - userEvent.type(screen.getByLabelText('New password'), 'admin'); - userEvent.type(screen.getByLabelText('Confirm password'), 'admin'); + await userEvent.type(screen.getByLabelText('Old password'), 'test'); + await userEvent.type(screen.getByLabelText('New password'), 'admin'); + await userEvent.type(screen.getByLabelText('Confirm password'), 'admin'); fireEvent.click(screen.getByRole('button', { name: 'Change Password' })); await waitFor(() => { expect(props.changePassword).toHaveBeenCalledTimes(1); diff --git a/public/app/features/profile/UserProfileEditPage.test.tsx b/public/app/features/profile/UserProfileEditPage.test.tsx index b8f3c476de6..bc31d02c8c6 100644 --- a/public/app/features/profile/UserProfileEditPage.test.tsx +++ b/public/app/features/profile/UserProfileEditPage.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; +import userEvent, { PointerEventsCheckLevel } from '@testing-library/user-event'; import { within } from '@testing-library/dom'; import { OrgRole } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; @@ -239,10 +239,10 @@ describe('UserProfileEditPage', () => { const { props } = await getTestContext(); const { email, saveProfile } = getSelectors(); - userEvent.clear(email()); - userEvent.type(email(), 'test@test.se'); + await userEvent.clear(email()); + await userEvent.type(email(), 'test@test.se'); // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed - userEvent.click(saveProfile(), undefined, { skipPointerEventsCheck: true }); + await userEvent.click(saveProfile(), { pointerEventsCheck: PointerEventsCheckLevel.Never }); await waitFor(() => expect(props.updateUserProfile).toHaveBeenCalledTimes(1)); expect(props.updateUserProfile).toHaveBeenCalledWith({ @@ -261,7 +261,7 @@ describe('UserProfileEditPage', () => { name: /select organisation/i, }); - userEvent.click(orgsAdminSelectButton()); + await userEvent.click(orgsAdminSelectButton()); await waitFor(() => expect(props.changeUserOrg).toHaveBeenCalledTimes(1)); expect(props.changeUserOrg).toHaveBeenCalledWith({ @@ -280,7 +280,7 @@ describe('UserProfileEditPage', () => { name: /revoke user session/i, }); - userEvent.click(sessionsRevokeButton()); + await userEvent.click(sessionsRevokeButton()); await waitFor(() => expect(props.revokeUserSession).toHaveBeenCalledTimes(1)); expect(props.revokeUserSession).toHaveBeenCalledWith(0); diff --git a/public/app/features/variables/adhoc/picker/AdHocFilter.test.tsx b/public/app/features/variables/adhoc/picker/AdHocFilter.test.tsx index cccdc3f8a1c..526c76f353d 100644 --- a/public/app/features/variables/adhoc/picker/AdHocFilter.test.tsx +++ b/public/app/features/variables/adhoc/picker/AdHocFilter.test.tsx @@ -20,13 +20,13 @@ describe('AdHocFilter', () => { const { addFilter } = setup(); // Select key - userEvent.click(screen.getByLabelText('Add Filter')); + await userEvent.click(screen.getByLabelText('Add Filter')); const selectEl = screen.getByTestId('AdHocFilterKey-add-key-wrapper'); expect(selectEl).toBeInTheDocument(); await selectEvent.select(selectEl, 'key3', { container: document.body }); // Select value - userEvent.click(screen.getByText('select value')); + await userEvent.click(screen.getByText('select value')); // There are already some filters rendered const selectEl2 = screen.getAllByTestId('AdHocFilterValue-value-wrapper')[2]; await selectEvent.select(selectEl2, 'val3', { container: document.body }); @@ -39,7 +39,7 @@ describe('AdHocFilter', () => { const { removeFilter } = setup(); // Select key - userEvent.click(screen.getByText('key1')); + await userEvent.click(screen.getByText('key1')); const selectEl = screen.getAllByTestId('AdHocFilterKey-key-wrapper')[0]; expect(selectEl).toBeInTheDocument(); await selectEvent.select(selectEl, '-- remove filter --', { container: document.body }); @@ -52,7 +52,7 @@ describe('AdHocFilter', () => { const { changeFilter } = setup(); // Select key - userEvent.click(screen.getByText('val1')); + await userEvent.click(screen.getByText('val1')); const selectEl = screen.getAllByTestId('AdHocFilterValue-value-wrapper')[0]; expect(selectEl).toBeInTheDocument(); await selectEvent.select(selectEl, 'val4', { container: document.body }); diff --git a/public/app/features/variables/inspect/VariablesUnknownTable.test.tsx b/public/app/features/variables/inspect/VariablesUnknownTable.test.tsx index 0173b108dc0..be9a286e80f 100644 --- a/public/app/features/variables/inspect/VariablesUnknownTable.test.tsx +++ b/public/app/features/variables/inspect/VariablesUnknownTable.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import * as runtime from '@grafana/runtime'; -import { render, screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react'; +import { act, render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { VariablesUnknownTable, VariablesUnknownTableProps } from './VariablesUnknownTable'; @@ -36,25 +36,17 @@ describe('VariablesUnknownTable', () => { }); describe('when expanding the section', () => { - it('then it should show loading spinner', async () => { - await getTestContext(); - - userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); - await waitFor(() => expect(screen.getByText('Loading...')).toBeInTheDocument()); - }); - it('then it should call getUnknownsNetwork', async () => { const { getUnknownsNetworkSpy } = await getTestContext(); - userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); + await userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); await waitFor(() => expect(getUnknownsNetworkSpy).toHaveBeenCalledTimes(1)); }); it('then it should report the interaction', async () => { const { reportInteractionSpy } = await getTestContext(); - userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); - await waitFor(() => expect(screen.getByText('Loading...')).toBeInTheDocument()); + await userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); expect(reportInteractionSpy).toHaveBeenCalledTimes(1); expect(reportInteractionSpy).toHaveBeenCalledWith('Unknown variables section expanded'); @@ -64,14 +56,14 @@ describe('VariablesUnknownTable', () => { it('then it should not call getUnknownsNetwork', async () => { const { getUnknownsNetworkSpy } = await getTestContext(); - userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); + await userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); await waitFor(() => expect(screen.getByRole('button')).toHaveAttribute('aria-expanded', 'true')); expect(getUnknownsNetworkSpy).toHaveBeenCalledTimes(1); - userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); + await userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); await waitFor(() => expect(screen.getByRole('button')).toHaveAttribute('aria-expanded', 'false')); - userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); + await userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); await waitFor(() => expect(screen.getByRole('button')).toHaveAttribute('aria-expanded', 'true')); expect(getUnknownsNetworkSpy).toHaveBeenCalledTimes(1); @@ -82,8 +74,7 @@ describe('VariablesUnknownTable', () => { it('then it should render the correct message', async () => { await getTestContext(); - userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); - await waitForElementToBeRemoved(() => screen.getByText('Loading...')); + await userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); expect(screen.getByText('No renamed or missing variables found.')).toBeInTheDocument(); }); @@ -95,8 +86,7 @@ describe('VariablesUnknownTable', () => { const usages = [{ variable, nodes: [], edges: [], showGraph: false }]; const { reportInteractionSpy } = await getTestContext({}, usages); - userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); - await waitForElementToBeRemoved(() => screen.getByText('Loading...')); + await userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); expect(screen.queryByText('No renamed or missing variables found.')).not.toBeInTheDocument(); expect(screen.getByText('Renamed Variable')).toBeInTheDocument(); @@ -117,16 +107,22 @@ describe('VariablesUnknownTable', () => { it('then it should report slow expansion', async () => { const variable = customBuilder().withId('Renamed Variable').withName('Renamed Variable').build(); const usages = [{ variable, nodes: [], edges: [], showGraph: false }]; - const { reportInteractionSpy } = await getTestContext({}, usages); + const { reportInteractionSpy, rerender } = await getTestContext({}, usages); const dateNowStart = 1000; const dateNowStop = 2000; Date.now = jest.fn().mockReturnValueOnce(dateNowStart).mockReturnValue(dateNowStop); - userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); - await waitForElementToBeRemoved(() => screen.getByText('Loading...')); + await userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); + const props: VariablesUnknownTableProps = { + variables: [], + dashboard: null, + }; + await act(async () => { + rerender(); + }); // make sure we report the interaction for slow expansion - expect(reportInteractionSpy).toHaveBeenCalledTimes(2); + await waitFor(() => expect(reportInteractionSpy).toHaveBeenCalledTimes(2)); expect(reportInteractionSpy.mock.calls[0][0]).toEqual('Unknown variables section expanded'); expect(reportInteractionSpy.mock.calls[1][0]).toEqual('Slow unknown variables expansion'); expect(reportInteractionSpy.mock.calls[1][1]).toEqual({ elapsed: 1000 }); diff --git a/public/app/features/variables/pickers/OptionsPicker/OptionPicker.test.tsx b/public/app/features/variables/pickers/OptionsPicker/OptionPicker.test.tsx index 36a1a50e17e..8806a3a6231 100644 --- a/public/app/features/variables/pickers/OptionsPicker/OptionPicker.test.tsx +++ b/public/app/features/variables/pickers/OptionsPicker/OptionPicker.test.tsx @@ -71,11 +71,11 @@ describe('OptionPicker', () => { expect(getSubMenu('A + C')).toBeInTheDocument(); }); - it('link text should be clickable', () => { + it('link text should be clickable', async () => { const { dispatch } = setupTestContext(); dispatch.mockClear(); - userEvent.click(getSubMenu('A + C')); + await userEvent.click(getSubMenu('A + C')); expect(dispatch).toHaveBeenCalledTimes(1); }); }); @@ -89,14 +89,14 @@ describe('OptionPicker', () => { expect(getSubMenu('A + C')).toBeInTheDocument(); }); - it('link text should be clickable', () => { + it('link text should be clickable', async () => { const { dispatch } = setupTestContext({ variable: defaultVariable, pickerState: { id: 'Other' }, }); dispatch.mockClear(); - userEvent.click(getSubMenu('A + C')); + await userEvent.click(getSubMenu('A + C')); expect(dispatch).toHaveBeenCalledTimes(1); }); }); @@ -110,13 +110,13 @@ describe('OptionPicker', () => { expect(screen.getByLabelText(selectors.components.LoadingIndicator.icon)).toBeInTheDocument(); }); - it('link text should not be clickable', () => { + it('link text should not be clickable', async () => { const { dispatch } = setupTestContext({ variable: { ...defaultVariable, state: LoadingState.Loading }, }); dispatch.mockClear(); - userEvent.click(getSubMenu('A + C')); + await userEvent.click(getSubMenu('A + C')); expect(dispatch).toHaveBeenCalledTimes(0); }); }); diff --git a/public/app/features/variables/query/QueryVariableEditor.test.tsx b/public/app/features/variables/query/QueryVariableEditor.test.tsx index 987446dd904..b8a79ce6f52 100644 --- a/public/app/features/variables/query/QueryVariableEditor.test.tsx +++ b/public/app/features/variables/query/QueryVariableEditor.test.tsx @@ -69,13 +69,13 @@ describe('QueryVariableEditor', () => { ${'regex'} | ${'onPropChange'} | ${[{ propName: 'regex', propValue: 't', updateOptions: true }]} `( '$fieldName field and tabs away then $propName should be called with correct args', - ({ fieldName, propName, expectedArgs }) => { + async ({ fieldName, propName, expectedArgs }) => { const { props } = setupTestContext({}); const propUnderTest = props[propName]; const fieldAccessor = fieldAccessors[fieldName]; - userEvent.type(fieldAccessor(), 't'); - userEvent.tab(); + await userEvent.type(fieldAccessor(), 't'); + await userEvent.tab(); expect(propUnderTest).toHaveBeenCalledTimes(1); expect(propUnderTest).toHaveBeenCalledWith(...expectedArgs); @@ -90,14 +90,14 @@ describe('QueryVariableEditor', () => { ${'regex'} | ${'onPropChange'} `( '$fieldName field but reverts the change and tabs away then $propName should not be called', - ({ fieldName, propName }) => { + async ({ fieldName, propName }) => { const { props } = setupTestContext({}); const propUnderTest = props[propName]; const fieldAccessor = fieldAccessors[fieldName]; - userEvent.type(fieldAccessor(), 't'); - userEvent.type(fieldAccessor(), '{backspace}'); - userEvent.tab(); + await userEvent.type(fieldAccessor(), 't'); + await userEvent.type(fieldAccessor(), '{backspace}'); + await userEvent.tab(); expect(propUnderTest).not.toHaveBeenCalled(); } diff --git a/public/app/plugins/datasource/cloudwatch/components/Dimensions/Dimensions.test.tsx b/public/app/plugins/datasource/cloudwatch/components/Dimensions/Dimensions.test.tsx index 671cb909f90..7e38c356722 100644 --- a/public/app/plugins/datasource/cloudwatch/components/Dimensions/Dimensions.test.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/Dimensions/Dimensions.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { fireEvent, render, screen, act, within } from '@testing-library/react'; +import { fireEvent, render, screen, within } from '@testing-library/react'; import { setupMockedDataSource } from '../../__mocks__/CloudWatchDataSource'; import { CloudWatchMetricsQuery } from '../../types'; import userEvent from '@testing-library/user-event'; @@ -60,7 +60,7 @@ describe('Dimensions', () => { const onChange = jest.fn(); render(); - userEvent.click(screen.getByLabelText('Add')); + await userEvent.click(screen.getByLabelText('Add')); expect(screen.getByTestId('cloudwatch-dimensions-filter-item')).toBeInTheDocument(); expect(onChange).not.toHaveBeenCalled(); }); @@ -74,13 +74,13 @@ describe('Dimensions', () => { ); - userEvent.click(screen.getByLabelText('Add')); + await userEvent.click(screen.getByLabelText('Add')); const filterItemElement = screen.getByTestId('cloudwatch-dimensions-filter-item'); expect(filterItemElement).toBeInTheDocument(); const keyElement = container.querySelector('#cloudwatch-dimensions-filter-item-key'); expect(keyElement).toBeInTheDocument(); - userEvent.type(keyElement!, 'my-key'); + await userEvent.type(keyElement!, 'my-key'); fireEvent.keyDown(keyElement!, { keyCode: 13 }); expect(onChange).not.toHaveBeenCalled(); }); @@ -95,24 +95,20 @@ describe('Dimensions', () => { ); const label = await screen.findByLabelText('Add'); - userEvent.click(label); + await userEvent.click(label); const filterItemElement = screen.getByTestId('cloudwatch-dimensions-filter-item'); expect(filterItemElement).toBeInTheDocument(); const keyElement = container.querySelector('#cloudwatch-dimensions-filter-item-key'); expect(keyElement).toBeInTheDocument(); - await act(async () => { - userEvent.type(keyElement!, 'my-key'); - fireEvent.keyDown(keyElement!, { keyCode: 13 }); - }); + await userEvent.type(keyElement!, 'my-key'); + fireEvent.keyDown(keyElement!, { keyCode: 13 }); expect(onChange).not.toHaveBeenCalled(); const valueElement = container.querySelector('#cloudwatch-dimensions-filter-item-value'); expect(valueElement).toBeInTheDocument(); - await act(async () => { - userEvent.type(valueElement!, 'my-value'); - fireEvent.keyDown(valueElement!, { keyCode: 13 }); - }); + await userEvent.type(valueElement!, 'my-value'); + fireEvent.keyDown(valueElement!, { keyCode: 13 }); expect(onChange).not.toHaveBeenCalledWith({ ...props.query, dimensions: { diff --git a/public/app/plugins/datasource/cloudwatch/components/MetricStatEditor/MetricStatEditor.test.tsx b/public/app/plugins/datasource/cloudwatch/components/MetricStatEditor/MetricStatEditor.test.tsx index 4dbf411b2ec..09b053731ae 100644 --- a/public/app/plugins/datasource/cloudwatch/components/MetricStatEditor/MetricStatEditor.test.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/MetricStatEditor/MetricStatEditor.test.tsx @@ -47,7 +47,7 @@ describe('MetricStatEditor', () => { const statisticElement = await screen.findByLabelText('Statistic'); expect(statisticElement).toBeInTheDocument(); - userEvent.type(statisticElement, statistic); + await userEvent.type(statisticElement, statistic); fireEvent.keyDown(statisticElement, { keyCode: 13 }); expect(onChange).toHaveBeenCalledWith({ ...props.query, statistic }); expect(onRunQuery).toHaveBeenCalled(); @@ -62,7 +62,7 @@ describe('MetricStatEditor', () => { const statisticElement = await screen.findByLabelText('Statistic'); expect(statisticElement).toBeInTheDocument(); - userEvent.type(statisticElement, statistic); + await userEvent.type(statisticElement, statistic); fireEvent.keyDown(statisticElement, { keyCode: 13 }); expect(onChange).not.toHaveBeenCalled(); expect(onRunQuery).not.toHaveBeenCalled(); diff --git a/public/app/plugins/datasource/dashboard/DashboardQueryEditor.test.tsx b/public/app/plugins/datasource/dashboard/DashboardQueryEditor.test.tsx index afef996aff0..2d8ad850609 100644 --- a/public/app/plugins/datasource/dashboard/DashboardQueryEditor.test.tsx +++ b/public/app/plugins/datasource/dashboard/DashboardQueryEditor.test.tsx @@ -79,7 +79,7 @@ describe('DashboardQueryEditor', () => { ); const select = screen.getByText('Choose panel'); - userEvent.click(select); + await userEvent.click(select); const myFirstPanel = await screen.findByText('My first panel'); expect(myFirstPanel).toBeInTheDocument(); @@ -102,7 +102,7 @@ describe('DashboardQueryEditor', () => { ); const select = screen.getByText('Choose panel'); - userEvent.click(select); + await userEvent.click(select); expect(screen.queryByText('My first panel')).not.toBeInTheDocument(); diff --git a/public/app/plugins/datasource/elasticsearch/components/hooks/useCreatableSelectPersistedBehaviour.test.tsx b/public/app/plugins/datasource/elasticsearch/components/hooks/useCreatableSelectPersistedBehaviour.test.tsx index 8b5f9cb5ede..c8a2e756467 100644 --- a/public/app/plugins/datasource/elasticsearch/components/hooks/useCreatableSelectPersistedBehaviour.test.tsx +++ b/public/app/plugins/datasource/elasticsearch/components/hooks/useCreatableSelectPersistedBehaviour.test.tsx @@ -5,7 +5,7 @@ import { useCreatableSelectPersistedBehaviour } from './useCreatableSelectPersis import userEvent from '@testing-library/user-event'; describe('useCreatableSelectPersistedBehaviour', () => { - it('Should make a Select accept custom values', () => { + it('Should make a Select accept custom values', async () => { const MyComp = (_: { force?: boolean }) => ( { expect(input).toBeInTheDocument(); // we open the menu - userEvent.click(input); + await userEvent.click(input); // we expect 2 elemnts having "Option 2": the input itself and the option. expect(screen.getAllByText('Option 2')).toHaveLength(2); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/NewMetricsQueryEditor/MetricsQueryEditor.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/NewMetricsQueryEditor/MetricsQueryEditor.test.tsx index 6b7347e2d71..d39c0e787c7 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/NewMetricsQueryEditor/MetricsQueryEditor.test.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/NewMetricsQueryEditor/MetricsQueryEditor.test.tsx @@ -83,9 +83,9 @@ describe('MetricsQueryEditor', () => { const checkbox = await screen.findByLabelText('web-server'); expect(checkbox).toBeInTheDocument(); expect(checkbox).not.toBeChecked(); - userEvent.click(checkbox); + await userEvent.click(checkbox); expect(checkbox).toBeChecked(); - userEvent.click(await screen.findByRole('button', { name: 'Apply' })); + await userEvent.click(await screen.findByRole('button', { name: 'Apply' })); expect(onChange).toBeCalledTimes(1); expect(onChange).toBeCalledWith( @@ -136,9 +136,9 @@ describe('MetricsQueryEditor', () => { const checkbox = await screen.findByLabelText('db-server'); expect(checkbox).toBeInTheDocument(); expect(checkbox).not.toBeChecked(); - userEvent.click(checkbox); + await userEvent.click(checkbox); expect(checkbox).toBeChecked(); - userEvent.click(await screen.findByRole('button', { name: 'Apply' })); + await userEvent.click(await screen.findByRole('button', { name: 'Apply' })); expect(onChange).toBeCalledTimes(1); expect(onChange).toBeCalledWith( diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/ResourcePicker.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/ResourcePicker.test.tsx index 7b6a3881623..fa19e3fe1f2 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/ResourcePicker.test.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/ResourcePicker.test.tsx @@ -100,7 +100,7 @@ describe('AzureMonitor ResourcePicker', () => { advancedSection.click(); const advancedInput = await screen.findByLabelText('Resource URI'); - userEvent.type(advancedInput, '/subscriptions/def-123'); + await userEvent.type(advancedInput, '/subscriptions/def-123'); const applyButton = screen.getByRole('button', { name: 'Apply' }); applyButton.click(); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/VariableEditor/VariableEditor.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/VariableEditor/VariableEditor.test.tsx index f9a233d1853..d9385aab47d 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/VariableEditor/VariableEditor.test.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/VariableEditor/VariableEditor.test.tsx @@ -137,7 +137,7 @@ describe('VariableEditor:', () => { }; render(); await waitFor(() => screen.queryByText('Grafana template variable function')); - userEvent.type(screen.getByDisplayValue('Su'), 'bscriptions()'); + await userEvent.type(screen.getByDisplayValue('Su'), 'bscriptions()'); expect(screen.getByDisplayValue('Subscriptions()')).toBeInTheDocument(); screen.getByDisplayValue('Subscriptions()').blur(); await waitFor(() => screen.queryByText('None')); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/AnalyticsConfig.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/AnalyticsConfig.test.tsx index b6e1edea44d..9528c74900a 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/AnalyticsConfig.test.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/AnalyticsConfig.test.tsx @@ -91,7 +91,7 @@ describe('Render', () => { updateOptions: onUpdate, })); expect(screen.queryByText('is no longer supported', { exact: false })).toBeInTheDocument(); - userEvent.click(screen.getByText('Clear Azure Monitor Logs Credentials')); + await userEvent.click(screen.getByText('Clear Azure Monitor Logs Credentials')); expect(onUpdate).toHaveBeenCalled(); const newOpts = onUpdate.mock.calls[0][0]({}); expect(newOpts).toEqual({ jsonData: { azureLogAnalyticsSameAs: true } }); diff --git a/public/app/plugins/datasource/influxdb/components/RawInfluxQLEditor.test.tsx b/public/app/plugins/datasource/influxdb/components/RawInfluxQLEditor.test.tsx index 74a6ce5d65c..54ad496b2b1 100644 --- a/public/app/plugins/datasource/influxdb/components/RawInfluxQLEditor.test.tsx +++ b/public/app/plugins/datasource/influxdb/components/RawInfluxQLEditor.test.tsx @@ -74,7 +74,7 @@ describe('RawInfluxQLEditor', () => { // value before expect(queryTextarea).toHaveValue('test query 1'); - userEvent.type(queryTextarea, 'new changes'); + await userEvent.type(queryTextarea, 'new changes'); // the field should have a new value, but no onChange yet. expect(queryTextarea).toHaveValue('test query 1new changes'); @@ -97,7 +97,7 @@ describe('RawInfluxQLEditor', () => { // value before expect(aliasInput).toHaveValue('alias42'); - userEvent.type(aliasInput, 'new changes'); + await userEvent.type(aliasInput, 'new changes'); // the field should have a new value, but no onChange yet. expect(aliasInput).toHaveValue('alias42new changes'); diff --git a/public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/Editor.tags.test.tsx b/public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/Editor.tags.test.tsx index 91d9e92cbb0..0b3c7d8169d 100644 --- a/public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/Editor.tags.test.tsx +++ b/public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/Editor.tags.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { InfluxQuery } from '../../types'; import InfluxDatasource from '../../datasource'; -import { render, screen, act } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { Editor } from './Editor'; import * as mockedMeta from '../../influxQLMetadataQuery'; @@ -95,9 +95,7 @@ describe('InfluxDB InfluxQL Visual Editor field-filtering', () => { expect(mockedMeta.getTagKeysForMeasurementAndTags).toHaveBeenCalledTimes(1); // we click the WHERE/cpu button - await act(async () => { - userEvent.click(screen.getByRole('button', { name: 'cpu' })); - }); + await userEvent.click(screen.getByRole('button', { name: 'cpu' })); // and verify getTagKeysForMeasurementAndTags was called again, // and in the tags-param we did not receive the `field1` part. @@ -105,18 +103,14 @@ describe('InfluxDB InfluxQL Visual Editor field-filtering', () => { expect((mockedMeta.getTagKeysForMeasurementAndTags as jest.Mock).mock.calls[1][2]).toStrictEqual(ONLY_TAGS); // now we click on the WHERE/host2 button - await act(async () => { - userEvent.click(screen.getByRole('button', { name: 'host2' })); - }); + await userEvent.click(screen.getByRole('button', { name: 'host2' })); // verify `getTagValues` was called once, and in the tags-param we did not receive `field1` expect(mockedMeta.getTagValues).toHaveBeenCalledTimes(1); expect((mockedMeta.getTagValues as jest.Mock).mock.calls[0][3]).toStrictEqual(ONLY_TAGS); // now we click on the FROM/cpudata button - await act(async () => { - userEvent.click(screen.getByRole('button', { name: 'cpudata' })); - }); + await userEvent.click(screen.getByRole('button', { name: 'cpudata' })); // verify `getTagValues` was called once, and in the tags-param we did not receive `field1` expect(mockedMeta.getAllMeasurementsForTags).toHaveBeenCalledTimes(1); diff --git a/public/app/plugins/datasource/jaeger/components/SearchForm.test.tsx b/public/app/plugins/datasource/jaeger/components/SearchForm.test.tsx index 77d85ac8526..f7059afb903 100644 --- a/public/app/plugins/datasource/jaeger/components/SearchForm.test.tsx +++ b/public/app/plugins/datasource/jaeger/components/SearchForm.test.tsx @@ -1,4 +1,4 @@ -import { act, render, screen, waitFor } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import { backendSrv } from 'app/core/services/backend_srv'; import { createFetchResponse } from 'test/helpers/createFetchResponse'; import { DataQueryRequest, DataSourceInstanceSettings, dateTime, PluginType } from '@grafana/data'; @@ -9,6 +9,7 @@ import React from 'react'; import SearchForm from './SearchForm'; import { testResponse } from '../testResponse'; import userEvent from '@testing-library/user-event'; +import { UserEvent } from '@testing-library/user-event/dist/types/setup'; describe('SearchForm', () => { it('should call the `onChange` function on click of the Input', async () => { @@ -38,7 +39,7 @@ describe('SearchForm', () => { const asyncServiceSelect = await waitFor(() => screen.getByRole('combobox', { name: 'select-service-name' })); expect(asyncServiceSelect).toBeInTheDocument(); - userEvent.click(asyncServiceSelect); + await userEvent.click(asyncServiceSelect); const jaegerService = await screen.findByText('jaeger-query'); expect(jaegerService).toBeInTheDocument(); @@ -68,13 +69,21 @@ describe('SearchForm', () => { }); describe('SearchForm', () => { + let user: UserEvent; + + beforeEach(() => { + jest.useFakeTimers(); + // Need to use delay: null here to work with fakeTimers + // see https://github.com/testing-library/user-event/issues/833 + user = userEvent.setup({ delay: null }); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + it('should show loader if there is a delay fetching options', async () => { - const promise = Promise.resolve(); - const handleOnChange = jest.fn(() => { - setTimeout(() => { - return promise; - }, 3000); - }); + const handleOnChange = jest.fn(); const query = { ...defaultQuery, targets: [ @@ -91,12 +100,19 @@ describe('SearchForm', () => { render(); + jest.spyOn(ds, 'metadataRequest').mockImplementation(() => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(['jaeger-query']); + }, 3000); + }); + }); const asyncServiceSelect = screen.getByRole('combobox', { name: 'select-service-name' }); - userEvent.click(asyncServiceSelect); - const loader = screen.getByText('Loading options...'); + await user.click(asyncServiceSelect); + expect(screen.getByText('Loading options...')).toBeInTheDocument(); - expect(loader).toBeInTheDocument(); - await act(() => promise); + jest.advanceTimersByTime(3000); + await waitFor(() => expect(screen.queryByText('Loading options...')).not.toBeInTheDocument()); }); }); diff --git a/public/app/plugins/datasource/loki/components/LokiLabelBrowser.test.tsx b/public/app/plugins/datasource/loki/components/LokiLabelBrowser.test.tsx index f64f2df2634..4f5468438f7 100644 --- a/public/app/plugins/datasource/loki/components/LokiLabelBrowser.test.tsx +++ b/public/app/plugins/datasource/loki/components/LokiLabelBrowser.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render, screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { createTheme } from '@grafana/data'; @@ -118,9 +118,9 @@ describe('LokiLabelBrowser', () => { }; // Clear label selection manually because it's saved in localStorage - afterEach(() => { + afterEach(async () => { const clearBtn = screen.getByLabelText('Selector clear button'); - userEvent.click(clearBtn); + await userEvent.click(clearBtn); }); it('renders and loader shows when empty, and then first set of labels', async () => { @@ -143,10 +143,10 @@ describe('LokiLabelBrowser', () => { const props = setupProps(); render(); // Selecting label2 - const label2 = await screen.findByRole('option', { name: /label2/, selected: false }); + const label2 = await screen.findByRole('option', { name: 'label2', selected: false }); expect(screen.queryByRole('list', { name: /Values/ })).not.toBeInTheDocument(); - userEvent.click(label2); - expect(screen.queryByRole('option', { name: /label2/, selected: true })).toBeInTheDocument(); + await userEvent.click(label2); + expect(screen.queryByRole('option', { name: 'label2', selected: true })).toBeInTheDocument(); // List of values for label2 appears expect(await screen.findAllByRole('list')).toHaveLength(1); expect(screen.queryByLabelText(/Values for/)).toHaveTextContent('label2'); @@ -154,24 +154,24 @@ describe('LokiLabelBrowser', () => { expect(screen.queryByRole('option', { name: 'value2-2' })).toBeInTheDocument(); expect(screen.queryByLabelText('selector')).toHaveTextContent('{}'); // Selecting label1, list for its values appears - const label1 = await screen.findByRole('option', { name: /label1/, selected: false }); - userEvent.click(label1); - expect(screen.queryByRole('option', { name: /label1/, selected: true })).toBeInTheDocument(); + const label1 = await screen.findByRole('option', { name: 'label1', selected: false }); + await userEvent.click(label1); + expect(screen.queryByRole('option', { name: 'label1', selected: true })).toBeInTheDocument(); await screen.findByLabelText('Values for label1'); expect(await screen.findAllByRole('list')).toHaveLength(2); // Selecting value2-2 of label2 const value = await screen.findByRole('option', { name: 'value2-2', selected: false }); - userEvent.click(value); + await userEvent.click(value); await screen.findByRole('option', { name: 'value2-2', selected: true }); expect(screen.queryByLabelText('selector')).toHaveTextContent('{label2="value2-2"}'); // Selecting value2-1 of label2, both values now selected const value2 = await screen.findByRole('option', { name: 'value2-1', selected: false }); - userEvent.click(value2); + await userEvent.click(value2); // await screen.findByRole('option', {name: 'value2-1', selected: true}); await screen.findByText('{label2=~"value2-1|value2-2"}'); // Deselecting value2-2, one value should remain const selectedValue = await screen.findByRole('option', { name: 'value2-2', selected: true }); - userEvent.click(selectedValue); + await userEvent.click(selectedValue); await screen.findByRole('option', { name: 'value2-1', selected: true }); await screen.findByRole('option', { name: 'value2-2', selected: false }); expect(screen.queryByLabelText('selector')).toHaveTextContent('{label2="value2-1"}'); @@ -183,28 +183,30 @@ describe('LokiLabelBrowser', () => { // Selecting label2 const label2 = await screen.findByRole('option', { name: /label2/, selected: false }); - userEvent.click(label2); + await userEvent.click(label2); // List of values for label2 appears - expect(await screen.findAllByRole('list')).toHaveLength(1); + expect(screen.queryByLabelText(/Values for/)).toHaveTextContent('label2'); + expect(screen.queryByRole('option', { name: 'value2-1' })).toBeInTheDocument(); + expect(screen.queryByRole('option', { name: 'value2-2' })).toBeInTheDocument(); expect(screen.queryByLabelText('selector')).toHaveTextContent('{}'); // Selecting label1, list for its values appears - const label1 = await screen.findByRole('option', { name: /label1/, selected: false }); - userEvent.click(label1); + const label1 = await screen.findByRole('option', { name: 'label1', selected: false }); + await userEvent.click(label1); await screen.findByLabelText('Values for label1'); expect(await screen.findAllByRole('list')).toHaveLength(2); // Selecting value2-1 of label2 const value2 = await screen.findByRole('option', { name: 'value2-1', selected: false }); - userEvent.click(value2); + await userEvent.click(value2); await screen.findByText('{label2="value2-1"}'); // Selecting value from label1 for combined selector const value1 = await screen.findByRole('option', { name: 'value1-2', selected: false }); - userEvent.click(value1); + await userEvent.click(value1); await screen.findByRole('option', { name: 'value1-2', selected: true }); await screen.findByText('{label1="value1-2",label2="value2-1"}'); // Deselect label1 should remove label and value const selectedLabel = (await screen.findAllByRole('option', { name: /label1/, selected: true }))[0]; - userEvent.click(selectedLabel); + await userEvent.click(selectedLabel); await screen.findByRole('option', { name: /label1/, selected: false }); expect(await screen.findAllByRole('list')).toHaveLength(1); expect(screen.queryByLabelText('selector')).toHaveTextContent('{label2="value2-1"}'); @@ -215,25 +217,27 @@ describe('LokiLabelBrowser', () => { render(); // Selecting label2 - const label2 = await screen.findByRole('option', { name: /label2/, selected: false }); - userEvent.click(label2); + const label2 = await screen.findByRole('option', { name: 'label2', selected: false }); + await userEvent.click(label2); // List of values for label2 appears - expect(await screen.findAllByRole('list')).toHaveLength(1); + expect(screen.queryByLabelText(/Values for/)).toHaveTextContent('label2'); + expect(screen.queryByRole('option', { name: 'value2-1' })).toBeInTheDocument(); + expect(screen.queryByRole('option', { name: 'value2-2' })).toBeInTheDocument(); expect(screen.queryByLabelText('selector')).toHaveTextContent('{}'); // Selecting label1, list for its values appears - const label1 = await screen.findByRole('option', { name: /label1/, selected: false }); - userEvent.click(label1); + const label1 = await screen.findByRole('option', { name: 'label1', selected: false }); + await userEvent.click(label1); await screen.findByLabelText('Values for label1'); expect(await screen.findAllByRole('list')).toHaveLength(2); // Selecting value2-1 of label2 const value2 = await screen.findByRole('option', { name: 'value2-1', selected: false }); - userEvent.click(value2); + await userEvent.click(value2); await screen.findByText('{label2="value2-1"}'); // Clear selector const clearBtn = screen.getByLabelText('Selector clear button'); - userEvent.click(clearBtn); - await screen.findByRole('option', { name: /label2/, selected: false }); + await userEvent.click(clearBtn); + await screen.findByRole('option', { name: 'label2', selected: false }); expect(screen.queryByLabelText('selector')).toHaveTextContent('{}'); }); @@ -242,14 +246,14 @@ describe('LokiLabelBrowser', () => { render(); // Selecting label2 and label1 const label2 = await screen.findByRole('option', { name: /label2/, selected: false }); - userEvent.click(label2); + await userEvent.click(label2); const label1 = await screen.findByRole('option', { name: /label1/, selected: false }); - userEvent.click(label1); + await userEvent.click(label1); await screen.findByLabelText('Values for label1'); await screen.findByLabelText('Values for label2'); expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(4); // Typing '1' to filter for values - userEvent.type(screen.getByLabelText('Filter expression for values'), 'val1'); + await userEvent.type(screen.getByLabelText('Filter expression for values'), 'val1'); expect(screen.getByLabelText('Filter expression for values')).toHaveValue('val1'); expect(screen.queryByRole('option', { name: 'value2-2' })).not.toBeInTheDocument(); expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(3); @@ -260,25 +264,25 @@ describe('LokiLabelBrowser', () => { render(); // Selecting label2 and label1 const label2 = await screen.findByRole('option', { name: /label2/, selected: false }); - userEvent.click(label2); + await userEvent.click(label2); const label1 = await screen.findByRole('option', { name: /label1/, selected: false }); - userEvent.click(label1); + await userEvent.click(label1); await screen.findByLabelText('Values for label1'); await screen.findByLabelText('Values for label2'); expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(4); expect(screen.queryByRole('option', { name: /label3/ })).toHaveTextContent('label3'); // Click value1-1 which triggers facetting for value3-x, and still show all value1-x const value1 = await screen.findByRole('option', { name: 'value1-1', selected: false }); - userEvent.click(value1); - await waitForElementToBeRemoved(screen.queryByRole('option', { name: 'value2-2' })); + await userEvent.click(value1); + await waitFor(() => expect(screen.queryByRole('option', { name: 'value2-2' })).not.toBeInTheDocument()); expect(screen.queryByRole('option', { name: 'value1-2' })).toBeInTheDocument(); expect(screen.queryByLabelText('selector')).toHaveTextContent('{label1="value1-1"}'); expect(screen.queryByRole('option', { name: /label3/ })).toHaveTextContent('label3 (1)'); // Click value1-2 for which facetting will allow more values for value3-x const value12 = await screen.findByRole('option', { name: 'value1-2', selected: false }); - userEvent.click(value12); + await userEvent.click(value12); await screen.findByRole('option', { name: 'value1-2', selected: true }); - userEvent.click(screen.getByRole('option', { name: /label3/ })); + await userEvent.click(screen.getByRole('option', { name: /label3/ })); await screen.findByLabelText('Values for label3'); expect(screen.queryByRole('option', { name: 'value1-1', selected: true })).toBeInTheDocument(); expect(screen.queryByRole('option', { name: 'value1-2', selected: true })).toBeInTheDocument(); diff --git a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.test.tsx b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.test.tsx index 09d3e069942..6d9177d979d 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.test.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.test.tsx @@ -15,10 +15,10 @@ describe('LokiQueryBuilder', () => { it('tries to load labels when no labels are selected', async () => { const { datasource } = setup(); datasource.languageProvider.fetchSeriesLabels = jest.fn().mockReturnValue({ job: ['a'], instance: ['b'] }); - userEvent.click(screen.getByLabelText('Add')); + await userEvent.click(screen.getByLabelText('Add')); const labels = screen.getByText(/Labels/); const selects = getAllByRole(labels.parentElement!.parentElement!.parentElement!, 'combobox'); - userEvent.click(selects[3]); + await userEvent.click(selects[3]); await waitFor(() => expect(screen.getByText('job')).toBeInTheDocument()); }); diff --git a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderContainer.test.tsx b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderContainer.test.tsx index 80717a06fea..599e88674a1 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderContainer.test.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderContainer.test.tsx @@ -30,7 +30,7 @@ describe('LokiQueryBuilderContainer', () => { }; render(); expect(screen.getByText('testjob')).toBeInTheDocument(); - addOperation('Range functions', 'Rate'); + await addOperation('Range functions', 'Rate'); expect(props.onChange).toBeCalledWith({ expr: 'rate({job="testjob"} [$__interval])', refId: 'A', diff --git a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.test.tsx b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.test.tsx index ae015f9d3fd..4690fb6e3eb 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.test.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.test.tsx @@ -25,7 +25,7 @@ describe('LokiQueryBuilderOptions', () => { screen.getByTitle('Click to edit options').click(); const element = screen.getByLabelText('Legend'); - userEvent.type(element, 'asd'); + await userEvent.type(element, 'asd'); fireEvent.keyDown(element, { key: 'Enter', code: 'Enter', charCode: 13 }); expect(props.onChange).toHaveBeenCalledWith({ diff --git a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryEditorSelector.test.tsx b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryEditorSelector.test.tsx index 23c9dae50fb..ed29bb1c83f 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryEditorSelector.test.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryEditorSelector.test.tsx @@ -73,7 +73,7 @@ describe('LokiQueryEditorSelector', () => { it('changes to builder mode', async () => { const { onChange } = renderWithMode(QueryEditorMode.Code); - switchToMode(QueryEditorMode.Builder); + await switchToMode(QueryEditorMode.Builder); expect(onChange).toBeCalledWith({ refId: 'A', expr: defaultQuery.expr, @@ -108,7 +108,7 @@ describe('LokiQueryEditorSelector', () => { it('changes to code mode', async () => { const { onChange } = renderWithMode(QueryEditorMode.Builder); - switchToMode(QueryEditorMode.Code); + await switchToMode(QueryEditorMode.Code); expect(onChange).toBeCalledWith({ refId: 'A', expr: defaultQuery.expr, @@ -119,7 +119,7 @@ describe('LokiQueryEditorSelector', () => { it('changes to explain mode', async () => { const { onChange } = renderWithMode(QueryEditorMode.Code); - switchToMode(QueryEditorMode.Explain); + await switchToMode(QueryEditorMode.Explain); expect(onChange).toBeCalledWith({ refId: 'A', expr: defaultQuery.expr, @@ -134,7 +134,7 @@ describe('LokiQueryEditorSelector', () => { expr: 'rate({instance="host.docker.internal:3000"}[$__interval])', editorMode: QueryEditorMode.Code, }); - switchToMode(QueryEditorMode.Builder); + await switchToMode(QueryEditorMode.Builder); rerender( { expect(queryByTestId(alertingTestIds.editor)).toBeNull(); }); - it('should not run query onBlur in explore', () => { + it('should not run query onBlur in explore', async () => { const { getByTestId, onRunQuery } = setup(CoreApp.Explore); const input = getByTestId('dummy-code-input'); expect(input).toBeInTheDocument(); - userEvent.type(input, 'metric'); + await userEvent.type(input, 'metric'); input.blur(); expect(onRunQuery).not.toHaveBeenCalled(); }); - it('should run query onBlur in dashboard', () => { + it('should run query onBlur in dashboard', async () => { const { getByTestId, onRunQuery } = setup(CoreApp.Dashboard); const input = getByTestId('dummy-code-input'); expect(input).toBeInTheDocument(); - userEvent.type(input, 'metric'); + await userEvent.type(input, 'metric'); input.blur(); expect(onRunQuery).toHaveBeenCalled(); }); diff --git a/public/app/plugins/datasource/prometheus/components/PrometheusMetricsBrowser.test.tsx b/public/app/plugins/datasource/prometheus/components/PrometheusMetricsBrowser.test.tsx index c79ea55a171..13ef5c7a4bf 100644 --- a/public/app/plugins/datasource/prometheus/components/PrometheusMetricsBrowser.test.tsx +++ b/public/app/plugins/datasource/prometheus/components/PrometheusMetricsBrowser.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render, screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { getTheme } from '@grafana/ui'; @@ -142,9 +142,9 @@ describe('PrometheusMetricsBrowser', () => { }; // Clear label selection manually because it's saved in localStorage - afterEach(() => { + afterEach(async () => { const clearBtn = screen.getByLabelText('Selector clear button'); - userEvent.click(clearBtn); + await userEvent.click(clearBtn); }); it('renders and loader shows when empty, and then first set of labels', async () => { @@ -167,35 +167,34 @@ describe('PrometheusMetricsBrowser', () => { const props = setupProps(); render(); // Selecting label2 - const label2 = await screen.findByRole('option', { name: /label2/, selected: false }); + const label2 = await screen.findByRole('option', { name: 'label2', selected: false }); expect(screen.queryByRole('list', { name: /Values/ })).not.toBeInTheDocument(); - userEvent.click(label2); - expect(screen.queryByRole('option', { name: /label2/, selected: true })).toBeInTheDocument(); + await userEvent.click(label2); + expect(screen.queryByRole('option', { name: 'label2', selected: true })).toBeInTheDocument(); // List of values for label2 appears - expect(await screen.findAllByRole('list')).toHaveLength(1); expect(screen.queryByLabelText(/Values for/)).toHaveTextContent('label2'); expect(screen.queryByRole('option', { name: 'value2-1' })).toBeInTheDocument(); expect(screen.queryByRole('option', { name: 'value2-2' })).toBeInTheDocument(); expect(screen.queryByLabelText('selector')).toHaveTextContent('{}'); // Selecting label1, list for its values appears - const label1 = await screen.findByRole('option', { name: /label1/, selected: false }); - userEvent.click(label1); - expect(screen.queryByRole('option', { name: /label1/, selected: true })).toBeInTheDocument(); + const label1 = await screen.findByRole('option', { name: 'label1', selected: false }); + await userEvent.click(label1); + expect(screen.queryByRole('option', { name: 'label1', selected: true })).toBeInTheDocument(); await screen.findByLabelText('Values for label1'); expect(await screen.findAllByRole('list', { name: /Values/ })).toHaveLength(2); // Selecting value2-2 of label2 const value = await screen.findByRole('option', { name: 'value2-2', selected: false }); - userEvent.click(value); + await userEvent.click(value); await screen.findByRole('option', { name: 'value2-2', selected: true }); expect(screen.queryByLabelText('selector')).toHaveTextContent('{label2="value2-2"}'); // Selecting value2-1 of label2, both values now selected const value2 = await screen.findByRole('option', { name: 'value2-1', selected: false }); - userEvent.click(value2); + await userEvent.click(value2); // await screen.findByRole('option', {name: 'value2-1', selected: true}); await screen.findByText('{label2=~"value2-1|value2-2"}'); // Deselecting value2-2, one value should remain const selectedValue = await screen.findByRole('option', { name: 'value2-2', selected: true }); - userEvent.click(selectedValue); + await userEvent.click(selectedValue); await screen.findByRole('option', { name: 'value2-1', selected: true }); await screen.findByRole('option', { name: 'value2-2', selected: false }); expect(screen.queryByLabelText('selector')).toHaveTextContent('{label2="value2-1"}'); @@ -206,29 +205,31 @@ describe('PrometheusMetricsBrowser', () => { render(); // Selecting label2 - const label2 = await screen.findByRole('option', { name: /label2/, selected: false }); - userEvent.click(label2); + const label2 = await screen.findByRole('option', { name: 'label2', selected: false }); + await userEvent.click(label2); // List of values for label2 appears - expect(await screen.findAllByRole('list')).toHaveLength(1); + expect(screen.queryByLabelText(/Values for/)).toHaveTextContent('label2'); + expect(screen.queryByRole('option', { name: 'value2-1' })).toBeInTheDocument(); + expect(screen.queryByRole('option', { name: 'value2-2' })).toBeInTheDocument(); expect(screen.queryByLabelText('selector')).toHaveTextContent('{}'); // Selecting label1, list for its values appears - const label1 = await screen.findByRole('option', { name: /label1/, selected: false }); - userEvent.click(label1); + const label1 = await screen.findByRole('option', { name: 'label1', selected: false }); + await userEvent.click(label1); await screen.findByLabelText('Values for label1'); expect(await screen.findAllByRole('list', { name: /Values/ })).toHaveLength(2); // Selecting value2-1 of label2 const value2 = await screen.findByRole('option', { name: 'value2-1', selected: false }); - userEvent.click(value2); + await userEvent.click(value2); await screen.findByText('{label2="value2-1"}'); // Selecting value from label1 for combined selector const value1 = await screen.findByRole('option', { name: 'value1-2', selected: false }); - userEvent.click(value1); + await userEvent.click(value1); await screen.findByRole('option', { name: 'value1-2', selected: true }); await screen.findByText('{label1="value1-2",label2="value2-1"}'); // Deselect label1 should remove label and value const selectedLabel = (await screen.findAllByRole('option', { name: /label1/, selected: true }))[0]; - userEvent.click(selectedLabel); + await userEvent.click(selectedLabel); await screen.findByRole('option', { name: /label1/, selected: false }); expect(await screen.findAllByRole('list', { name: /Values/ })).toHaveLength(1); expect(screen.queryByLabelText('selector')).toHaveTextContent('{label2="value2-1"}'); @@ -239,25 +240,27 @@ describe('PrometheusMetricsBrowser', () => { render(); // Selecting label2 - const label2 = await screen.findByRole('option', { name: /label2/, selected: false }); - userEvent.click(label2); + const label2 = await screen.findByRole('option', { name: 'label2', selected: false }); + await userEvent.click(label2); // List of values for label2 appears - expect(await screen.findAllByRole('list')).toHaveLength(1); + expect(screen.queryByLabelText(/Values for/)).toHaveTextContent('label2'); + expect(screen.queryByRole('option', { name: 'value2-1' })).toBeInTheDocument(); + expect(screen.queryByRole('option', { name: 'value2-2' })).toBeInTheDocument(); expect(screen.queryByLabelText('selector')).toHaveTextContent('{}'); // Selecting label1, list for its values appears - const label1 = await screen.findByRole('option', { name: /label1/, selected: false }); - userEvent.click(label1); + const label1 = await screen.findByRole('option', { name: 'label1', selected: false }); + await userEvent.click(label1); await screen.findByLabelText('Values for label1'); expect(await screen.findAllByRole('list', { name: /Values/ })).toHaveLength(2); // Selecting value2-1 of label2 const value2 = await screen.findByRole('option', { name: 'value2-1', selected: false }); - userEvent.click(value2); + await userEvent.click(value2); await screen.findByText('{label2="value2-1"}'); // Clear selector const clearBtn = screen.getByLabelText('Selector clear button'); - userEvent.click(clearBtn); - await screen.findByRole('option', { name: /label2/, selected: false }); + await userEvent.click(clearBtn); + await screen.findByRole('option', { name: 'label2', selected: false }); expect(screen.queryByLabelText('selector')).toHaveTextContent('{}'); }); @@ -266,14 +269,14 @@ describe('PrometheusMetricsBrowser', () => { render(); // Selecting label2 and label1 const label2 = await screen.findByRole('option', { name: /label2/, selected: false }); - userEvent.click(label2); + await userEvent.click(label2); const label1 = await screen.findByRole('option', { name: /label1/, selected: false }); - userEvent.click(label1); + await userEvent.click(label1); await screen.findByLabelText('Values for label1'); await screen.findByLabelText('Values for label2'); expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(4); // Typing '1' to filter for values - userEvent.type(screen.getByLabelText('Filter expression for label values'), '1'); + await userEvent.type(screen.getByLabelText('Filter expression for label values'), '1'); expect(screen.getByLabelText('Filter expression for label values')).toHaveValue('1'); expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(3); expect(screen.queryByRole('option', { name: 'value2-2' })).not.toBeInTheDocument(); @@ -284,26 +287,26 @@ describe('PrometheusMetricsBrowser', () => { render(); // Selecting label2 and label1 const label2 = await screen.findByRole('option', { name: /label2/, selected: false }); - userEvent.click(label2); + await userEvent.click(label2); const label1 = await screen.findByRole('option', { name: /label1/, selected: false }); - userEvent.click(label1); + await userEvent.click(label1); await screen.findByLabelText('Values for label1'); await screen.findByLabelText('Values for label2'); expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(4); expect(screen.queryByRole('option', { name: /label3/ })).toHaveTextContent('label3'); // Click value1-1 which triggers facetting for value3-x, and still show all value1-x const value1 = await screen.findByRole('option', { name: 'value1-1', selected: false }); - userEvent.click(value1); - await waitForElementToBeRemoved(screen.queryByRole('option', { name: 'value2-2' })); + await userEvent.click(value1); + await waitFor(() => expect(screen.queryByRole('option', { name: 'value2-2' })).not.toBeInTheDocument()); expect(screen.queryByRole('option', { name: 'value1-2' })).toBeInTheDocument(); expect(screen.queryByLabelText('selector')).toHaveTextContent('{label1="value1-1"}'); expect(screen.queryByRole('option', { name: /label3/ })).toHaveTextContent('label3 (1)'); // Click value1-2 for which facetting will allow more values for value3-x const value12 = await screen.findByRole('option', { name: 'value1-2', selected: false }); - userEvent.click(value12); + await userEvent.click(value12); await screen.findByRole('option', { name: 'value1-2', selected: true }); await screen.findByRole('option', { name: /label3/, selected: false }); - userEvent.click(screen.getByRole('option', { name: /label3/ })); + await userEvent.click(screen.getByRole('option', { name: /label3/ })); await screen.findByLabelText('Values for label3'); expect(screen.queryByRole('option', { name: 'value1-1', selected: true })).toBeInTheDocument(); expect(screen.queryByRole('option', { name: 'value1-2', selected: true })).toBeInTheDocument(); diff --git a/public/app/plugins/datasource/prometheus/querybuilder/components/MetricSelect.test.tsx b/public/app/plugins/datasource/prometheus/querybuilder/components/MetricSelect.test.tsx index 6dbfb42f8fb..6ae33d440fe 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/components/MetricSelect.test.tsx +++ b/public/app/plugins/datasource/prometheus/querybuilder/components/MetricSelect.test.tsx @@ -29,7 +29,7 @@ describe('MetricSelect', () => { render(); await openMetricSelect(); const input = screen.getByRole('combobox'); - userEvent.type(input, 'new'); + await userEvent.type(input, 'new'); await waitFor(() => expect(screen.getByText('Create: new')).toBeInTheDocument()); }); @@ -37,7 +37,7 @@ describe('MetricSelect', () => { render(); await openMetricSelect(); const input = screen.getByRole('combobox'); - userEvent.type(input, 'unique'); + await userEvent.type(input, 'unique'); await waitFor(() => expect(screen.getAllByLabelText('Select option')).toHaveLength(3)); }); @@ -45,7 +45,7 @@ describe('MetricSelect', () => { render(); await openMetricSelect(); const input = screen.getByRole('combobox'); - userEvent.type(input, 'more unique'); + await userEvent.type(input, 'more unique'); await waitFor(() => expect(screen.getAllByLabelText('Select option')).toHaveLength(2)); }); @@ -53,7 +53,7 @@ describe('MetricSelect', () => { render(); await openMetricSelect(); const input = screen.getByRole('combobox'); - userEvent.type(input, 'more unique metric'); + await userEvent.type(input, 'more unique metric'); await waitFor(() => expect(screen.getAllByLabelText('Select option')).toHaveLength(2)); }); @@ -61,7 +61,7 @@ describe('MetricSelect', () => { const { container } = render(); await openMetricSelect(); const input = screen.getByRole('combobox'); - userEvent.type(input, 'more'); + await userEvent.type(input, 'more'); await waitFor(() => expect(container.querySelectorAll('mark')).toHaveLength(1)); }); @@ -69,7 +69,7 @@ describe('MetricSelect', () => { const { container } = render(); await openMetricSelect(); const input = screen.getByRole('combobox'); - userEvent.type(input, 'more metric'); + await userEvent.type(input, 'more metric'); await waitFor(() => expect(container.querySelectorAll('mark')).toHaveLength(2)); }); @@ -77,7 +77,7 @@ describe('MetricSelect', () => { const { container } = render(); await openMetricSelect(); const input = screen.getByRole('combobox'); - userEvent.type(input, 'unique metric'); + await userEvent.type(input, 'unique metric'); await waitFor(() => expect(container.querySelectorAll('mark')).toHaveLength(4)); }); @@ -85,12 +85,12 @@ describe('MetricSelect', () => { const { container } = render(); await openMetricSelect(); const input = screen.getByRole('combobox'); - userEvent.type(input, 'new'); + await userEvent.type(input, 'new'); await waitFor(() => expect(container.querySelector('mark')).not.toBeInTheDocument()); }); }); async function openMetricSelect() { const select = await screen.getByText('Select metric').parentElement!; - userEvent.click(select); + await userEvent.click(select); } diff --git a/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilder.test.tsx b/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilder.test.tsx index 32c266025ab..ec4b47beade 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilder.test.tsx +++ b/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilder.test.tsx @@ -70,7 +70,7 @@ describe('PromQueryBuilder', () => { it('tries to load metrics without labels', async () => { const { languageProvider, container } = setup(); - openMetricSelect(container); + await openMetricSelect(container); await waitFor(() => expect(languageProvider.getLabelValues).toBeCalledWith('__name__')); }); @@ -79,27 +79,27 @@ describe('PromQueryBuilder', () => { ...defaultQuery, labels: [{ label: 'label_name', op: '=', value: 'label_value' }], }); - openMetricSelect(container); + await openMetricSelect(container); await waitFor(() => expect(languageProvider.getSeries).toBeCalledWith('{label_name="label_value"}', true)); }); it('tries to load variables in metric field', async () => { const { datasource, container } = setup(); datasource.getVariables = jest.fn().mockReturnValue([]); - openMetricSelect(container); + await openMetricSelect(container); await waitFor(() => expect(datasource.getVariables).toBeCalled()); }); it('tries to load labels when metric selected', async () => { const { languageProvider } = setup(); - openLabelNameSelect(); + await openLabelNameSelect(); await waitFor(() => expect(languageProvider.fetchSeriesLabels).toBeCalledWith('{__name__="random_metric"}')); }); it('tries to load variables in label field', async () => { const { datasource } = setup(); datasource.getVariables = jest.fn().mockReturnValue([]); - openLabelNameSelect(); + await openLabelNameSelect(); await waitFor(() => expect(datasource.getVariables).toBeCalled()); }); @@ -111,7 +111,7 @@ describe('PromQueryBuilder', () => { { label: 'foo', op: '=', value: 'bar' }, ], }); - openLabelNameSelect(1); + await openLabelNameSelect(1); await waitFor(() => expect(languageProvider.fetchSeriesLabels).toBeCalledWith('{label_name="label_value", __name__="random_metric"}') ); @@ -122,7 +122,7 @@ describe('PromQueryBuilder', () => { ...defaultQuery, metric: '', }); - openLabelNameSelect(); + await openLabelNameSelect(); await waitFor(() => expect(languageProvider.fetchLabels).toBeCalled()); }); @@ -132,8 +132,8 @@ describe('PromQueryBuilder', () => { labels: [], operations: [], }); - openMetricSelect(container); - userEvent.click(screen.getByText('histogram_metric_bucket')); + await openMetricSelect(container); + await userEvent.click(screen.getByText('histogram_metric_bucket')); await waitFor(() => expect(screen.getByText('hint: add histogram_quantile()')).toBeInTheDocument()); }); @@ -143,8 +143,8 @@ describe('PromQueryBuilder', () => { labels: [], operations: [], }); - openMetricSelect(container); - userEvent.click(screen.getByText('histogram_metric_sum')); + await openMetricSelect(container); + await userEvent.click(screen.getByText('histogram_metric_sum')); await waitFor(() => expect(screen.getByText('hint: add rate()')).toBeInTheDocument()); }); @@ -154,8 +154,8 @@ describe('PromQueryBuilder', () => { labels: [], operations: [], }); - openMetricSelect(container); - userEvent.click(screen.getByText('histogram_metric_sum')); + await openMetricSelect(container); + await userEvent.click(screen.getByText('histogram_metric_sum')); await waitFor(() => expect(screen.getByText('hint: add rate()')).toBeInTheDocument()); }); @@ -176,9 +176,9 @@ describe('PromQueryBuilder', () => { }, data ); - openMetricSelect(container); - userEvent.click(screen.getByText('histogram_metric_sum')); - await waitFor(() => expect(screen.getAllByText(/hint:/g)).toHaveLength(2)); + await openMetricSelect(container); + await userEvent.click(screen.getByText('histogram_metric_sum')); + await waitFor(() => expect(screen.getAllByText(/hint:/)).toHaveLength(2)); }); }); @@ -205,14 +205,14 @@ function setup(query: PromVisualQuery = defaultQuery, data?: PanelData) { return { languageProvider, datasource, container }; } -function openMetricSelect(container: HTMLElement) { +async function openMetricSelect(container: HTMLElement) { const select = container.querySelector('#prometheus-metric-select'); if (select) { - userEvent.click(select); + await userEvent.click(select); } } -function openLabelNameSelect(index = 0) { +async function openLabelNameSelect(index = 0) { const { name } = getLabelSelects(index); - userEvent.click(name); + await userEvent.click(name); } diff --git a/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilderContainer.test.tsx b/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilderContainer.test.tsx index 164ce6db537..09c75180021 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilderContainer.test.tsx +++ b/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilderContainer.test.tsx @@ -14,7 +14,7 @@ describe('PromQueryBuilderContainer', () => { const { props } = setup({ expr: 'rate(metric_test{job="testjob"}[$__rate_interval])' }); expect(screen.getByText('metric_test')).toBeInTheDocument(); - addOperation('Range functions', 'Rate'); + await addOperation('Range functions', 'Rate'); expect(props.onChange).toBeCalledWith({ expr: 'rate(metric_test{job="testjob"}[$__rate_interval])', refId: 'A', @@ -23,7 +23,7 @@ describe('PromQueryBuilderContainer', () => { it('Can add rest param', async () => { const { container } = setup({ expr: 'sum(ALERTS)' }); - userEvent.click(screen.getByTestId('operations.0.add-rest-param')); + await userEvent.click(screen.getByTestId('operations.0.add-rest-param')); waitFor(() => { expect(container.querySelector(`${getOperationParamId(0, 0)}`)).toBeInTheDocument(); diff --git a/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryEditorSelector.test.tsx b/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryEditorSelector.test.tsx index b1283286882..b2b64c7f516 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryEditorSelector.test.tsx +++ b/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryEditorSelector.test.tsx @@ -81,7 +81,7 @@ describe('PromQueryEditorSelector', () => { it('changes to builder mode', async () => { const { onChange } = renderWithMode(QueryEditorMode.Code); - switchToMode(QueryEditorMode.Builder); + await switchToMode(QueryEditorMode.Builder); expect(onChange).toBeCalledWith({ refId: 'A', expr: defaultQuery.expr, @@ -116,7 +116,7 @@ describe('PromQueryEditorSelector', () => { it('changes to code mode', async () => { const { onChange } = renderWithMode(QueryEditorMode.Builder); - switchToMode(QueryEditorMode.Code); + await switchToMode(QueryEditorMode.Code); expect(onChange).toBeCalledWith({ refId: 'A', expr: defaultQuery.expr, @@ -127,7 +127,7 @@ describe('PromQueryEditorSelector', () => { it('changes to explain mode', async () => { const { onChange } = renderWithMode(QueryEditorMode.Code); - switchToMode(QueryEditorMode.Explain); + await switchToMode(QueryEditorMode.Explain); expect(onChange).toBeCalledWith({ refId: 'A', expr: defaultQuery.expr, @@ -142,7 +142,7 @@ describe('PromQueryEditorSelector', () => { expr: 'rate(test_metric{instance="host.docker.internal:3000"}[$__interval])', editorMode: QueryEditorMode.Code, }); - switchToMode(QueryEditorMode.Builder); + await switchToMode(QueryEditorMode.Builder); rerender( { it('adds new label', async () => { const { onChange } = setup([{ label: 'foo', op: '=', value: 'bar' }]); - userEvent.click(getAddButton()); + await userEvent.click(getAddButton()); expect(screen.getAllByText(/Choose/)).toHaveLength(2); const { name, value } = getLabelSelects(1); await selectOptionInTest(name, 'baz'); @@ -44,7 +44,7 @@ describe('LabelFilters', () => { it('removes label', async () => { const { onChange } = setup([{ label: 'foo', op: '=', value: 'bar' }]); - userEvent.click(screen.getByLabelText(/remove/)); + await userEvent.click(screen.getByLabelText(/remove/)); expect(onChange).toBeCalledWith([]); }); diff --git a/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationList.test.tsx b/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationList.test.tsx index 197b2b22589..ac9ad278741 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationList.test.tsx +++ b/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationList.test.tsx @@ -36,7 +36,7 @@ describe('OperationList', () => { const { onChange } = setup(); const removeOperationButtons = screen.getAllByTitle('Remove operation'); expect(removeOperationButtons).toHaveLength(2); - userEvent.click(removeOperationButtons[1]); + await userEvent.click(removeOperationButtons[1]); expect(onChange).toBeCalledWith({ labels: [{ label: 'instance', op: '=', value: 'localhost:9090' }], metric: 'random_metric', @@ -46,7 +46,7 @@ describe('OperationList', () => { it('adds an operation', async () => { const { onChange } = setup(); - addOperation('Aggregations', 'Min'); + await addOperation('Aggregations', 'Min'); expect(onChange).toBeCalledWith({ labels: [{ label: 'instance', op: '=', value: 'localhost:9090' }], metric: 'random_metric', diff --git a/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationList.testUtils.ts b/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationList.testUtils.ts index 40440b72524..aabbc820ac1 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationList.testUtils.ts +++ b/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationList.testUtils.ts @@ -1,13 +1,13 @@ import { screen, fireEvent } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -export function addOperation(section: string, op: string) { +export async function addOperation(section: string, op: string) { const addOperationButton = screen.getByTitle('Add operation'); expect(addOperationButton).toBeInTheDocument(); - userEvent.click(addOperationButton); + await userEvent.click(addOperationButton); const sectionItem = screen.getByTitle(section); expect(sectionItem).toBeInTheDocument(); - // Weirdly the userEvent.click doesn't work here, it reports the item has pointer-events: none. Don't see that + // Weirdly the await userEvent.click doesn't work here, it reports the item has pointer-events: none. Don't see that // anywhere when debugging so not sure what style is it picking up. fireEvent.click(sectionItem.children[0]); const opItem = screen.getByTitle(op); diff --git a/public/app/plugins/datasource/tempo/QueryEditor/NativeSearch.test.tsx b/public/app/plugins/datasource/tempo/QueryEditor/NativeSearch.test.tsx index a321d0a322e..f8372122399 100644 --- a/public/app/plugins/datasource/tempo/QueryEditor/NativeSearch.test.tsx +++ b/public/app/plugins/datasource/tempo/QueryEditor/NativeSearch.test.tsx @@ -1,20 +1,25 @@ import NativeSearch from './NativeSearch'; import React from 'react'; -import { act, render, screen } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import { TempoDatasource, TempoQuery } from '../datasource'; import userEvent from '@testing-library/user-event'; +import { UserEvent } from '@testing-library/user-event/dist/types/setup'; const getOptions = jest.fn().mockImplementation(() => { - return Promise.resolve([ - { - value: 'customer', - label: 'customer', - }, - { - value: 'driver', - label: 'driver', - }, - ]); + return new Promise((resolve) => { + setTimeout(() => { + resolve([ + { + value: 'customer', + label: 'customer', + }, + { + value: 'driver', + label: 'driver', + }, + ]); + }, 1000); + }); }); jest.mock('../language_provider', () => { @@ -31,6 +36,36 @@ const mockQuery = { } as TempoQuery; describe('NativeSearch', () => { + let user: UserEvent; + + beforeEach(() => { + jest.useFakeTimers(); + // Need to use delay: null here to work with fakeTimers + // see https://github.com/testing-library/user-event/issues/833 + user = userEvent.setup({ delay: null }); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('should show loader when there is a delay', async () => { + render( + + ); + + const asyncServiceSelect = screen.getByRole('combobox', { name: 'select-span-name' }); + + await user.click(asyncServiceSelect); + const loader = screen.getByText('Loading options...'); + + expect(loader).toBeInTheDocument(); + + jest.advanceTimersByTime(1000); + + await waitFor(() => expect(screen.queryByText('Loading options...')).not.toBeInTheDocument()); + }); + it('should call the `onChange` function on click of the Input', async () => { const promise = Promise.resolve(); const handleOnChange = jest.fn(() => promise); @@ -54,56 +89,12 @@ describe('NativeSearch', () => { const asyncServiceSelect = await screen.findByRole('combobox', { name: 'select-span-name' }); expect(asyncServiceSelect).toBeInTheDocument(); - userEvent.click(asyncServiceSelect); + await user.click(asyncServiceSelect); + jest.advanceTimersByTime(1000); const driverOption = await screen.findByText('driver'); - userEvent.click(driverOption); + await user.click(driverOption); expect(handleOnChange).toHaveBeenCalledWith(fakeOptionChoice); }); }); - -describe('TempoLanguageProvider with delay', () => { - const getOptions2 = jest.fn().mockImplementation(() => { - return Promise.resolve([ - { - value: 'customer', - label: 'customer', - }, - { - value: 'driver', - label: 'driver', - }, - ]); - }); - - jest.mock('../language_provider', () => { - return jest.fn().mockImplementation(() => { - setTimeout(() => { - return { getOptions2 }; - }, 3000); - }); - }); - - it('should show loader', async () => { - const promise = Promise.resolve(); - const handleOnChange = jest.fn(() => promise); - - render( - {}} - /> - ); - - const asyncServiceSelect = screen.getByRole('combobox', { name: 'select-span-name' }); - - userEvent.click(asyncServiceSelect); - const loader = screen.getByText('Loading options...'); - - expect(loader).toBeInTheDocument(); - await act(() => promise); - }); -}); diff --git a/public/app/plugins/panel/annolist/AnnoListPanel.test.tsx b/public/app/plugins/panel/annolist/AnnoListPanel.test.tsx index 4ac712f23fe..e7ace69adda 100644 --- a/public/app/plugins/panel/annolist/AnnoListPanel.test.tsx +++ b/public/app/plugins/panel/annolist/AnnoListPanel.test.tsx @@ -200,7 +200,7 @@ describe('AnnoListPanel', () => { getMock.mockClear(); expect(screen.getByText(/result text/i)).toBeInTheDocument(); - userEvent.click(screen.getByText(/result text/i)); + await userEvent.click(screen.getByText(/result text/i)); await waitFor(() => expect(getMock).toHaveBeenCalledTimes(1)); expect(getMock).toHaveBeenCalledWith('/api/search', { dashboardIds: 14 }); @@ -215,7 +215,7 @@ describe('AnnoListPanel', () => { getMock.mockClear(); expect(screen.getByText('Result tag B')).toBeInTheDocument(); - userEvent.click(screen.getByText('Result tag B')); + await userEvent.click(screen.getByText('Result tag B')); expect(getMock).toHaveBeenCalledTimes(1); expect(getMock).toHaveBeenCalledWith( @@ -239,7 +239,7 @@ describe('AnnoListPanel', () => { getMock.mockClear(); expect(screen.getByRole('img')).toBeInTheDocument(); - userEvent.click(screen.getByRole('img')); + await userEvent.click(screen.getByRole('img')); expect(getMock).toHaveBeenCalledTimes(1); expect(getMock).toHaveBeenCalledWith( diff --git a/public/app/plugins/panel/nodeGraph/NodeGraph.test.tsx b/public/app/plugins/panel/nodeGraph/NodeGraph.test.tsx index e6bdf8380c7..c693cf8dbd4 100644 --- a/public/app/plugins/panel/nodeGraph/NodeGraph.test.tsx +++ b/public/app/plugins/panel/nodeGraph/NodeGraph.test.tsx @@ -3,7 +3,6 @@ import { render, screen, fireEvent, waitFor, getByText } from '@testing-library/ import userEvent from '@testing-library/user-event'; import { NodeGraph } from './NodeGraph'; import { makeEdgesDataFrame, makeNodesDataFrame } from './utils'; -import { act } from 'react-dom/test-utils'; jest.mock('react-use/lib/useMeasure', () => { return { @@ -32,9 +31,9 @@ describe('NodeGraph', () => { const zoomOut = await screen.findByTitle(/Zoom out/); expect(getScale()).toBe(1); - userEvent.click(zoomIn); + await userEvent.click(zoomIn); expect(getScale()).toBe(1.5); - userEvent.click(zoomOut); + await userEvent.click(zoomOut); expect(getScale()).toBe(1); }); @@ -84,17 +83,11 @@ describe('NodeGraph', () => { /> ); const node = await screen.findByLabelText(/Node: service:0/); - // This shows warning because there is no position for the click. We cannot add any because we use pageX/Y in the - // context menu which is experimental (but supported) property and userEvents does not seem to support that - act(() => { - userEvent.click(node); - }); + await userEvent.click(node); await screen.findByText(/Node traces/); const edge = await screen.findByLabelText(/Edge from/); - act(() => { - userEvent.click(edge); - }); + await userEvent.click(edge); await screen.findByText(/Edge traces/); }); @@ -183,9 +176,7 @@ describe('NodeGraph', () => { expect(node).toBeInTheDocument(); const marker = await screen.findByLabelText(/Hidden nodes marker: 3/); - act(() => { - userEvent.click(marker); - }); + await userEvent.click(marker); expect(screen.queryByLabelText(/Node: service:0/)).not.toBeInTheDocument(); expect(screen.getByLabelText(/Node: service:4/)).toBeInTheDocument(); @@ -210,7 +201,7 @@ describe('NodeGraph', () => { ); const button = await screen.findByTitle(/Grid layout/); - userEvent.click(button); + await userEvent.click(button); await expectNodePositionCloseTo('service:0', { x: -60, y: -60 }); await expectNodePositionCloseTo('service:1', { x: 60, y: -60 }); diff --git a/yarn.lock b/yarn.lock index 105b341fcfd..7c46cf3147b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3997,7 +3997,7 @@ __metadata: "@testing-library/jest-dom": 5.16.2 "@testing-library/react": 12.1.4 "@testing-library/react-hooks": 7.0.2 - "@testing-library/user-event": 13.5.0 + "@testing-library/user-event": 14.0.0 "@types/d3-interpolate": ^1.4.0 "@types/history": 4.7.11 "@types/jest": 27.4.1 @@ -4180,7 +4180,7 @@ __metadata: "@sentry/browser": 6.19.1 "@testing-library/dom": 8.13.0 "@testing-library/react": 12.1.4 - "@testing-library/user-event": ^13.5.0 + "@testing-library/user-event": 14.0.0 "@types/angular": 1.8.4 "@types/history": 4.7.11 "@types/jest": 27.4.1 @@ -4387,7 +4387,7 @@ __metadata: "@testing-library/jest-dom": 5.16.2 "@testing-library/react": 12.1.4 "@testing-library/react-hooks": 7.0.2 - "@testing-library/user-event": 13.5.0 + "@testing-library/user-event": 14.0.0 "@types/classnames": 2.3.0 "@types/common-tags": ^1.8.0 "@types/d3": 7.1.0 @@ -9030,7 +9030,7 @@ __metadata: languageName: node linkType: hard -"@testing-library/dom@npm:8.13.0": +"@testing-library/dom@npm:8.13.0, @testing-library/dom@npm:>=7, @testing-library/dom@npm:^8.0.0": version: 8.13.0 resolution: "@testing-library/dom@npm:8.13.0" dependencies: @@ -9046,22 +9046,6 @@ __metadata: languageName: node linkType: hard -"@testing-library/dom@npm:>=7, @testing-library/dom@npm:^8.0.0": - version: 8.10.1 - resolution: "@testing-library/dom@npm:8.10.1" - dependencies: - "@babel/code-frame": ^7.10.4 - "@babel/runtime": ^7.12.5 - "@types/aria-query": ^4.2.0 - aria-query: ^5.0.0 - chalk: ^4.1.0 - dom-accessibility-api: ^0.5.9 - lz-string: ^1.4.4 - pretty-format: ^27.0.2 - checksum: c11da16d981d4479fa18b2e90a62d2c19871da414016a4f80136394cf35e833ca4de4b56a64225f6bd5d3a13842176236d3508d217e3858c5e2ef35a3f391d5b - languageName: node - linkType: hard - "@testing-library/jest-dom@npm:5.16.2": version: 5.16.2 resolution: "@testing-library/jest-dom@npm:5.16.2" @@ -9115,14 +9099,12 @@ __metadata: languageName: node linkType: hard -"@testing-library/user-event@npm:13.5.0, @testing-library/user-event@npm:^13.5.0": - version: 13.5.0 - resolution: "@testing-library/user-event@npm:13.5.0" - dependencies: - "@babel/runtime": ^7.12.5 +"@testing-library/user-event@npm:14.0.0": + version: 14.0.0 + resolution: "@testing-library/user-event@npm:14.0.0" peerDependencies: "@testing-library/dom": ">=7.21.4" - checksum: 16319de685fbb7008f1ba667928f458b2d08196918002daca56996de80ef35e6d9de26e9e1ece7d00a004692b95a597cf9142fff0dc53f2f51606a776584f549 + checksum: 171a40e680616e6a34299f9fb783dcadcd36f255256a53fe06cb71f4149715d8e5b84e65ea1c144af179f2e4b1799af92c79a8fc0c4c0ee2e94a713f2c476efd languageName: node linkType: hard @@ -20472,7 +20454,7 @@ __metadata: "@testing-library/jest-dom": 5.16.2 "@testing-library/react": 12.1.4 "@testing-library/react-hooks": 7.0.2 - "@testing-library/user-event": 13.5.0 + "@testing-library/user-event": 14.0.0 "@types/angular": 1.8.4 "@types/angular-route": 1.7.2 "@types/classnames": 2.3.0