Upgrade @testing-library/user-event to v14 (#47898)

* Update dependency @testing-library/user-event to v14

* everything is async...

* everything is async pt.2

* Fix cascader tests

* hack the yarn.lock file to remove the old version of @testing-library/dom

* some more fixes!

* MOAR FIXES

* more fixes

* remove a bunch of places where we're wrapping in act()

* down to 7 failing tests...

* Fix arrow tests

* Fix rest of NavBarItem tests

* Fix last tests

* Use {Enter} instead of {enter}

* Revert "Use {Enter} instead of {enter}"

This reverts commit e72453bb52.

* remove some unused act imports

* Fix LibraryPanelsSearch tests

* more stable test

* More consistent test...

Co-authored-by: Renovate Bot <bot@renovateapp.com>
This commit is contained in:
Ashley Harrison
2022-04-21 13:15:21 +01:00
committed by GitHub
parent 9ed7e48454
commit d832bde270
89 changed files with 900 additions and 912 deletions

View File

@@ -113,7 +113,7 @@
"@testing-library/jest-dom": "5.16.2", "@testing-library/jest-dom": "5.16.2",
"@testing-library/react": "12.1.4", "@testing-library/react": "12.1.4",
"@testing-library/react-hooks": "7.0.2", "@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": "1.8.4",
"@types/angular-route": "1.7.2", "@types/angular-route": "1.7.2",
"@types/classnames": "2.3.0", "@types/classnames": "2.3.0",

View File

@@ -51,7 +51,7 @@
"@testing-library/jest-dom": "5.16.2", "@testing-library/jest-dom": "5.16.2",
"@testing-library/react": "12.1.4", "@testing-library/react": "12.1.4",
"@testing-library/react-hooks": "7.0.2", "@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/history": "4.7.11",
"@types/jest": "27.4.1", "@types/jest": "27.4.1",
"@types/jquery": "3.5.14", "@types/jquery": "3.5.14",

View File

@@ -40,7 +40,7 @@
"@rollup/plugin-node-resolve": "13.1.3", "@rollup/plugin-node-resolve": "13.1.3",
"@testing-library/dom": "8.13.0", "@testing-library/dom": "8.13.0",
"@testing-library/react": "12.1.4", "@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/angular": "1.8.4",
"@types/history": "4.7.11", "@types/history": "4.7.11",
"@types/jest": "27.4.1", "@types/jest": "27.4.1",

View File

@@ -12,7 +12,7 @@ describe('DataSourcePicker', () => {
const select = render(<DataSourcePicker onClear={onClear} />); const select = render(<DataSourcePicker onClear={onClear} />);
const clearButton = select.getByLabelText('select-clear-value'); const clearButton = select.getByLabelText('select-clear-value');
userEvent.click(clearButton); await userEvent.click(clearButton);
expect(onClear).toHaveBeenCalled(); expect(onClear).toHaveBeenCalled();
}); });

View File

@@ -118,7 +118,7 @@
"@testing-library/jest-dom": "5.16.2", "@testing-library/jest-dom": "5.16.2",
"@testing-library/react": "12.1.4", "@testing-library/react": "12.1.4",
"@testing-library/react-hooks": "7.0.2", "@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/classnames": "2.3.0",
"@types/common-tags": "^1.8.0", "@types/common-tags": "^1.8.0",
"@types/d3": "7.1.0", "@types/d3": "7.1.0",

View File

@@ -1,7 +1,8 @@
import React from 'react'; import React from 'react';
import { Cascader, CascaderOption, CascaderProps } from './Cascader'; import { Cascader, CascaderOption, CascaderProps } from './Cascader';
import { act, render, screen } from '@testing-library/react'; 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 = [ const options = [
{ {
@@ -45,78 +46,86 @@ describe('Cascader', () => {
const placeholder = 'cascader-placeholder'; const placeholder = 'cascader-placeholder';
describe('options from state change', () => { describe('options from state change', () => {
let user: UserEvent;
beforeEach(() => { beforeEach(() => {
jest.useFakeTimers('modern'); 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(<CascaderWithOptionsStateUpdate placeholder={placeholder} onSelect={jest.fn()} />); render(<CascaderWithOptionsStateUpdate placeholder={placeholder} onSelect={jest.fn()} />);
act(() => { await user.click(screen.getByPlaceholderText(placeholder));
userEvent.click(screen.getByPlaceholderText(placeholder));
});
expect(screen.getByText('Initial state option')).toBeInTheDocument(); expect(screen.getByText('Initial state option')).toBeInTheDocument();
expect(screen.queryByText('First')).not.toBeInTheDocument(); expect(screen.queryByText('First')).not.toBeInTheDocument();
act(() => { act(() => {
jest.runAllTimers(); jest.runAllTimers();
userEvent.click(screen.getByPlaceholderText(placeholder));
}); });
await user.click(screen.getByPlaceholderText(placeholder));
expect(screen.queryByText('Initial state option')).not.toBeInTheDocument(); expect(screen.queryByText('Initial state option')).not.toBeInTheDocument();
expect(screen.getByText('First')).toBeInTheDocument(); expect(screen.getByText('First')).toBeInTheDocument();
}); });
it('filters updated results when searching', () => { it('filters updated results when searching', async () => {
render(<CascaderWithOptionsStateUpdate placeholder={placeholder} onSelect={jest.fn()} />); render(<CascaderWithOptionsStateUpdate placeholder={placeholder} onSelect={jest.fn()} />);
act(() => { act(() => {
jest.runAllTimers(); jest.runAllTimers();
}); });
userEvent.type(screen.getByPlaceholderText(placeholder), 'Third'); await user.type(screen.getByPlaceholderText(placeholder), 'Third');
expect(screen.queryByText('Second')).not.toBeInTheDocument(); expect(screen.queryByText('Second')).not.toBeInTheDocument();
expect(screen.getByText('First / Third')).toBeInTheDocument(); expect(screen.getByText('First / Third')).toBeInTheDocument();
}); });
}); });
it('filters results when searching', () => { it('filters results when searching', async () => {
render(<Cascader placeholder={placeholder} options={options} onSelect={jest.fn()} />); render(<Cascader placeholder={placeholder} options={options} onSelect={jest.fn()} />);
userEvent.type(screen.getByPlaceholderText(placeholder), 'Third'); await userEvent.type(screen.getByPlaceholderText(placeholder), 'Third');
expect(screen.queryByText('Second')).not.toBeInTheDocument(); expect(screen.queryByText('Second')).not.toBeInTheDocument();
expect(screen.getByText('First / Third')).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( render(
<Cascader displayAllSelectedLevels={true} placeholder={placeholder} options={options} onSelect={jest.fn()} /> <Cascader displayAllSelectedLevels={true} placeholder={placeholder} options={options} onSelect={jest.fn()} />
); );
userEvent.type(screen.getByPlaceholderText(placeholder), 'Third'); await userEvent.type(screen.getByPlaceholderText(placeholder), 'Third');
userEvent.click(screen.getByText('First / Third')); await userEvent.click(screen.getByText('First / Third'));
expect(screen.getByDisplayValue('First / Third')).toBeInTheDocument(); 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( render(
<Cascader displayAllSelectedLevels={true} placeholder={placeholder} options={options} onSelect={() => {}} /> <Cascader displayAllSelectedLevels={true} placeholder={placeholder} options={options} onSelect={() => {}} />
); );
expect(screen.queryByDisplayValue('First/Second')).not.toBeInTheDocument(); 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 // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed
userEvent.click(screen.getByText('First'), undefined, { skipPointerEventsCheck: true }); await userEvent.click(screen.getByText('First'), { pointerEventsCheck: PointerEventsCheckLevel.Never });
userEvent.click(screen.getByText('Second'), undefined, { skipPointerEventsCheck: true }); await userEvent.click(screen.getByText('Second'), { pointerEventsCheck: PointerEventsCheckLevel.Never });
expect(screen.getByDisplayValue('First/Second')).toBeInTheDocument(); 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 = ','; const separator = ',';
render( render(
@@ -131,34 +140,34 @@ describe('Cascader', () => {
expect(screen.queryByDisplayValue('First/Second')).not.toBeInTheDocument(); 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 // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed
userEvent.click(screen.getByText('First'), undefined, { skipPointerEventsCheck: true }); await userEvent.click(screen.getByText('First'), { pointerEventsCheck: PointerEventsCheckLevel.Never });
userEvent.click(screen.getByText('Second'), undefined, { skipPointerEventsCheck: true }); await userEvent.click(screen.getByText('Second'), { pointerEventsCheck: PointerEventsCheckLevel.Never });
expect(screen.getByDisplayValue(`First${separator}Second`)).toBeInTheDocument(); 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( render(
<Cascader displayAllSelectedLevels={false} placeholder={placeholder} options={options} onSelect={jest.fn()} /> <Cascader displayAllSelectedLevels={false} placeholder={placeholder} options={options} onSelect={jest.fn()} />
); );
userEvent.click(screen.getByPlaceholderText(placeholder)); await userEvent.click(screen.getByPlaceholderText(placeholder));
// TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed
userEvent.click(screen.getByText('First'), undefined, { skipPointerEventsCheck: true }); await userEvent.click(screen.getByText('First'), { pointerEventsCheck: PointerEventsCheckLevel.Never });
userEvent.click(screen.getByText('Second'), undefined, { skipPointerEventsCheck: true }); await userEvent.click(screen.getByText('Second'), { pointerEventsCheck: PointerEventsCheckLevel.Never });
expect(screen.getByDisplayValue('Second')).toBeInTheDocument(); 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(<Cascader placeholder={placeholder} options={options} onSelect={jest.fn()} />); render(<Cascader placeholder={placeholder} options={options} onSelect={jest.fn()} />);
userEvent.click(screen.getByPlaceholderText(placeholder)); await userEvent.click(screen.getByPlaceholderText(placeholder));
// TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed
userEvent.click(screen.getByText('First'), undefined, { skipPointerEventsCheck: true }); await userEvent.click(screen.getByText('First'), { pointerEventsCheck: PointerEventsCheckLevel.Never });
userEvent.click(screen.getByText('Second'), undefined, { skipPointerEventsCheck: true }); await userEvent.click(screen.getByText('Second'), { pointerEventsCheck: PointerEventsCheckLevel.Never });
expect(screen.getByDisplayValue('Second')).toBeInTheDocument(); expect(screen.getByDisplayValue('Second')).toBeInTheDocument();
}); });

View File

@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { act, render, screen } from '@testing-library/react'; import { render, screen } from '@testing-library/react';
import { ColorPickerPopover } from './ColorPickerPopover'; import { ColorPickerPopover } from './ColorPickerPopover';
import { createTheme } from '@grafana/data'; import { createTheme } from '@grafana/data';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
@@ -7,24 +7,20 @@ import userEvent from '@testing-library/user-event';
describe('ColorPickerPopover', () => { describe('ColorPickerPopover', () => {
const theme = createTheme(); const theme = createTheme();
it('should be tabbable', () => { it('should be tabbable', async () => {
render(<ColorPickerPopover color={'red'} onChange={() => {}} />); render(<ColorPickerPopover color={'red'} onChange={() => {}} />);
const color = screen.getByRole('button', { name: 'dark-red color' }); const color = screen.getByRole('button', { name: 'dark-red color' });
const customTab = screen.getByRole('button', { name: 'Custom' }); const customTab = screen.getByRole('button', { name: 'Custom' });
act(() => { await userEvent.tab();
userEvent.tab();
});
expect(customTab).toHaveFocus(); expect(customTab).toHaveFocus();
act(() => { await userEvent.tab();
userEvent.tab();
});
expect(color).toHaveFocus(); expect(color).toHaveFocus();
}); });
describe('rendering', () => { 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(<ColorPickerPopover color={'green'} onChange={() => {}} />); render(<ColorPickerPopover color={'green'} onChange={() => {}} />);
const color = screen.getByRole('button', { name: 'green color' }); const color = screen.getByRole('button', { name: 'green color' });
const colorSwatchWrapper = screen.getAllByTestId('data-testid-colorswatch'); const colorSwatchWrapper = screen.getAllByTestId('data-testid-colorswatch');
@@ -32,9 +28,7 @@ describe('ColorPickerPopover', () => {
expect(color).toBeInTheDocument(); expect(color).toBeInTheDocument();
expect(colorSwatchWrapper[0]).toBeInTheDocument(); expect(colorSwatchWrapper[0]).toBeInTheDocument();
act(() => { await userEvent.click(colorSwatchWrapper[0]);
userEvent.click(colorSwatchWrapper[0]);
});
expect(color).toHaveStyle('box-shadow: inset 0 0 0 2px #73BF69,inset 0 0 0 4px #000000'); 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', () => { describe('named colors support', () => {
const onChangeSpy = jest.fn(); 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(<ColorPickerPopover color={'red'} onChange={onChangeSpy} />); render(<ColorPickerPopover color={'red'} onChange={onChangeSpy} />);
const color = screen.getByRole('button', { name: 'red color' }); const color = screen.getByRole('button', { name: 'red color' });
act(() => { await userEvent.click(color);
userEvent.click(color);
});
expect(onChangeSpy).toBeCalledTimes(1); expect(onChangeSpy).toBeCalledTimes(1);
expect(onChangeSpy).toBeCalledWith(theme.visualization.getColorByName('red')); 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(<ColorPickerPopover color={'red'} enableNamedColors onChange={onChangeSpy} />); render(<ColorPickerPopover color={'red'} enableNamedColors onChange={onChangeSpy} />);
const color = screen.getByRole('button', { name: 'red color' }); const color = screen.getByRole('button', { name: 'red color' });
act(() => { await userEvent.click(color);
userEvent.click(color);
});
expect(onChangeSpy).toBeCalledTimes(2); expect(onChangeSpy).toBeCalledTimes(2);
expect(onChangeSpy).toBeCalledWith(theme.visualization.getColorByName('red')); expect(onChangeSpy).toBeCalledWith(theme.visualization.getColorByName('red'));

View File

@@ -6,7 +6,7 @@ import { ConfirmButton } from './ConfirmButton';
import { expect } from '../../../../../public/test/lib/common'; import { expect } from '../../../../../public/test/lib/common';
describe('ConfirmButton', () => { describe('ConfirmButton', () => {
it('should show confirm delete when clicked', () => { it('should show confirm delete when clicked', async () => {
const onConfirm = jest.fn(); const onConfirm = jest.fn();
render( render(
<ConfirmButton confirmText="Confirm delete" onConfirm={onConfirm}> <ConfirmButton confirmText="Confirm delete" onConfirm={onConfirm}>
@@ -17,18 +17,18 @@ describe('ConfirmButton', () => {
// Confirm button should not be visible before clicking the Delete button // Confirm button should not be visible before clicking the Delete button
expect(screen.queryByRole('button', { name: 'Confirm delete' })).not.toBeInTheDocument(); 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 // Confirm button should now be visible
expect(screen.getByRole('button', { name: 'Confirm delete' })).toBeInTheDocument(); 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(); expect(onConfirm).toHaveBeenCalled();
// Confirm button should be visible if closeOnConfirm is false // Confirm button should be visible if closeOnConfirm is false
expect(screen.queryByRole('button', { name: 'Confirm delete' })).toBeInTheDocument(); 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( render(
<ConfirmButton confirmText="Confirm delete" onConfirm={() => {}} closeOnConfirm={true}> <ConfirmButton confirmText="Confirm delete" onConfirm={() => {}} closeOnConfirm={true}>
Delete Delete
@@ -38,17 +38,17 @@ describe('ConfirmButton', () => {
// Confirm button should not be visible before clicking the Delete button // Confirm button should not be visible before clicking the Delete button
expect(screen.queryByRole('button', { name: 'Confirm delete' })).not.toBeInTheDocument(); 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 // Confirm button should now be visible
expect(screen.getByRole('button', { name: 'Confirm delete' })).toBeInTheDocument(); 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 // Confirm button should not be visible if closeOnConfirm is true
expect(screen.queryByRole('button', { name: 'Confirm delete' })).not.toBeInTheDocument(); 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(); const onCancel = jest.fn();
render( render(
<ConfirmButton confirmText="Confirm delete" onCancel={onCancel} onConfirm={() => {}}> <ConfirmButton confirmText="Confirm delete" onCancel={onCancel} onConfirm={() => {}}>
@@ -59,11 +59,11 @@ describe('ConfirmButton', () => {
// Cancel button should not be visible before clicking the Delete button // Cancel button should not be visible before clicking the Delete button
expect(screen.queryByRole('button', { name: 'Cancel' })).not.toBeInTheDocument(); 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 // Cancel button should now be visible
expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument(); 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(); expect(onCancel).toHaveBeenCalled();
// Cancel button should not be visible after click // Cancel button should not be visible after click

View File

@@ -54,13 +54,13 @@ function assertRowCount(configuredInputCount: number, passwordInputCount: number
} }
describe('Render', () => { describe('Render', () => {
it('should add a new header', () => { it('should add a new header', async () => {
setup(); setup();
const b = screen.getByRole('button', { name: 'Add header' }); const b = screen.getByRole('button', { name: 'Add header' });
expect(b).toBeInTheDocument(); expect(b).toBeInTheDocument();
assertRowCount(0, 0); assertRowCount(0, 0);
userEvent.click(b); await userEvent.click(b);
assertRowCount(0, 1); assertRowCount(0, 1);
}); });
@@ -71,7 +71,7 @@ describe('Render', () => {
expect(b.getAttribute('type')).toBe('button'); expect(b.getAttribute('type')).toBe('button');
}); });
it('should remove a header', () => { it('should remove a header', async () => {
const { onChange } = setup({ const { onChange } = setup({
dataSourceConfig: { dataSourceConfig: {
jsonData: { jsonData: {
@@ -87,14 +87,14 @@ describe('Render', () => {
assertRowCount(1, 0); assertRowCount(1, 0);
userEvent.click(b); await userEvent.click(b);
assertRowCount(0, 0); assertRowCount(0, 0);
expect(onChange).toHaveBeenCalledTimes(1); expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange.mock.calls[0][0].jsonData).toStrictEqual({}); 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({ const { onChange } = setup({
dataSourceConfig: { dataSourceConfig: {
jsonData: { jsonData: {
@@ -109,7 +109,7 @@ describe('Render', () => {
// we remove the row // we remove the row
const removeButton = screen.getByRole('button', { name: 'Remove header' }); const removeButton = screen.getByRole('button', { name: 'Remove header' });
expect(removeButton).toBeInTheDocument(); expect(removeButton).toBeInTheDocument();
userEvent.click(removeButton); await userEvent.click(removeButton);
assertRowCount(0, 0); assertRowCount(0, 0);
expect(onChange).toHaveBeenCalled(); expect(onChange).toHaveBeenCalled();
@@ -119,7 +119,7 @@ describe('Render', () => {
expect(lastCall[0].secureJsonData).not.toHaveProperty('httpHeaderValue1'); expect(lastCall[0].secureJsonData).not.toHaveProperty('httpHeaderValue1');
}); });
it('should reset a header', () => { it('should reset a header', async () => {
setup({ setup({
dataSourceConfig: { dataSourceConfig: {
jsonData: { jsonData: {
@@ -135,7 +135,7 @@ describe('Render', () => {
expect(b).toBeInTheDocument(); expect(b).toBeInTheDocument();
assertRowCount(1, 0); assertRowCount(1, 0);
userEvent.click(b); await userEvent.click(b);
assertRowCount(0, 1); assertRowCount(0, 1);
}); });
}); });

View File

@@ -33,7 +33,7 @@ describe('<SecretInput />', () => {
expect(screen.queryByRole('button', { name: RESET_BUTTON_TEXT })).toBeInTheDocument(); 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(); const onReset = jest.fn();
render(<SecretInput isConfigured={true} onChange={() => {}} onReset={onReset} placeholder={PLACEHOLDER_TEXT} />); render(<SecretInput isConfigured={true} onChange={() => {}} onReset={onReset} placeholder={PLACEHOLDER_TEXT} />);
@@ -43,12 +43,12 @@ describe('<SecretInput />', () => {
expect(screen.queryByRole('button', { name: RESET_BUTTON_TEXT })).toBeInTheDocument(); expect(screen.queryByRole('button', { name: RESET_BUTTON_TEXT })).toBeInTheDocument();
// Click on "Reset" // 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); 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(); const onChange = jest.fn();
render(<SecretInput isConfigured={false} onChange={onChange} onReset={() => {}} placeholder={PLACEHOLDER_TEXT} />); render(<SecretInput isConfigured={false} onChange={onChange} onReset={() => {}} placeholder={PLACEHOLDER_TEXT} />);
@@ -57,7 +57,7 @@ describe('<SecretInput />', () => {
expect(input).toHaveValue(''); expect(input).toHaveValue('');
userEvent.type(input, 'Foo'); await userEvent.type(input, 'Foo');
expect(onChange).toHaveBeenCalled(); expect(onChange).toHaveBeenCalled();
expect(input).toHaveValue('Foo'); expect(input).toHaveValue('Foo');

View File

@@ -22,9 +22,9 @@ describe('SelectBase', () => {
render(<SelectBase menuShouldPortal onChange={onChangeHandler} />); render(<SelectBase menuShouldPortal onChange={onChangeHandler} />);
}); });
it('renders empty options information', () => { it('renders empty options information', async () => {
render(<SelectBase menuShouldPortal onChange={onChangeHandler} />); render(<SelectBase menuShouldPortal onChange={onChangeHandler} />);
userEvent.click(screen.getByText(/choose/i)); await userEvent.click(screen.getByText(/choose/i));
expect(screen.queryByText(/no options found/i)).toBeVisible(); expect(screen.queryByText(/no options found/i)).toBeVisible();
}); });
@@ -54,7 +54,7 @@ describe('SelectBase', () => {
render(<Test />); render(<Test />);
expect(screen.queryByText('Test label')).toBeInTheDocument(); 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(); expect(screen.queryByText('Test label')).not.toBeInTheDocument();
}); });
@@ -186,9 +186,9 @@ describe('SelectBase', () => {
}); });
describe('options', () => { describe('options', () => {
it('renders menu with provided options', () => { it('renders menu with provided options', async () => {
render(<SelectBase menuShouldPortal options={options} onChange={onChangeHandler} />); render(<SelectBase menuShouldPortal options={options} onChange={onChangeHandler} />);
userEvent.click(screen.getByText(/choose/i)); await userEvent.click(screen.getByText(/choose/i));
const menuOptions = screen.getAllByLabelText('Select option'); const menuOptions = screen.getAllByLabelText('Select option');
expect(menuOptions).toHaveLength(2); expect(menuOptions).toHaveLength(2);
}); });

View File

@@ -159,11 +159,11 @@ describe('Table', () => {
}); });
describe('when sorting with column header', () => { describe('when sorting with column header', () => {
it('then correct rows should be rendered', () => { it('then correct rows should be rendered', async () => {
getTestContext(); getTestContext();
userEvent.click(within(getColumnHeader(/temperature/)).getByText(/temperature/i)); await 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));
const rows = within(getTable()).getAllByRole('row'); const rows = within(getTable()).getAllByRole('row');
expect(rows).toHaveLength(5); expect(rows).toHaveLength(5);

View File

@@ -48,11 +48,11 @@ describe('ChangePassword Page', () => {
expect(await screen.findByText('New Password is required')).toBeInTheDocument(); expect(await screen.findByText('New Password is required')).toBeInTheDocument();
expect(screen.getByText('Confirmed Password is required')).toBeInTheDocument(); expect(screen.getByText('Confirmed Password is required')).toBeInTheDocument();
userEvent.type(screen.getByLabelText('New password'), 'admin'); await userEvent.type(screen.getByLabelText('New password'), 'admin');
userEvent.type(screen.getByLabelText('Confirm new password'), 'a'); await userEvent.type(screen.getByLabelText('Confirm new password'), 'a');
await waitFor(() => expect(screen.getByText('Passwords must match!')).toBeInTheDocument()); 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()); await waitFor(() => expect(screen.queryByText('Passwords must match!')).not.toBeInTheDocument());
}); });
it('should navigate to default url if change password is successful', async () => { it('should navigate to default url if change password is successful', async () => {
@@ -64,8 +64,8 @@ describe('ChangePassword Page', () => {
postMock.mockResolvedValueOnce({ message: 'Logged in' }); postMock.mockResolvedValueOnce({ message: 'Logged in' });
render(<ChangePasswordPage {...props} />); render(<ChangePasswordPage {...props} />);
userEvent.type(screen.getByLabelText('New password'), 'test'); await userEvent.type(screen.getByLabelText('New password'), 'test');
userEvent.type(screen.getByLabelText('Confirm new password'), 'test'); await userEvent.type(screen.getByLabelText('Confirm new password'), 'test');
fireEvent.click(screen.getByRole('button', { name: 'Submit' })); fireEvent.click(screen.getByRole('button', { name: 'Submit' }));
await waitFor(() => await waitFor(() =>
expect(postMock).toHaveBeenCalledWith('/api/user/password/reset', { expect(postMock).toHaveBeenCalledWith('/api/user/password/reset', {

View File

@@ -41,14 +41,14 @@ describe('VerifyEmail Page', () => {
fireEvent.click(screen.getByRole('button', { name: 'Send reset email' })); fireEvent.click(screen.getByRole('button', { name: 'Send reset email' }));
expect(await screen.findByText('Email or username is required')).toBeInTheDocument(); 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()); await waitFor(() => expect(screen.queryByText('Email is invalid')).not.toBeInTheDocument());
}); });
it('should show success meessage if reset-password is successful', async () => { it('should show success meessage if reset-password is successful', async () => {
postMock.mockResolvedValueOnce({ message: 'Email sent' }); postMock.mockResolvedValueOnce({ message: 'Email sent' });
render(<SendResetMailPage />); render(<SendResetMailPage />);
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' })); fireEvent.click(screen.getByRole('button', { name: 'Send reset email' }));
await waitFor(() => await waitFor(() =>
expect(postMock).toHaveBeenCalledWith('/api/user/password/send-reset-email', { expect(postMock).toHaveBeenCalledWith('/api/user/password/send-reset-email', {

View File

@@ -56,7 +56,7 @@ describe('Login Page', () => {
fireEvent.click(screen.getByRole('button', { name: 'Login button' })); fireEvent.click(screen.getByRole('button', { name: 'Login button' }));
expect(await screen.findByText('Email or username is required')).toBeInTheDocument(); 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()); await waitFor(() => expect(screen.queryByText('Email or username is required')).not.toBeInTheDocument());
}); });
it('should pass validation checks for password field', async () => { it('should pass validation checks for password field', async () => {
@@ -65,7 +65,7 @@ describe('Login Page', () => {
fireEvent.click(screen.getByRole('button', { name: 'Login button' })); fireEvent.click(screen.getByRole('button', { name: 'Login button' }));
expect(await screen.findByText('Password is required')).toBeInTheDocument(); 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()); await waitFor(() => expect(screen.queryByText('Password is required')).not.toBeInTheDocument());
}); });
it('should navigate to default url if credentials is valid', async () => { it('should navigate to default url if credentials is valid', async () => {
@@ -77,8 +77,8 @@ describe('Login Page', () => {
postMock.mockResolvedValueOnce({ message: 'Logged in' }); postMock.mockResolvedValueOnce({ message: 'Logged in' });
render(<LoginPage />); render(<LoginPage />);
userEvent.type(screen.getByLabelText('Username input field'), 'admin'); await userEvent.type(screen.getByLabelText('Username input field'), 'admin');
userEvent.type(screen.getByLabelText('Password input field'), 'test'); await userEvent.type(screen.getByLabelText('Password input field'), 'test');
fireEvent.click(screen.getByLabelText('Login button')); fireEvent.click(screen.getByLabelText('Login button'));
await waitFor(() => expect(postMock).toHaveBeenCalledWith('/login', { password: 'test', user: 'admin' })); await waitFor(() => expect(postMock).toHaveBeenCalledWith('/login', { password: 'test', user: 'admin' }));

View File

@@ -1,11 +1,21 @@
import React from 'react'; 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 userEvent from '@testing-library/user-event';
import { BrowserRouter } from 'react-router-dom'; import { BrowserRouter } from 'react-router-dom';
import { locationUtil } from '@grafana/data'; import { locationUtil } from '@grafana/data';
import { config, setLocationService } from '@grafana/runtime'; import { config, setLocationService } from '@grafana/runtime';
import TestProvider from '../../../../test/helpers/TestProvider'; 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'; import NavBarItem, { Props } from './NavBarItem';
const onClickMock = jest.fn(); const onClickMock = jest.fn();
@@ -21,7 +31,7 @@ const defaults: Props = {
}, },
}; };
function getTestContext(overrides: Partial<Props> = {}, subUrl = '') { async function getTestContext(overrides: Partial<Props> = {}, subUrl = '') {
jest.clearAllMocks(); jest.clearAllMocks();
config.appSubUrl = subUrl; config.appSubUrl = subUrl;
locationUtil.initialize({ config, getTimeRangeForUrl: jest.fn(), getVariablesUrlParams: jest.fn() }); locationUtil.initialize({ config, getTimeRangeForUrl: jest.fn(), getVariablesUrlParams: jest.fn() });
@@ -38,33 +48,34 @@ function getTestContext(overrides: Partial<Props> = {}, subUrl = '') {
</TestProvider> </TestProvider>
); );
// 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 }; return { rerender, pushMock };
} }
describe('NavBarItem', () => { describe('NavBarItem', () => {
describe('when url property is not set', () => { describe('when url property is not set', () => {
it('then it renders the menu trigger as a button', () => { it('then it renders the menu trigger as a button', async () => {
getTestContext(); await getTestContext();
expect(screen.getAllByRole('button')).toHaveLength(1); expect(screen.getAllByRole('button')).toHaveLength(1);
}); });
describe('and clicking on the menu trigger button', () => { describe('and clicking on the menu trigger button', () => {
it('then the onClick handler should be called', () => { it('then the onClick handler should be called', async () => {
getTestContext(); await getTestContext();
act(() => { await userEvent.click(screen.getByRole('button'));
userEvent.click(screen.getByRole('button'));
});
expect(onClickMock).toHaveBeenCalledTimes(1); expect(onClickMock).toHaveBeenCalledTimes(1);
}); });
}); });
describe('and hovering over the menu trigger button', () => { describe('and hovering over the menu trigger button', () => {
it('then the menu items should be visible', () => { it('then the menu items should be visible', async () => {
getTestContext(); await getTestContext();
userEvent.hover(screen.getByRole('button')); await userEvent.hover(screen.getByRole('button'));
expect(screen.getByRole('menuitem', { name: 'Parent Node' })).toBeInTheDocument(); expect(screen.getByRole('menuitem', { name: 'Parent Node' })).toBeInTheDocument();
expect(screen.getByText('Child Node 1')).toBeInTheDocument(); expect(screen.getByText('Child Node 1')).toBeInTheDocument();
@@ -73,10 +84,10 @@ describe('NavBarItem', () => {
}); });
describe('and tabbing to the menu trigger button', () => { describe('and tabbing to the menu trigger button', () => {
it('then the menu items should be visible', () => { it('then the menu items should be visible', async () => {
getTestContext(); await getTestContext();
userEvent.tab(); await userEvent.tab();
expect(screen.getByText('Parent Node')).toBeInTheDocument(); expect(screen.getByText('Parent Node')).toBeInTheDocument();
expect(screen.getByText('Child Node 1')).toBeInTheDocument(); expect(screen.getByText('Child Node 1')).toBeInTheDocument();
@@ -85,16 +96,16 @@ describe('NavBarItem', () => {
}); });
describe('and pressing arrow right on the menu trigger button', () => { describe('and pressing arrow right on the menu trigger button', () => {
it('then the correct menu item should receive focus', () => { it('then the correct menu item should receive focus', async () => {
getTestContext(); await getTestContext();
userEvent.tab(); await userEvent.tab();
expect(screen.getAllByRole('menuitem')).toHaveLength(3); expect(screen.getAllByRole('menuitem')).toHaveLength(3);
expect(screen.getByRole('menuitem', { name: 'Parent Node' })).toHaveAttribute('tabIndex', '-1'); expect(screen.getByRole('menuitem', { name: 'Parent Node' })).toHaveAttribute('tabIndex', '-1');
expect(screen.getAllByRole('menuitem')[1]).toHaveAttribute('tabIndex', '-1'); expect(screen.getAllByRole('menuitem')[1]).toHaveAttribute('tabIndex', '-1');
expect(screen.getAllByRole('menuitem')[2]).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')).toHaveLength(3);
expect(screen.getAllByRole('menuitem')[0]).toHaveAttribute('tabIndex', '0'); expect(screen.getAllByRole('menuitem')[0]).toHaveAttribute('tabIndex', '0');
expect(screen.getAllByRole('menuitem')[1]).toHaveAttribute('tabIndex', '-1'); expect(screen.getAllByRole('menuitem')[1]).toHaveAttribute('tabIndex', '-1');
@@ -104,18 +115,18 @@ describe('NavBarItem', () => {
}); });
describe('when url property is set', () => { describe('when url property is set', () => {
it('then it renders the menu trigger as a link', () => { it('then it renders the menu trigger as a link', async () => {
getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } }); await getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } });
expect(screen.getAllByRole('link')).toHaveLength(1); expect(screen.getAllByRole('link')).toHaveLength(1);
expect(screen.getByRole('link')).toHaveAttribute('href', 'https://www.grafana.com'); expect(screen.getByRole('link')).toHaveAttribute('href', 'https://www.grafana.com');
}); });
describe('and hovering over the menu trigger link', () => { describe('and hovering over the menu trigger link', () => {
it('then the menu items should be visible', () => { it('then the menu items should be visible', async () => {
getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } }); 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('Parent Node')).toBeInTheDocument();
expect(screen.getByText('Child Node 1')).toBeInTheDocument(); expect(screen.getByText('Child Node 1')).toBeInTheDocument();
@@ -124,10 +135,10 @@ describe('NavBarItem', () => {
}); });
describe('and tabbing to the menu trigger link', () => { describe('and tabbing to the menu trigger link', () => {
it('then the menu items should be visible', () => { it('then the menu items should be visible', async () => {
getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } }); await getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } });
userEvent.tab(); await userEvent.tab();
expect(screen.getByText('Parent Node')).toBeInTheDocument(); expect(screen.getByText('Parent Node')).toBeInTheDocument();
expect(screen.getByText('Child Node 1')).toBeInTheDocument(); expect(screen.getByText('Child Node 1')).toBeInTheDocument();
@@ -136,17 +147,17 @@ describe('NavBarItem', () => {
}); });
describe('and pressing arrow right on the menu trigger link', () => { describe('and pressing arrow right on the menu trigger link', () => {
it('then the correct menu item should receive focus', () => { it('then the correct menu item should receive focus', async () => {
getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } }); await getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } });
userEvent.tab(); await userEvent.tab();
expect(screen.getAllByRole('link')[0]).toHaveFocus(); expect(screen.getAllByRole('link')[0]).toHaveFocus();
expect(screen.getAllByRole('menuitem')).toHaveLength(3); expect(screen.getAllByRole('menuitem')).toHaveLength(3);
expect(screen.getAllByRole('menuitem')[0]).toHaveAttribute('tabIndex', '-1'); expect(screen.getAllByRole('menuitem')[0]).toHaveAttribute('tabIndex', '-1');
expect(screen.getAllByRole('menuitem')[1]).toHaveAttribute('tabIndex', '-1'); expect(screen.getAllByRole('menuitem')[1]).toHaveAttribute('tabIndex', '-1');
expect(screen.getAllByRole('menuitem')[2]).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('link')[0]).not.toHaveFocus();
expect(screen.getAllByRole('menuitem')).toHaveLength(3); expect(screen.getAllByRole('menuitem')).toHaveLength(3);
expect(screen.getAllByRole('menuitem')[0]).toHaveAttribute('tabIndex', '0'); expect(screen.getAllByRole('menuitem')[0]).toHaveAttribute('tabIndex', '0');
@@ -156,18 +167,18 @@ describe('NavBarItem', () => {
}); });
describe('and pressing arrow left on a menu item', () => { describe('and pressing arrow left on a menu item', () => {
it('then the nav bar item should receive focus', () => { it('then the nav bar item should receive focus', async () => {
getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } }); await getTestContext({ link: { ...defaults.link, url: 'https://www.grafana.com' } });
userEvent.tab(); await userEvent.tab();
userEvent.keyboard('{arrowright}'); await userEvent.keyboard('{ArrowRight}');
expect(screen.getAllByRole('link')[0]).not.toHaveFocus(); expect(screen.getAllByRole('link')[0]).not.toHaveFocus();
expect(screen.getAllByRole('menuitem')).toHaveLength(3); expect(screen.getAllByRole('menuitem')).toHaveLength(3);
expect(screen.getAllByRole('menuitem')[0]).toHaveAttribute('tabIndex', '0'); expect(screen.getAllByRole('menuitem')[0]).toHaveAttribute('tabIndex', '0');
expect(screen.getAllByRole('menuitem')[1]).toHaveAttribute('tabIndex', '-1'); expect(screen.getAllByRole('menuitem')[1]).toHaveAttribute('tabIndex', '-1');
expect(screen.getAllByRole('menuitem')[2]).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('link')[0]).toHaveFocus();
expect(screen.getAllByRole('menuitem')).toHaveLength(3); expect(screen.getAllByRole('menuitem')).toHaveLength(3);
expect(screen.getAllByRole('menuitem')[0]).toHaveAttribute('tabIndex', '-1'); 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', () => { describe('when appSubUrl is configured and user clicks on menuitem link', () => {
it('then location service should be called with correct url', async () => { it('then location service should be called with correct url', async () => {
const { pushMock } = getTestContext( const { pushMock } = await getTestContext(
{ {
link: { link: {
...defaults.link, ...defaults.link,
@@ -189,15 +200,13 @@ describe('NavBarItem', () => {
'/grafana' '/grafana'
); );
userEvent.hover(screen.getByRole('link')); await userEvent.hover(screen.getByRole('link'));
await waitFor(() => { await waitFor(() => {
expect(screen.getByText('Parent Node')).toBeInTheDocument(); expect(screen.getByText('Parent Node')).toBeInTheDocument();
expect(screen.getByText('New')).toBeInTheDocument(); expect(screen.getByText('New')).toBeInTheDocument();
}); });
act(() => { await userEvent.click(screen.getByText('New'));
userEvent.click(screen.getByText('New'));
});
await waitFor(() => { await waitFor(() => {
expect(pushMock).toHaveBeenCalledTimes(1); expect(pushMock).toHaveBeenCalledTimes(1);
expect(pushMock).toHaveBeenCalledWith('/dashboard/new'); expect(pushMock).toHaveBeenCalledWith('/dashboard/new');
@@ -207,7 +216,7 @@ describe('NavBarItem', () => {
describe('when appSubUrl is not configured and user clicks on menuitem link', () => { describe('when appSubUrl is not configured and user clicks on menuitem link', () => {
it('then location service should be called with correct url', async () => { it('then location service should be called with correct url', async () => {
const { pushMock } = getTestContext({ const { pushMock } = await getTestContext({
link: { link: {
...defaults.link, ...defaults.link,
url: 'https://www.grafana.com', url: 'https://www.grafana.com',
@@ -215,15 +224,13 @@ describe('NavBarItem', () => {
}, },
}); });
userEvent.hover(screen.getByRole('link')); await userEvent.hover(screen.getByRole('link'));
await waitFor(() => { await waitFor(() => {
expect(screen.getByText('Parent Node')).toBeInTheDocument(); expect(screen.getByText('Parent Node')).toBeInTheDocument();
expect(screen.getByText('New')).toBeInTheDocument(); expect(screen.getByText('New')).toBeInTheDocument();
}); });
act(() => { await userEvent.click(screen.getByText('New'));
userEvent.click(screen.getByText('New'));
});
await waitFor(() => { await waitFor(() => {
expect(pushMock).toHaveBeenCalledTimes(1); expect(pushMock).toHaveBeenCalledTimes(1);
expect(pushMock).toHaveBeenCalledWith('/grafana/dashboard/new'); expect(pushMock).toHaveBeenCalledWith('/grafana/dashboard/new');

View File

@@ -23,10 +23,10 @@ describe('NavBarMenu', () => {
expect(closeButton).toBeInTheDocument(); 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' }); const closeButton = screen.getByRole('button', { name: 'Close navigation menu' });
expect(closeButton).toBeInTheDocument(); expect(closeButton).toBeInTheDocument();
userEvent.click(closeButton); await userEvent.click(closeButton);
expect(mockOnClose).toHaveBeenCalled(); expect(mockOnClose).toHaveBeenCalled();
}); });
}); });

View File

@@ -76,7 +76,7 @@ describe('ReadonlyFolderPicker', () => {
expect(within(selectors.container.get()).getByRole('combobox')).toBeInTheDocument(); expect(within(selectors.container.get()).getByRole('combobox')).toBeInTheDocument();
getFoldersAsOptionsSpy.mockClear(); 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)); await waitFor(() => expect(getFoldersAsOptionsSpy).toHaveBeenCalledTimes(1));
expect(getFoldersAsOptionsSpy).toHaveBeenCalledWith({ expect(getFoldersAsOptionsSpy).toHaveBeenCalledWith({

View File

@@ -60,10 +60,10 @@ describe('Signup Page', () => {
fireEvent.click(screen.getByRole('button', { name: 'Submit' })); fireEvent.click(screen.getByRole('button', { name: 'Submit' }));
expect(await screen.findByText('Email is required')).toBeInTheDocument(); 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()); 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()); await waitFor(() => expect(screen.queryByText('Email is invalid')).not.toBeInTheDocument());
}); });
it('should pass validation checks for password and confirm password field', async () => { 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('Password is required')).toBeInTheDocument();
expect(await screen.findByText('Confirmed password is required')).toBeInTheDocument(); expect(await screen.findByText('Confirmed password is required')).toBeInTheDocument();
userEvent.type(screen.getByLabelText('Password'), 'admin'); await userEvent.type(screen.getByLabelText('Password'), 'admin');
userEvent.type(screen.getByLabelText('Confirm password'), 'a'); await userEvent.type(screen.getByLabelText('Confirm password'), 'a');
await waitFor(() => expect(screen.queryByText('Passwords must match!')).toBeInTheDocument()); 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()); await waitFor(() => expect(screen.queryByText('Passwords must match!')).not.toBeInTheDocument());
}); });
it('should navigate to default url if signup is successful', async () => { it('should navigate to default url if signup is successful', async () => {
@@ -89,10 +89,10 @@ describe('Signup Page', () => {
postMock.mockResolvedValueOnce({ message: 'Logged in' }); postMock.mockResolvedValueOnce({ message: 'Logged in' });
render(<SignupPage {...props} />); render(<SignupPage {...props} />);
userEvent.type(screen.getByRole('textbox', { name: 'Your name' }), 'test-user'); await userEvent.type(screen.getByRole('textbox', { name: 'Your name' }), 'test-user');
userEvent.type(screen.getByRole('textbox', { name: 'Email' }), 'test@gmail.com'); await userEvent.type(screen.getByRole('textbox', { name: 'Email' }), 'test@gmail.com');
userEvent.type(screen.getByLabelText('Password'), 'admin'); await userEvent.type(screen.getByLabelText('Password'), 'admin');
userEvent.type(screen.getByLabelText('Confirm password'), 'admin'); await userEvent.type(screen.getByLabelText('Confirm password'), 'admin');
fireEvent.click(screen.getByRole('button', { name: 'Submit' })); fireEvent.click(screen.getByRole('button', { name: 'Submit' }));
await waitFor(() => await waitFor(() =>

View File

@@ -44,17 +44,17 @@ describe('VerifyEmail Page', () => {
fireEvent.click(screen.getByRole('button', { name: 'Send verification email' })); fireEvent.click(screen.getByRole('button', { name: 'Send verification email' }));
expect(await screen.findByText('Email is required')).toBeInTheDocument(); 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()); 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()); await waitFor(() => expect(screen.queryByText('Email is invalid')).not.toBeInTheDocument());
}); });
it('should show complete signup if email-verification is successful', async () => { it('should show complete signup if email-verification is successful', async () => {
postMock.mockResolvedValueOnce({ message: 'SignUpCreated' }); postMock.mockResolvedValueOnce({ message: 'SignUpCreated' });
render(<VerifyEmailPage />); render(<VerifyEmailPage />);
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' })); fireEvent.click(screen.getByRole('button', { name: 'Send verification email' }));
await waitFor(() => await waitFor(() =>

View File

@@ -53,7 +53,7 @@ describe('OrgSwitcher', () => {
it('should switch orgId in call to backend', async () => { it('should switch orgId in call to backend', async () => {
const row = screen.getByRole('row', { name: /org 2 admin switch to/i }); const row = screen.getByRole('row', { name: /org 2 admin switch to/i });
const switchToButton = within(row).getByText(/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' })); 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 row = screen.getByRole('row', { name: /org 2 admin switch to/i });
const switchToButton = within(row).getByText(/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')); await waitFor(() => expect(window.location.href).toEqual('/subUrl/?orgId=2'));
}); });

View File

@@ -14,12 +14,12 @@ describe('Unified Alerting promotion', () => {
expect(screen.queryByText('Try out the Grafana 8 alerting!')).toBeInTheDocument(); 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(<UnifiedAlertingPromotion />); const promotion = render(<UnifiedAlertingPromotion />);
expect(window.localStorage.getItem(LOCAL_STORAGE_KEY)).toBe('true'); expect(window.localStorage.getItem(LOCAL_STORAGE_KEY)).toBe('true');
const dismissButton = promotion.getByRole('button'); const dismissButton = promotion.getByRole('button');
userEvent.click(dismissButton); await userEvent.click(dismissButton);
expect(screen.queryByText('Try out the Grafana 8 alerting!')).not.toBeInTheDocument(); expect(screen.queryByText('Try out the Grafana 8 alerting!')).not.toBeInTheDocument();
expect(window.localStorage.getItem(LOCAL_STORAGE_KEY)).toBe('false'); expect(window.localStorage.getItem(LOCAL_STORAGE_KEY)).toBe('false');

View File

@@ -83,10 +83,10 @@ describe('AlertGroups', () => {
expect(groups[0]).toHaveTextContent('No grouping'); expect(groups[0]).toHaveTextContent('No grouping');
expect(groups[1]).toHaveTextContent('severity=warningregion=US-Central'); 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(); 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.silenceButton.get(ui.groupTable.get())).toBeDefined();
expect(ui.sourceButton.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[1]).toHaveTextContent('region=EMEA');
expect(groups[2]).toHaveTextContent('region=APAC'); expect(groups[2]).toHaveTextContent('region=APAC');
userEvent.type(groupByInput, 'appName{enter}'); await userEvent.type(groupByInput, 'appName{enter}');
await waitFor(() => expect(groupByWrapper).toHaveTextContent('appName')); await waitFor(() => expect(groupByWrapper).toHaveTextContent('appName'));
@@ -130,10 +130,10 @@ describe('AlertGroups', () => {
expect(groups[1]).toHaveTextContent('appName=auth'); expect(groups[1]).toHaveTextContent('appName=auth');
expect(groups[2]).toHaveTextContent('appName=frontend'); expect(groups[2]).toHaveTextContent('appName=frontend');
userEvent.click(ui.clearButton.get()); await userEvent.click(ui.clearButton.get());
await waitFor(() => expect(groupByWrapper).not.toHaveTextContent('appName')); await waitFor(() => expect(groupByWrapper).not.toHaveTextContent('appName'));
userEvent.type(groupByInput, 'env{enter}'); await userEvent.type(groupByInput, 'env{enter}');
await waitFor(() => expect(groupByWrapper).toHaveTextContent('env')); await waitFor(() => expect(groupByWrapper).toHaveTextContent('env'));
groups = ui.group.getAll(); groups = ui.group.getAll();
@@ -142,10 +142,10 @@ describe('AlertGroups', () => {
expect(groups[0]).toHaveTextContent('env=production'); expect(groups[0]).toHaveTextContent('env=production');
expect(groups[1]).toHaveTextContent('env=staging'); expect(groups[1]).toHaveTextContent('env=staging');
userEvent.click(ui.clearButton.get()); await userEvent.click(ui.clearButton.get());
await waitFor(() => expect(groupByWrapper).not.toHaveTextContent('env')); await waitFor(() => expect(groupByWrapper).not.toHaveTextContent('env'));
userEvent.type(groupByInput, 'uniqueLabel{enter}'); await userEvent.type(groupByInput, 'uniqueLabel{enter}');
await waitFor(() => expect(groupByWrapper).toHaveTextContent('uniqueLabel')); await waitFor(() => expect(groupByWrapper).toHaveTextContent('uniqueLabel'));
groups = ui.group.getAll(); groups = ui.group.getAll();

View File

@@ -116,7 +116,7 @@ describe('AlertsFolderView tests', () => {
expect(ui.ruleList.row.queryAll()).toHaveLength(0); 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 // Arrange
const store = configureStore(); const store = configureStore();
const folder = mockFolder(); const folder = mockFolder();
@@ -143,14 +143,14 @@ describe('AlertsFolderView tests', () => {
</Provider> </Provider>
); );
userEvent.type(ui.filter.name.get(), 'cpu'); await userEvent.type(ui.filter.name.get(), 'cpu');
// Assert // Assert
expect(ui.ruleList.row.queryAll()).toHaveLength(1); expect(ui.ruleList.row.queryAll()).toHaveLength(1);
expect(ui.ruleList.row.get()).toHaveTextContent('CPU Alert'); expect(ui.ruleList.row.get()).toHaveTextContent('CPU Alert');
}); });
it('Should filter alert rule by labels', () => { it('Should filter alert rule by labels', async () => {
// Arrange // Arrange
const store = configureStore(); const store = configureStore();
const folder = mockFolder(); const folder = mockFolder();
@@ -180,7 +180,7 @@ describe('AlertsFolderView tests', () => {
</Provider> </Provider>
); );
userEvent.type(ui.filter.label.get(), 'severity=critical'); await userEvent.type(ui.filter.label.get(), 'severity=critical');
// Assert // Assert
expect(ui.ruleList.row.queryAll()).toHaveLength(1); expect(ui.ruleList.row.queryAll()).toHaveLength(1);

View File

@@ -273,24 +273,24 @@ describe('AmRoutes', () => {
// open root route for editing // open root route for editing
const rootRouteContainer = await ui.rootRouteContainer.find(); const rootRouteContainer = await ui.rootRouteContainer.find();
userEvent.click(ui.editButton.get(rootRouteContainer)); await userEvent.click(ui.editButton.get(rootRouteContainer));
// configure receiver & group by // configure receiver & group by
const receiverSelect = await ui.receiverSelect.find(); const receiverSelect = await ui.receiverSelect.find();
await clickSelectOption(receiverSelect, 'critical'); await clickSelectOption(receiverSelect, 'critical');
const groupSelect = ui.groupSelect.get(); 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 // 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.groupWaitContainer.get(), '1', 'Minutes');
await updateTiming(ui.groupIntervalContainer.get(), '4', 'Minutes'); await updateTiming(ui.groupIntervalContainer.get(), '4', 'Minutes');
await updateTiming(ui.groupRepeatContainer.get(), '5', 'Hours'); await updateTiming(ui.groupRepeatContainer.get(), '5', 'Hours');
//save //save
userEvent.click(ui.saveButton.get(rootRouteContainer)); await userEvent.click(ui.saveButton.get(rootRouteContainer));
// wait for it to go out of edit mode // wait for it to go out of edit mode
await waitFor(() => expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument()); await waitFor(() => expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument());
@@ -333,17 +333,17 @@ describe('AmRoutes', () => {
// open root route for editing // open root route for editing
const rootRouteContainer = await ui.rootRouteContainer.find(); const rootRouteContainer = await ui.rootRouteContainer.find();
userEvent.click(ui.editButton.get(rootRouteContainer)); await userEvent.click(ui.editButton.get(rootRouteContainer));
// configure receiver & group by // configure receiver & group by
const receiverSelect = await ui.receiverSelect.find(); const receiverSelect = await ui.receiverSelect.find();
await clickSelectOption(receiverSelect, 'default'); await clickSelectOption(receiverSelect, 'default');
const groupSelect = ui.groupSelect.get(); const groupSelect = ui.groupSelect.get();
userEvent.type(byRole('combobox').get(groupSelect), 'severity{enter}'); await userEvent.type(byRole('combobox').get(groupSelect), 'severity{enter}');
userEvent.type(byRole('combobox').get(groupSelect), 'namespace{enter}'); await userEvent.type(byRole('combobox').get(groupSelect), 'namespace{enter}');
//save //save
userEvent.click(ui.saveButton.get(rootRouteContainer)); await userEvent.click(ui.saveButton.get(rootRouteContainer));
// wait for it to go out of edit mode // wait for it to go out of edit mode
await waitFor(() => expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument()); await waitFor(() => expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument());
@@ -426,8 +426,8 @@ describe('AmRoutes', () => {
// Toggle a save to test new object_matchers // Toggle a save to test new object_matchers
const rootRouteContainer = await ui.rootRouteContainer.find(); const rootRouteContainer = await ui.rootRouteContainer.find();
userEvent.click(ui.editButton.get(rootRouteContainer)); await userEvent.click(ui.editButton.get(rootRouteContainer));
userEvent.click(ui.saveButton.get(rootRouteContainer)); await userEvent.click(ui.saveButton.get(rootRouteContainer));
await waitFor(() => expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument()); await waitFor(() => expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument());
@@ -497,8 +497,8 @@ describe('AmRoutes', () => {
// Toggle a save to test new object_matchers // Toggle a save to test new object_matchers
const rootRouteContainer = await ui.rootRouteContainer.find(); const rootRouteContainer = await ui.rootRouteContainer.find();
userEvent.click(ui.editButton.get(rootRouteContainer)); await userEvent.click(ui.editButton.get(rootRouteContainer));
userEvent.click(ui.saveButton.get(rootRouteContainer)); await userEvent.click(ui.saveButton.get(rootRouteContainer));
await waitFor(() => expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument()); await waitFor(() => expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument());
@@ -599,7 +599,7 @@ describe('AmRoutes', () => {
await renderAmRoutes(dataSources.am.name); await renderAmRoutes(dataSources.am.name);
const rows = await ui.row.findAll(); const rows = await ui.row.findAll();
expect(rows).toHaveLength(1); expect(rows).toHaveLength(1);
userEvent.click(ui.editRouteButton.get(rows[0])); await userEvent.click(ui.editRouteButton.get(rows[0]));
const muteTimingSelect = ui.muteTimingSelect.get(); const muteTimingSelect = ui.muteTimingSelect.get();
await clickSelectOption(muteTimingSelect, 'default-mute'); await clickSelectOption(muteTimingSelect, 'default-mute');
@@ -608,7 +608,7 @@ describe('AmRoutes', () => {
const savePolicyButton = ui.savePolicyButton.get(); const savePolicyButton = ui.savePolicyButton.get();
expect(savePolicyButton).toBeInTheDocument(); expect(savePolicyButton).toBeInTheDocument();
userEvent.click(savePolicyButton); await userEvent.click(savePolicyButton);
await waitFor(() => expect(savePolicyButton).not.toBeInTheDocument()); await waitFor(() => expect(savePolicyButton).not.toBeInTheDocument());
@@ -637,14 +637,14 @@ describe('AmRoutes', () => {
}); });
const clickSelectOption = async (selectElement: HTMLElement, optionText: string): Promise<void> => { const clickSelectOption = async (selectElement: HTMLElement, optionText: string): Promise<void> => {
userEvent.click(byRole('combobox').get(selectElement)); await userEvent.click(byRole('combobox').get(selectElement));
await selectOptionInTest(selectElement, optionText); await selectOptionInTest(selectElement, optionText);
}; };
const updateTiming = async (selectElement: HTMLElement, value: string, timeUnit: string): Promise<void> => { const updateTiming = async (selectElement: HTMLElement, value: string, timeUnit: string): Promise<void> => {
const input = byRole('textbox').get(selectElement); const input = byRole('textbox').get(selectElement);
const select = byRole('combobox').get(selectElement); const select = byRole('combobox').get(selectElement);
userEvent.type(input, value); await userEvent.type(input, value);
userEvent.click(select); await userEvent.click(select);
await selectOptionInTest(selectElement, timeUnit); await selectOptionInTest(selectElement, timeUnit);
}; };

View File

@@ -118,11 +118,11 @@ describe('Mute timings', () => {
await waitFor(() => expect(mocks.api.fetchAlertManagerConfig).toHaveBeenCalled()); await waitFor(() => expect(mocks.api.fetchAlertManagerConfig).toHaveBeenCalled());
expect(ui.nameField.get()).toBeInTheDocument(); expect(ui.nameField.get()).toBeInTheDocument();
userEvent.type(ui.nameField.get(), 'maintenance period'); await userEvent.type(ui.nameField.get(), 'maintenance period');
userEvent.type(ui.startsAt.get(), '22:00'); await userEvent.type(ui.startsAt.get(), '22:00');
userEvent.type(ui.endsAt.get(), '24:00'); await userEvent.type(ui.endsAt.get(), '24:00');
userEvent.type(ui.days.get(), '-1'); await userEvent.type(ui.days.get(), '-1');
userEvent.type(ui.months.get(), 'january, july'); await userEvent.type(ui.months.get(), 'january, july');
fireEvent.submit(ui.form.get()); fireEvent.submit(ui.form.get());
@@ -163,17 +163,17 @@ describe('Mute timings', () => {
expect(ui.nameField.get()).toHaveValue(muteTimeInterval.name); expect(ui.nameField.get()).toHaveValue(muteTimeInterval.name);
expect(ui.months.get()).toHaveValue(muteTimeInterval.time_intervals[0].months?.join(', ')); expect(ui.months.get()).toHaveValue(muteTimeInterval.time_intervals[0].months?.join(', '));
userEvent.clear(ui.startsAt.getAll()?.[0]); await userEvent.clear(ui.startsAt.getAll()?.[0]);
userEvent.clear(ui.endsAt.getAll()?.[0]); await userEvent.clear(ui.endsAt.getAll()?.[0]);
userEvent.clear(ui.weekdays.get()); await userEvent.clear(ui.weekdays.get());
userEvent.clear(ui.days.get()); await userEvent.clear(ui.days.get());
userEvent.clear(ui.months.get()); await userEvent.clear(ui.months.get());
userEvent.clear(ui.years.get()); await userEvent.clear(ui.years.get());
userEvent.type(ui.weekdays.get(), 'monday'); await userEvent.type(ui.weekdays.get(), 'monday');
userEvent.type(ui.days.get(), '-7:-1'); await userEvent.type(ui.days.get(), '-7:-1');
userEvent.type(ui.months.get(), '3, 6, 9, 12'); await userEvent.type(ui.months.get(), '3, 6, 9, 12');
userEvent.type(ui.years.get(), '2021:2024'); await userEvent.type(ui.years.get(), '2021:2024');
fireEvent.submit(ui.form.get()); fireEvent.submit(ui.form.get());
@@ -243,8 +243,8 @@ describe('Mute timings', () => {
expect(ui.nameField.get()).toBeInTheDocument(); expect(ui.nameField.get()).toBeInTheDocument();
expect(ui.nameField.get()).toHaveValue(muteTimeInterval.name); expect(ui.nameField.get()).toHaveValue(muteTimeInterval.name);
userEvent.clear(ui.nameField.get()); await userEvent.clear(ui.nameField.get());
userEvent.type(ui.nameField.get(), 'Lunch breaks'); await userEvent.type(ui.nameField.get(), 'Lunch breaks');
fireEvent.submit(ui.form.get()); fireEvent.submit(ui.form.get());

View File

@@ -4,7 +4,7 @@ import { Router } from 'react-router-dom';
import Receivers from './Receivers'; import Receivers from './Receivers';
import React from 'react'; import React from 'react';
import { locationService, setDataSourceSrv } from '@grafana/runtime'; 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 { getAllDataSources } from './utils/config';
import { updateAlertManagerConfig, fetchAlertManagerConfig, fetchStatus, testReceivers } from './api/alertmanager'; import { updateAlertManagerConfig, fetchAlertManagerConfig, fetchStatus, testReceivers } from './api/alertmanager';
import { import {
@@ -116,7 +116,7 @@ const ui = {
}; };
const clickSelectOption = async (selectElement: HTMLElement, optionText: string): Promise<void> => { const clickSelectOption = async (selectElement: HTMLElement, optionText: string): Promise<void> => {
userEvent.click(byRole('combobox').get(selectElement)); await userEvent.click(byRole('combobox').get(selectElement));
await selectOptionInTest(selectElement, optionText); await selectOptionInTest(selectElement, optionText);
}; };
@@ -194,29 +194,25 @@ describe('Receivers', () => {
await renderReceivers(); await renderReceivers();
// go to new contact point page // go to new contact point page
await act(async () => { await userEvent.click(await ui.newContactPointButton.find());
userEvent.click(await ui.newContactPointButton.find());
});
await byRole('heading', { name: /create contact point/i }).find(); await byRole('heading', { name: /create contact point/i }).find();
expect(locationService.getLocation().pathname).toEqual('/alerting/notifications/receivers/new'); expect(locationService.getLocation().pathname).toEqual('/alerting/notifications/receivers/new');
await act(async () => { // type in a name for the new receiver
// type in a name for the new receiver await userEvent.type(ui.inputs.name.get(), 'my new receiver');
userEvent.type(ui.inputs.name.get(), 'my new receiver');
// enter some email // enter some email
const email = ui.inputs.email.addresses.get(); const email = ui.inputs.email.addresses.get();
userEvent.clear(email); await userEvent.clear(email);
userEvent.type(email, 'tester@grafana.com'); await userEvent.type(email, 'tester@grafana.com');
// try to test the contact point // try to test the contact point
userEvent.click(await ui.testContactPointButton.find()); await userEvent.click(await ui.testContactPointButton.find());
});
await waitFor(() => expect(ui.testContactPointModal.get()).toBeInTheDocument(), { timeout: 1000 }); 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()); await waitFor(() => expect(ui.contactPointAnnotationSelect(0).get()).toBeInTheDocument());
// enter custom annotations and labels // 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.contactPointAnnotationValue(0).get(), 'Test contact point');
await userEvent.type(ui.contactPointLabelKey(0).get(), 'foo'); await userEvent.type(ui.contactPointLabelKey(0).get(), 'foo');
await userEvent.type(ui.contactPointLabelValue(0).get(), 'bar'); 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()); await waitFor(() => expect(mocks.api.testReceivers).toHaveBeenCalled());
@@ -260,7 +256,7 @@ describe('Receivers', () => {
expect(locationService.getLocation().pathname).toEqual('/alerting/notifications/receivers/new'); expect(locationService.getLocation().pathname).toEqual('/alerting/notifications/receivers/new');
// type in a name for the new receiver // 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 // check that default email form is rendered
await ui.inputs.email.addresses.find(); await ui.inputs.email.addresses.find();
@@ -274,13 +270,10 @@ describe('Receivers', () => {
const urlInput = ui.inputs.hipchat.url.get(); const urlInput = ui.inputs.hipchat.url.get();
const apiKeyInput = ui.inputs.hipchat.apiKey.get(); const apiKeyInput = ui.inputs.hipchat.apiKey.get();
userEvent.type(urlInput, 'http://hipchat'); await userEvent.type(urlInput, 'http://hipchat');
userEvent.type(apiKeyInput, 'foobarbaz'); await userEvent.type(apiKeyInput, 'foobarbaz');
// it seems react-hook-form does some async state updates after submit await userEvent.click(await ui.saveContactButton.find());
await act(async () => {
userEvent.click(await ui.saveContactButton.find());
});
// see that we're back to main page and proper api calls have been made // see that we're back to main page and proper api calls have been made
await ui.receiversTable.find(); await ui.receiversTable.find();
@@ -350,13 +343,13 @@ describe('Receivers', () => {
// modify webhook url // modify webhook url
const slackContainer = ui.channelFormContainer.get(); const slackContainer = ui.channelFormContainer.get();
await userEvent.click(byText('Optional Slack settings').get(slackContainer)); 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 // add confirm button to action
await userEvent.click(byText(/Actions \(1\)/i).get(slackContainer)); await userEvent.click(byText(/Actions \(1\)/i).get(slackContainer));
await userEvent.click(await byTestId('items.1.settings.actions.0.confirm.add-button').find()); 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(); 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 // delete a field
await userEvent.click(byText(/Fields \(2\)/i).get(slackContainer)); await userEvent.click(byText(/Fields \(2\)/i).get(slackContainer));
@@ -366,12 +359,9 @@ describe('Receivers', () => {
// add another channel // add another channel
await userEvent.click(ui.newContactPointTypeButton.get()); await userEvent.click(ui.newContactPointTypeButton.get());
await clickSelectOption(await byTestId('items.2.type').find(), 'Webhook'); 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 userEvent.click(ui.saveContactButton.get());
await act(async () => {
await userEvent.click(ui.saveContactButton.get());
});
// see that we're back to main page and proper api calls have been made // see that we're back to main page and proper api calls have been made
await ui.receiversTable.find(); await ui.receiversTable.find();
@@ -438,7 +428,7 @@ describe('Receivers', () => {
const receiverRows = receiversTable.querySelectorAll<HTMLTableRowElement>('tbody tr'); const receiverRows = receiversTable.querySelectorAll<HTMLTableRowElement>('tbody tr');
expect(receiverRows[0]).toHaveTextContent('cloud-receiver'); expect(receiverRows[0]).toHaveTextContent('cloud-receiver');
expect(byTestId('edit').query(receiverRows[0])).not.toBeInTheDocument(); 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 // check that form is open
await byRole('heading', { name: /contact point/i }).find(); await byRole('heading', { name: /contact point/i }).find();

View File

@@ -9,7 +9,7 @@ import { byLabelText, byRole, byTestId, byText } from 'testing-library-selector'
import { selectOptionInTest } from '@grafana/ui'; import { selectOptionInTest } from '@grafana/ui';
import { contextSrv } from 'app/core/services/context_srv'; import { contextSrv } from 'app/core/services/context_srv';
import { mockDataSource, MockDataSourceSrv } from './mocks'; 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 { DataSourceInstanceSettings } from '@grafana/data';
import { getAllDataSources } from './utils/config'; import { getAllDataSources } from './utils/config';
import { fetchRulerRules, fetchRulerRulesGroup, fetchRulerRulesNamespace, setRulerRuleGroup } from './api/ruler'; import { fetchRulerRules, fetchRulerRulesGroup, fetchRulerRulesNamespace, setRulerRuleGroup } from './api/ruler';
@@ -144,30 +144,30 @@ describe('RuleEditor', () => {
await waitFor(() => expect(mocks.searchFolders).toHaveBeenCalled()); await waitFor(() => expect(mocks.searchFolders).toHaveBeenCalled());
await waitFor(() => expect(mocks.api.fetchBuildInfo).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');
userEvent.click(await ui.buttons.lotexAlert.get()); await userEvent.click(await ui.buttons.lotexAlert.get());
const dataSourceSelect = ui.inputs.dataSource.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 clickSelectOption(dataSourceSelect, 'Prom (default)');
await waitFor(() => expect(mocks.api.fetchRulerRules).toHaveBeenCalled()); await waitFor(() => expect(mocks.api.fetchRulerRules).toHaveBeenCalled());
await clickSelectOption(ui.inputs.namespace.get(), 'namespace2'); await clickSelectOption(ui.inputs.namespace.get(), 'namespace2');
await clickSelectOption(ui.inputs.group.get(), 'group2'); 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'); await userEvent.type(ui.inputs.annotationValue(0).get(), 'some summary');
userEvent.type(ui.inputs.annotationValue(1).get(), 'some description'); await userEvent.type(ui.inputs.annotationValue(1).get(), 'some description');
// TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed // 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'); await userEvent.type(ui.inputs.labelKey(0).get(), 'severity');
userEvent.type(ui.inputs.labelValue(0).get(), 'warn'); await userEvent.type(ui.inputs.labelValue(0).get(), 'warn');
userEvent.type(ui.inputs.labelKey(1).get(), 'team'); await userEvent.type(ui.inputs.labelKey(1).get(), 'team');
userEvent.type(ui.inputs.labelValue(1).get(), 'the a-team'); await userEvent.type(ui.inputs.labelValue(1).get(), 'the a-team');
// save and check what was sent to backend // 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()); await waitFor(() => expect(mocks.api.setRulerRuleGroup).toHaveBeenCalled());
expect(mocks.api.setRulerRuleGroup).toHaveBeenCalledWith( expect(mocks.api.setRulerRuleGroup).toHaveBeenCalledWith(
{ dataSourceName: 'Prom', apiVersion: 'legacy' }, { dataSourceName: 'Prom', apiVersion: 'legacy' },
@@ -244,27 +244,27 @@ describe('RuleEditor', () => {
await waitFor(() => expect(mocks.searchFolders).toHaveBeenCalled()); await waitFor(() => expect(mocks.searchFolders).toHaveBeenCalled());
await waitFor(() => expect(mocks.api.fetchBuildInfo).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(); const folderInput = await ui.inputs.folder.find();
await clickSelectOption(folderInput, 'Folder A'); await clickSelectOption(folderInput, 'Folder A');
const groupInput = screen.getByRole('textbox', { name: /^Group/ }); 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'); await userEvent.type(ui.inputs.annotationValue(0).get(), 'some summary');
userEvent.type(ui.inputs.annotationValue(1).get(), 'some description'); await userEvent.type(ui.inputs.annotationValue(1).get(), 'some description');
// TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed // 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'); await userEvent.type(ui.inputs.labelKey(0).get(), 'severity');
userEvent.type(ui.inputs.labelValue(0).get(), 'warn'); await userEvent.type(ui.inputs.labelValue(0).get(), 'warn');
userEvent.type(ui.inputs.labelKey(1).get(), 'team'); await userEvent.type(ui.inputs.labelKey(1).get(), 'team');
userEvent.type(ui.inputs.labelValue(1).get(), 'the a-team'); await userEvent.type(ui.inputs.labelValue(1).get(), 'the a-team');
// save and check what was sent to backend // 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()); await waitFor(() => expect(mocks.api.setRulerRuleGroup).toHaveBeenCalled());
expect(mocks.api.setRulerRuleGroup).toHaveBeenCalledWith( expect(mocks.api.setRulerRuleGroup).toHaveBeenCalledWith(
{ dataSourceName: GRAFANA_RULES_SOURCE_NAME, apiVersion: 'legacy' }, { dataSourceName: GRAFANA_RULES_SOURCE_NAME, apiVersion: 'legacy' },
@@ -336,27 +336,27 @@ describe('RuleEditor', () => {
await renderRuleEditor(); await renderRuleEditor();
await waitFor(() => expect(mocks.searchFolders).toHaveBeenCalled()); await waitFor(() => expect(mocks.searchFolders).toHaveBeenCalled());
await waitFor(() => expect(mocks.api.fetchBuildInfo).toHaveBeenCalled()); await waitFor(() => expect(mocks.api.fetchBuildInfo).toHaveBeenCalled());
userEvent.type(await ui.inputs.name.find(), 'my great new recording rule'); await userEvent.type(await ui.inputs.name.find(), 'my great new recording rule');
userEvent.click(await ui.buttons.lotexRecordingRule.get()); await userEvent.click(await ui.buttons.lotexRecordingRule.get());
const dataSourceSelect = ui.inputs.dataSource.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 clickSelectOption(dataSourceSelect, 'Prom (default)');
await waitFor(() => expect(mocks.api.fetchRulerRules).toHaveBeenCalled()); await waitFor(() => expect(mocks.api.fetchRulerRules).toHaveBeenCalled());
await clickSelectOption(ui.inputs.namespace.get(), 'namespace2'); await clickSelectOption(ui.inputs.namespace.get(), 'namespace2');
await clickSelectOption(ui.inputs.group.get(), 'group2'); 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 // 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'); await userEvent.type(ui.inputs.labelKey(1).get(), 'team');
userEvent.type(ui.inputs.labelValue(1).get(), 'the a-team'); await userEvent.type(ui.inputs.labelValue(1).get(), 'the a-team');
// try to save, find out that recording rule name is invalid // 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(() => await waitFor(() =>
expect( expect(
byText( byText(
@@ -367,11 +367,11 @@ describe('RuleEditor', () => {
expect(mocks.api.setRulerRuleGroup).not.toBeCalled(); expect(mocks.api.setRulerRuleGroup).not.toBeCalled();
// fix name and re-submit // fix name and re-submit
userEvent.type(await ui.inputs.name.find(), '{selectall}{del}my:great:new:recording:rule'); await userEvent.clear(await ui.inputs.name.find());
userEvent.click(ui.buttons.save.get()); await userEvent.type(await ui.inputs.name.find(), 'my:great:new:recording:rule');
// save and check what was sent to backend // 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()); await waitFor(() => expect(mocks.api.setRulerRuleGroup).toHaveBeenCalled());
expect(mocks.api.setRulerRuleGroup).toHaveBeenCalledWith( expect(mocks.api.setRulerRuleGroup).toHaveBeenCalledWith(
{ dataSourceName: 'Prom', apiVersion: 'legacy' }, { dataSourceName: 'Prom', apiVersion: 'legacy' },
@@ -458,15 +458,15 @@ describe('RuleEditor', () => {
// add an annotation // add an annotation
await clickSelectOption(ui.inputs.annotationKey(2).get(), /Add new/); await clickSelectOption(ui.inputs.annotationKey(2).get(), /Add new/);
userEvent.type(byRole('textbox').get(ui.inputs.annotationKey(2).get()), 'custom'); await userEvent.type(byRole('textbox').get(ui.inputs.annotationKey(2).get()), 'custom');
userEvent.type(ui.inputs.annotationValue(2).get(), 'value'); await userEvent.type(ui.inputs.annotationValue(2).get(), 'value');
//add a label //add a label
userEvent.type(ui.inputs.labelKey(2).get(), 'custom'); await userEvent.type(ui.inputs.labelKey(2).get(), 'custom');
userEvent.type(ui.inputs.labelValue(2).get(), 'value'); await userEvent.type(ui.inputs.labelValue(2).get(), 'value');
// save and check what was sent to backend // 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()); await waitFor(() => expect(mocks.api.setRulerRuleGroup).toHaveBeenCalled());
expect(mocks.api.setRulerRuleGroup).toHaveBeenCalledWith( expect(mocks.api.setRulerRuleGroup).toHaveBeenCalledWith(
@@ -614,11 +614,11 @@ describe('RuleEditor', () => {
await waitFor(() => expect(mocks.searchFolders).toHaveBeenCalled()); await waitFor(() => expect(mocks.searchFolders).toHaveBeenCalled());
await ui.inputs.name.find(); 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 // check that only rules sources that have ruler available are there
const dataSourceSelect = ui.inputs.dataSource.get(); 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(await byText('loki with ruler').query()).toBeInTheDocument();
expect(byText('cortex with ruler').query()).toBeInTheDocument(); expect(byText('cortex with ruler').query()).toBeInTheDocument();
expect(byText('loki with local rule store').query()).not.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<void> => { const clickSelectOption = async (selectElement: HTMLElement, optionText: Matcher): Promise<void> => {
userEvent.click(byRole('combobox').get(selectElement)); await userEvent.click(byRole('combobox').get(selectElement));
await selectOptionInTest(selectElement, optionText as string); await selectOptionInTest(selectElement, optionText as string);
}; };

View File

@@ -202,7 +202,7 @@ describe('RuleList', () => {
expect(errors).not.toHaveTextContent( expect(errors).not.toHaveTextContent(
'Failed to load rules state from Prometheus-broken: this datasource is broken' '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'); 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 // expand second group to see rules table
expect(ui.rulesTable.query()).not.toBeInTheDocument(); 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]); const table = await ui.rulesTable.find(groups[1]);
// check that rule rows are rendered properly // check that rule rows are rendered properly
@@ -315,7 +315,7 @@ describe('RuleList', () => {
expect(byText('Labels').query()).not.toBeInTheDocument(); expect(byText('Labels').query()).not.toBeInTheDocument();
// expand alert details // 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]); 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'); expect(instanceRows![1]).toHaveTextContent('Firingfoo=bazseverity=error2021-03-18 13:47:05');
// expand details of an instance // 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]); const alertDetails = byTestId('expanded-content').get(instanceRows[0]);
expect(alertDetails).toHaveTextContent('Value2e+10'); expect(alertDetails).toHaveTextContent('Value2e+10');
expect(alertDetails).toHaveTextContent('messagefirst alert message'); expect(alertDetails).toHaveTextContent('messagefirst alert message');
// collapse everything again // 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(); expect(byTestId('expanded-content').query(instanceRows[0])).not.toBeInTheDocument();
userEvent.click(ui.ruleCollapseToggle.getAll(ruleRows[1])[0]); await userEvent.click(ui.ruleCollapseToggle.getAll(ruleRows[1])[0]);
userEvent.click(ui.groupCollapseToggle.get(groups[1])); await userEvent.click(ui.groupCollapseToggle.get(groups[1]));
expect(ui.rulesTable.query()).not.toBeInTheDocument(); expect(ui.rulesTable.query()).not.toBeInTheDocument();
}); });
@@ -456,33 +456,36 @@ describe('RuleList', () => {
expect(groups).toHaveLength(2); expect(groups).toHaveLength(2);
const filterInput = ui.rulesFilterInput.get(); 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 // Input is debounced so wait for it to be visible
await waitFor(() => expect(filterInput).toHaveValue('{foo="bar"}')); await waitFor(() => expect(filterInput).toHaveValue('{foo="bar"}'));
// Group doesn't contain matching labels // Group doesn't contain matching labels
await waitFor(() => expect(ui.ruleGroup.queryAll()).toHaveLength(1)); 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]); const ruleRows = ui.ruleRow.getAll(groups[0]);
expect(ruleRows).toHaveLength(1); 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]); const ruleDetails = ui.expandedContent.get(ruleRows[0]);
expect(ruleDetails).toHaveTextContent('Labelsseverity=warningfoo=bar'); expect(ruleDetails).toHaveTextContent('Labelsseverity=warningfoo=bar');
// Check for different label matchers // 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 // Group doesn't contain matching labels
await waitFor(() => expect(ui.ruleGroup.queryAll()).toHaveLength(1)); await waitFor(() => expect(ui.ruleGroup.queryAll()).toHaveLength(1));
await waitFor(() => expect(ui.ruleGroup.get()).toHaveTextContent('group-2')); 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)); 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.queryAll()).toHaveLength(1));
await waitFor(() => expect(ui.ruleGroup.get()).toHaveTextContent('group-2')); await waitFor(() => expect(ui.ruleGroup.get()).toHaveTextContent('group-2'));
}); });
@@ -521,7 +524,7 @@ describe('RuleList', () => {
expect(groups).toHaveLength(3); expect(groups).toHaveLength(3);
// open edit dialog // 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.namespaceInput.get()).toHaveValue('namespace1');
expect(ui.editGroupModal.ruleGroupInput.get()).toHaveValue('group1'); expect(ui.editGroupModal.ruleGroupInput.get()).toHaveValue('group1');
@@ -531,16 +534,16 @@ describe('RuleList', () => {
testCase('rename both lotex namespace and group', async () => { testCase('rename both lotex namespace and group', async () => {
// make changes to form // make changes to form
userEvent.clear(ui.editGroupModal.namespaceInput.get()); await userEvent.clear(ui.editGroupModal.namespaceInput.get());
userEvent.type(ui.editGroupModal.namespaceInput.get(), 'super namespace'); await userEvent.type(ui.editGroupModal.namespaceInput.get(), 'super namespace');
userEvent.clear(ui.editGroupModal.ruleGroupInput.get()); await userEvent.clear(ui.editGroupModal.ruleGroupInput.get());
userEvent.type(ui.editGroupModal.ruleGroupInput.get(), 'super group'); 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 // 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()); await waitFor(() => expect(ui.editGroupModal.namespaceInput.query()).not.toBeInTheDocument());
@@ -572,12 +575,12 @@ describe('RuleList', () => {
testCase('rename just the lotex group', async () => { testCase('rename just the lotex group', async () => {
// make changes to form // make changes to form
userEvent.clear(ui.editGroupModal.ruleGroupInput.get()); await userEvent.clear(ui.editGroupModal.ruleGroupInput.get());
userEvent.type(ui.editGroupModal.ruleGroupInput.get(), 'super group'); 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 // 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()); await waitFor(() => expect(ui.editGroupModal.namespaceInput.query()).not.toBeInTheDocument());
@@ -604,10 +607,10 @@ describe('RuleList', () => {
testCase('edit lotex group eval interval, no renaming', async () => { testCase('edit lotex group eval interval, no renaming', async () => {
// make changes to form // 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 // 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()); await waitFor(() => expect(ui.editGroupModal.namespaceInput.query()).not.toBeInTheDocument());

View File

@@ -12,7 +12,7 @@ import { DataSourceType } from './utils/datasource';
import { parseMatchers } from './utils/alertmanager'; import { parseMatchers } from './utils/alertmanager';
import { AlertState, MatcherOperator } from 'app/plugins/datasource/alertmanager/types'; import { AlertState, MatcherOperator } from 'app/plugins/datasource/alertmanager/types';
import { byLabelText, byPlaceholderText, byRole, byTestId, byText } from 'testing-library-selector'; 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 { contextSrv } from 'app/core/services/context_srv';
import { AccessControlAction } from 'app/types'; import { AccessControlAction } from 'app/types';
@@ -171,7 +171,8 @@ describe('Silences', () => {
await waitFor(() => expect(mocks.api.fetchAlerts).toHaveBeenCalled()); await waitFor(() => expect(mocks.api.fetchAlerts).toHaveBeenCalled());
const queryBar = ui.queryBar.get(); 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)); await waitFor(() => expect(ui.silenceRow.getAll()).toHaveLength(1));
}, },
@@ -254,40 +255,40 @@ describe('Silence edit', () => {
const startDateString = dateTime(start).format('YYYY-MM-DD'); const startDateString = dateTime(start).format('YYYY-MM-DD');
const endDateString = dateTime(end).format('YYYY-MM-DD'); const endDateString = dateTime(end).format('YYYY-MM-DD');
userEvent.clear(ui.editor.durationInput.get()); await userEvent.clear(ui.editor.durationInput.get());
userEvent.type(ui.editor.durationInput.get(), '1d'); await userEvent.type(ui.editor.durationInput.get(), '1d');
await waitFor(() => expect(ui.editor.durationInput.query()).toHaveValue('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(startDateString));
await waitFor(() => expect(ui.editor.timeRange.get()).toHaveTextContent(endDateString)); await waitFor(() => expect(ui.editor.timeRange.get()).toHaveTextContent(endDateString));
userEvent.type(ui.editor.matcherName.get(), 'foo'); await userEvent.type(ui.editor.matcherName.get(), 'foo');
userEvent.type(ui.editor.matcherOperatorSelect.get(), '='); await userEvent.type(ui.editor.matcherOperatorSelect.get(), '=');
userEvent.tab(); await userEvent.tab();
userEvent.type(ui.editor.matcherValue.get(), 'bar'); await userEvent.type(ui.editor.matcherValue.get(), 'bar');
// TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed
userEvent.click(ui.editor.addMatcherButton.get(), undefined, { skipPointerEventsCheck: true }); await userEvent.click(ui.editor.addMatcherButton.get(), { pointerEventsCheck: PointerEventsCheckLevel.Never });
userEvent.type(ui.editor.matcherName.getAll()[1], 'bar'); await userEvent.type(ui.editor.matcherName.getAll()[1], 'bar');
userEvent.type(ui.editor.matcherOperatorSelect.getAll()[1], '!='); await userEvent.type(ui.editor.matcherOperatorSelect.getAll()[1], '!=');
userEvent.tab(); await userEvent.tab();
userEvent.type(ui.editor.matcherValue.getAll()[1], 'buzz'); await userEvent.type(ui.editor.matcherValue.getAll()[1], 'buzz');
// TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed
userEvent.click(ui.editor.addMatcherButton.get(), undefined, { skipPointerEventsCheck: true }); await userEvent.click(ui.editor.addMatcherButton.get(), { pointerEventsCheck: PointerEventsCheckLevel.Never });
userEvent.type(ui.editor.matcherName.getAll()[2], 'region'); await userEvent.type(ui.editor.matcherName.getAll()[2], 'region');
userEvent.type(ui.editor.matcherOperatorSelect.getAll()[2], '=~'); await userEvent.type(ui.editor.matcherOperatorSelect.getAll()[2], '=~');
userEvent.tab(); await userEvent.tab();
userEvent.type(ui.editor.matcherValue.getAll()[2], 'us-west-.*'); await userEvent.type(ui.editor.matcherValue.getAll()[2], 'us-west-.*');
// TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed // TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed
userEvent.click(ui.editor.addMatcherButton.get(), undefined, { skipPointerEventsCheck: true }); await userEvent.click(ui.editor.addMatcherButton.get(), { pointerEventsCheck: PointerEventsCheckLevel.Never });
userEvent.type(ui.editor.matcherName.getAll()[3], 'env'); await userEvent.type(ui.editor.matcherName.getAll()[3], 'env');
userEvent.type(ui.editor.matcherOperatorSelect.getAll()[3], '!~'); await userEvent.type(ui.editor.matcherOperatorSelect.getAll()[3], '!~');
userEvent.tab(); await userEvent.tab();
userEvent.type(ui.editor.matcherValue.getAll()[3], 'dev|staging'); 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(() => await waitFor(() =>
expect(mocks.api.createOrUpdateSilence).toHaveBeenCalledWith( expect(mocks.api.createOrUpdateSilence).toHaveBeenCalledWith(

View File

@@ -104,8 +104,8 @@ describe('Admin config', () => {
await renderAdminPage(dataSources.alertManager.name); await renderAdminPage(dataSources.alertManager.name);
userEvent.click(await ui.resetButton.find()); await userEvent.click(await ui.resetButton.find());
userEvent.click(ui.confirmButton.get()); await userEvent.click(ui.confirmButton.get());
await waitFor(() => expect(mocks.api.deleteAlertManagerConfig).toHaveBeenCalled()); await waitFor(() => expect(mocks.api.deleteAlertManagerConfig).toHaveBeenCalled());
expect(ui.confirmButton.query()).not.toBeInTheDocument(); expect(ui.confirmButton.query()).not.toBeInTheDocument();
}); });
@@ -132,12 +132,12 @@ describe('Admin config', () => {
await renderAdminPage(dataSources.alertManager.name); await renderAdminPage(dataSources.alertManager.name);
const input = await ui.configInput.find(); const input = await ui.configInput.find();
expect(input.value).toEqual(JSON.stringify(defaultConfig, null, 2)); 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. // 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 '[['. // To get the literal character, you have to escape them by typing '{{' or '[['.
// See https://github.com/testing-library/user-event/issues/584. // See https://github.com/testing-library/user-event/issues/584.
userEvent.type(input, JSON.stringify(newConfig, null, 2).replace(/[{[]/g, '$&$&')); await userEvent.type(input, JSON.stringify(newConfig, null, 2).replace(/[{[]/g, '$&$&'));
userEvent.click(ui.saveButton.get()); await userEvent.click(ui.saveButton.get());
await waitFor(() => expect(mocks.api.updateAlertManagerConfig).toHaveBeenCalled()); await waitFor(() => expect(mocks.api.updateAlertManagerConfig).toHaveBeenCalled());
await waitFor(() => expect(mocks.api.fetchConfig).toHaveBeenCalledTimes(3)); await waitFor(() => expect(mocks.api.fetchConfig).toHaveBeenCalledTimes(3));
expect(input.value).toEqual(JSON.stringify(newConfig, null, 2)); expect(input.value).toEqual(JSON.stringify(newConfig, null, 2));

View File

@@ -95,13 +95,13 @@ describe('Rules group tests', () => {
expect(ui.editGroupButton.query()).not.toBeInTheDocument(); expect(ui.editGroupButton.query()).not.toBeInTheDocument();
}); });
it('Delete button click should display confirmation modal', () => { it('Delete button click should display confirmation modal', async () => {
// Arrange // Arrange
hasRulerMock.mockReturnValue(true); hasRulerMock.mockReturnValue(true);
// Act // Act
renderRulesGroup(namespace, group); renderRulesGroup(namespace, group);
userEvent.click(ui.deleteGroupButton.get()); await userEvent.click(ui.deleteGroupButton.get());
// Assert // Assert
expect(ui.confirmDeleteModal.header.get()).toBeInTheDocument(); expect(ui.confirmDeleteModal.header.get()).toBeInTheDocument();

View File

@@ -7,7 +7,7 @@ import { setSearchQuery } from './state/reducers';
import { mockToolkitActionCreator } from '../../../test/core/redux/mocks'; import { mockToolkitActionCreator } from '../../../test/core/redux/mocks';
import { getMultipleMockKeys } from './__mocks__/apiKeysMock'; import { getMultipleMockKeys } from './__mocks__/apiKeysMock';
import { selectors } from '@grafana/e2e-selectors'; 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'; import { silenceConsoleOutput } from '../../../test/core/utils/silenceConsoleOutput';
const setup = (propOverrides: Partial<Props>) => { const setup = (propOverrides: Partial<Props>) => {
@@ -100,7 +100,7 @@ describe('ApiKeysPage', () => {
const apiKeys = getMultipleMockKeys(3); const apiKeys = getMultipleMockKeys(3);
const { toggleIncludeExpiredMock } = setup({ apiKeys, apiKeysCount: apiKeys.length, hasFetched: true }); const { toggleIncludeExpiredMock } = setup({ apiKeys, apiKeysCount: apiKeys.length, hasFetched: true });
toggleShowExpired(); await toggleShowExpired();
expect(toggleIncludeExpiredMock).toHaveBeenCalledTimes(1); expect(toggleIncludeExpiredMock).toHaveBeenCalledTimes(1);
}); });
}); });
@@ -112,7 +112,7 @@ describe('ApiKeysPage', () => {
setSearchQueryMock.mockClear(); setSearchQueryMock.mockClear();
expect(screen.getByPlaceholderText(/search keys/i)).toBeInTheDocument(); 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); expect(setSearchQueryMock).toHaveBeenCalledTimes(5);
}); });
}); });
@@ -130,21 +130,21 @@ describe('ApiKeysPage', () => {
deleteApiKeyMock.mockClear(); deleteApiKeyMock.mockClear();
expect(within(firstRow).getByLabelText('Delete API key')).toBeInTheDocument(); 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(); 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).toHaveBeenCalledTimes(1);
expect(deleteApiKeyMock).toHaveBeenCalledWith(1); expect(deleteApiKeyMock).toHaveBeenCalledWith(1);
toggleShowExpired(); await toggleShowExpired();
deleteApiKeyMock.mockClear(); deleteApiKeyMock.mockClear();
expect(within(secondRow).getByLabelText('Delete API key')).toBeInTheDocument(); 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(); expect(within(secondRow).getByRole('button', { name: /delete$/i })).toBeInTheDocument();
userEvent.click(within(secondRow).getByRole('button', { name: /delete$/i }), undefined, { await userEvent.click(within(secondRow).getByRole('button', { name: /delete$/i }), {
skipPointerEventsCheck: true, pointerEventsCheck: PointerEventsCheckLevel.Never,
}); });
expect(deleteApiKeyMock).toHaveBeenCalledTimes(1); expect(deleteApiKeyMock).toHaveBeenCalledTimes(1);
expect(deleteApiKeyMock).toHaveBeenCalledWith(2); expect(deleteApiKeyMock).toHaveBeenCalledWith(2);
@@ -157,7 +157,7 @@ describe('ApiKeysPage', () => {
const { addApiKeyMock } = setup({ apiKeys, apiKeysCount: apiKeys.length, hasFetched: true }); const { addApiKeyMock } = setup({ apiKeys, apiKeysCount: apiKeys.length, hasFetched: true });
addApiKeyMock.mockClear(); 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); await addAndVerifyApiKey(addApiKeyMock);
}); });
}); });
@@ -168,13 +168,13 @@ describe('ApiKeysPage', () => {
const { addApiKeyMock } = setup({ apiKeys, apiKeysCount: apiKeys.length, hasFetched: true }); const { addApiKeyMock } = setup({ apiKeys, apiKeysCount: apiKeys.length, hasFetched: true });
addApiKeyMock.mockClear(); 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); await addAndVerifyApiKey(addApiKeyMock);
toggleShowExpired(); await toggleShowExpired();
addApiKeyMock.mockClear(); 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); await addAndVerifyApiKey(addApiKeyMock);
}); });
}); });
@@ -185,20 +185,20 @@ describe('ApiKeysPage', () => {
const { addApiKeyMock } = setup({ apiKeys, apiKeysCount: apiKeys.length, hasFetched: true }); const { addApiKeyMock } = setup({ apiKeys, apiKeysCount: apiKeys.length, hasFetched: true });
addApiKeyMock.mockClear(); addApiKeyMock.mockClear();
userEvent.click(screen.getByRole('button', { name: /add api key/i })); await userEvent.click(screen.getByRole('button', { name: /add api key/i }));
userEvent.type(screen.getByPlaceholderText(/name/i), 'Test'); await userEvent.type(screen.getByPlaceholderText(/name/i), 'Test');
userEvent.type(screen.getByPlaceholderText(/1d/i), '60x'); await userEvent.type(screen.getByPlaceholderText(/1d/i), '60x');
expect(screen.queryByText(/not a valid duration/i)).not.toBeInTheDocument(); 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(screen.getByText(/not a valid duration/i)).toBeInTheDocument();
expect(addApiKeyMock).toHaveBeenCalledTimes(0); expect(addApiKeyMock).toHaveBeenCalledTimes(0);
}); });
}); });
}); });
function toggleShowExpired() { async function toggleShowExpired() {
expect(screen.queryByLabelText(/include expired keys/i)).toBeInTheDocument(); 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) { async function addAndVerifyApiKey(addApiKeyMock: jest.Mock) {
@@ -207,9 +207,9 @@ async function addAndVerifyApiKey(addApiKeyMock: jest.Mock) {
expect(screen.getByPlaceholderText(/1d/i)).toBeInTheDocument(); expect(screen.getByPlaceholderText(/1d/i)).toBeInTheDocument();
expect(screen.getByRole('button', { name: /^add$/i })).toBeInTheDocument(); expect(screen.getByRole('button', { name: /^add$/i })).toBeInTheDocument();
userEvent.type(screen.getByPlaceholderText(/name/i), 'Test'); await userEvent.type(screen.getByPlaceholderText(/name/i), 'Test');
userEvent.type(screen.getByPlaceholderText(/1d/i), '60s'); await userEvent.type(screen.getByPlaceholderText(/1d/i), '60s');
userEvent.click(screen.getByRole('button', { name: /^add$/i })); await userEvent.click(screen.getByRole('button', { name: /^add$/i }));
expect(addApiKeyMock).toHaveBeenCalledTimes(1); expect(addApiKeyMock).toHaveBeenCalledTimes(1);
expect(addApiKeyMock).toHaveBeenCalledWith({ name: 'Test', role: 'Viewer', secondsToLive: 60 }, expect.anything()); expect(addApiKeyMock).toHaveBeenCalledWith({ name: 'Test', role: 'Viewer', secondsToLive: 60 }, expect.anything());
} }

View File

@@ -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(<AnnotationsSettings dashboard={dashboard} />); render(<AnnotationsSettings dashboard={dashboard} />);
expect(screen.getByRole('heading', { name: /annotations/i })).toBeInTheDocument(); expect(screen.getByRole('heading', { name: /annotations/i })).toBeInTheDocument();
@@ -87,7 +87,7 @@ describe('AnnotationsSettings', () => {
).toBeInTheDocument(); ).toBeInTheDocument();
expect(screen.queryByRole('link', { name: /annotations documentation/i })).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', { const heading = screen.getByRole('heading', {
name: /annotations edit/i, name: /annotations edit/i,
@@ -96,13 +96,13 @@ describe('AnnotationsSettings', () => {
expect(heading).toBeInTheDocument(); expect(heading).toBeInTheDocument();
userEvent.clear(nameInput); await userEvent.clear(nameInput);
userEvent.type(nameInput, 'My Annotation'); await userEvent.type(nameInput, 'My Annotation');
expect(screen.queryByText(/grafana/i)).toBeInTheDocument(); expect(screen.queryByText(/grafana/i)).toBeInTheDocument();
expect(screen.getByRole('checkbox', { name: /hidden/i })).toBeChecked(); 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('table')).toBeInTheDocument();
expect(screen.getByRole('row', { name: /my annotation \(built\-in\) grafana/i })).toBeInTheDocument(); expect(screen.getByRole('row', { name: /my annotation \(built\-in\) grafana/i })).toBeInTheDocument();
@@ -111,8 +111,8 @@ describe('AnnotationsSettings', () => {
).toBeInTheDocument(); ).toBeInTheDocument();
expect(screen.queryByRole('button', { name: /new query/i })).not.toBeInTheDocument(); expect(screen.queryByRole('button', { name: /new query/i })).not.toBeInTheDocument();
userEvent.click(screen.getAllByLabelText(/Delete query with title/)[0]); await userEvent.click(screen.getAllByLabelText(/Delete query with title/)[0]);
userEvent.click(screen.getByRole('button', { name: 'Delete' })); await userEvent.click(screen.getByRole('button', { name: 'Delete' }));
expect(screen.queryAllByRole('row').length).toBe(0); expect(screen.queryAllByRole('row').length).toBe(0);
expect( expect(
@@ -120,7 +120,7 @@ describe('AnnotationsSettings', () => {
).toBeInTheDocument(); ).toBeInTheDocument();
}); });
test('it renders a sortable table of annotations', () => { test('it renders a sortable table of annotations', async () => {
const annotationsList = [ const annotationsList = [
...dashboard.annotations.list, ...dashboard.annotations.list,
{ {
@@ -164,9 +164,9 @@ describe('AnnotationsSettings', () => {
expect(within(getTableBodyRows()[1]).queryByText(/annotation 2/i)).toBeInTheDocument(); expect(within(getTableBodyRows()[1]).queryByText(/annotation 2/i)).toBeInTheDocument();
expect(within(getTableBodyRows()[2]).queryByText(/annotation 3/i)).toBeInTheDocument(); expect(within(getTableBodyRows()[2]).queryByText(/annotation 3/i)).toBeInTheDocument();
userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-down' })[0]); await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-down' })[0]);
userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-down' })[1]); await 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-up' })[0]);
// Checking if it has changed the sorting accordingly // Checking if it has changed the sorting accordingly
expect(within(getTableBodyRows()[0]).queryByText(/annotation 3/i)).toBeInTheDocument(); 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 () => { test('it renders a form for adding/editing annotations', async () => {
render(<AnnotationsSettings dashboard={dashboard} />); render(<AnnotationsSettings dashboard={dashboard} />);
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', { const heading = screen.getByRole('heading', {
name: /annotations edit/i, name: /annotations edit/i,
@@ -186,19 +186,19 @@ describe('AnnotationsSettings', () => {
expect(heading).toBeInTheDocument(); expect(heading).toBeInTheDocument();
userEvent.clear(nameInput); await userEvent.clear(nameInput);
userEvent.type(nameInput, 'My Prometheus Annotation'); 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(await screen.findByText(/Prometheus/i)).toBeVisible();
expect(screen.queryAllByText(/testdata/i)).toHaveLength(2); 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(); 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(within(screen.getAllByRole('rowgroup')[1]).getAllByRole('row').length).toBe(2);
expect(screen.queryByRole('row', { name: /my prometheus annotation prometheus/i })).toBeInTheDocument(); 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')) screen.queryByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query'))
).not.toBeInTheDocument(); ).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); expect(within(screen.getAllByRole('rowgroup')[1]).getAllByRole('row').length).toBe(3);
userEvent.click(screen.getAllByLabelText(/Delete query with title/)[0]); await userEvent.click(screen.getAllByLabelText(/Delete query with title/)[0]);
userEvent.click(screen.getByRole('button', { name: 'Delete' })); await userEvent.click(screen.getByRole('button', { name: 'Delete' }));
expect(within(screen.getAllByRole('rowgroup')[1]).getAllByRole('row').length).toBe(2); expect(within(screen.getAllByRole('rowgroup')[1]).getAllByRole('row').length).toBe(2);
}); });

View File

@@ -49,11 +49,11 @@ describe('AutoRefreshIntervals', () => {
}); });
describe('when input loses focus and intervals are valid', () => { 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 }); const { props } = setupTestContext({ validateIntervalsFunc: () => null });
userEvent.type(screen.getByRole('textbox'), ',30s'); await userEvent.type(screen.getByRole('textbox'), ',30s');
userEvent.tab(); await userEvent.tab();
expect(screen.getByRole('textbox')).toHaveValue('1s,5s,10s,30s'); expect(screen.getByRole('textbox')).toHaveValue('1s,5s,10s,30s');
expect(props.onRefreshIntervalChange).toHaveBeenCalledTimes(1); expect(props.onRefreshIntervalChange).toHaveBeenCalledTimes(1);
@@ -62,11 +62,11 @@ describe('AutoRefreshIntervals', () => {
}); });
describe('when input loses focus and intervals are invalid', () => { 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' }); const { props } = setupTestContext({ validateIntervalsFunc: () => 'Not valid' });
userEvent.type(screen.getByRole('textbox'), ',30q'); await userEvent.type(screen.getByRole('textbox'), ',30q');
userEvent.tab(); await userEvent.tab();
expect(screen.getByRole('textbox')).toHaveValue('1s,5s,10s,30q'); expect(screen.getByRole('textbox')).toHaveValue('1s,5s,10s,30q');
expect(props.onRefreshIntervalChange).toHaveBeenCalledTimes(0); expect(props.onRefreshIntervalChange).toHaveBeenCalledTimes(0);
@@ -74,14 +74,14 @@ describe('AutoRefreshIntervals', () => {
}); });
describe('when input loses focus and previous intervals were invalid', () => { 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 validateIntervalsFunc = jest.fn().mockReturnValueOnce('Not valid').mockReturnValue(null);
const { props } = setupTestContext({ validateIntervalsFunc }); const { props } = setupTestContext({ validateIntervalsFunc });
userEvent.type(screen.getByRole('textbox'), ',30q'); await userEvent.type(screen.getByRole('textbox'), ',30q');
userEvent.tab(); await userEvent.tab();
userEvent.type(screen.getByRole('textbox'), '{backspace}s'); await userEvent.type(screen.getByRole('textbox'), '{backspace}s');
userEvent.tab(); await userEvent.tab();
expect(screen.getByRole('textbox')).toHaveValue('1s,5s,10s,30s'); expect(screen.getByRole('textbox')).toHaveValue('1s,5s,10s,30s');
expect(props.onRefreshIntervalChange).toHaveBeenCalledTimes(1); expect(props.onRefreshIntervalChange).toHaveBeenCalledTimes(1);

View File

@@ -48,9 +48,9 @@ describe('General Settings', () => {
describe('when timezone is changed', () => { describe('when timezone is changed', () => {
it('should call update function', async () => { it('should call update function', async () => {
const { props } = setupTestContext({}); 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); 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'); await selectOptionInTest(timeZonePicker, 'Browser Time');
expect(props.updateTimeZone).toHaveBeenCalledWith('browser'); expect(props.updateTimeZone).toHaveBeenCalledWith('browser');
expect(props.dashboard.timezone).toBe('browser'); expect(props.dashboard.timezone).toBe('browser');

View File

@@ -84,7 +84,7 @@ describe('LinksSettings', () => {
).not.toBeInTheDocument(); ).not.toBeInTheDocument();
}); });
test('it rearranges the order of dashboard links', () => { test('it rearranges the order of dashboard links', async () => {
// @ts-ignore // @ts-ignore
render(<LinksSettings dashboard={dashboard} />); render(<LinksSettings dashboard={dashboard} />);
@@ -103,9 +103,9 @@ describe('LinksSettings', () => {
assertRowHasText(1, links[1].title); assertRowHasText(1, links[1].title);
assertRowHasText(2, links[2].url); assertRowHasText(2, links[2].url);
userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-down' })[0]); await userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-down' })[0]);
userEvent.click(within(getTableBody()).getAllByRole('button', { name: 'arrow-down' })[1]); await 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-up' })[0]);
// Checking if it has changed the sorting accordingly // Checking if it has changed the sorting accordingly
assertRowHasText(0, links[2].url); assertRowHasText(0, links[2].url);
@@ -113,35 +113,35 @@ describe('LinksSettings', () => {
assertRowHasText(2, links[0].title); assertRowHasText(2, links[0].title);
}); });
test('it duplicates dashboard links', () => { test('it duplicates dashboard links', async () => {
// @ts-ignore // @ts-ignore
render(<LinksSettings dashboard={dashboard} />); render(<LinksSettings dashboard={dashboard} />);
expect(getTableBodyRows().length).toBe(links.length); 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(getTableBodyRows().length).toBe(links.length + 1);
expect(within(getTableBody()).getAllByText(links[0].title).length).toBe(2); expect(within(getTableBody()).getAllByText(links[0].title).length).toBe(2);
}); });
test('it deletes dashboard links', () => { test('it deletes dashboard links', async () => {
// @ts-ignore // @ts-ignore
render(<LinksSettings dashboard={dashboard} />); render(<LinksSettings dashboard={dashboard} />);
expect(getTableBodyRows().length).toBe(links.length); expect(getTableBodyRows().length).toBe(links.length);
userEvent.click(within(getTableBody()).getAllByLabelText(/Delete link with title/)[0]); await userEvent.click(within(getTableBody()).getAllByLabelText(/Delete link with title/)[0]);
userEvent.click(within(getTableBody()).getByRole('button', { name: 'Delete' })); await userEvent.click(within(getTableBody()).getByRole('button', { name: 'Delete' }));
expect(getTableBodyRows().length).toBe(links.length - 1); expect(getTableBodyRows().length).toBe(links.length - 1);
expect(within(getTableBody()).queryByText(links[0].title)).not.toBeInTheDocument(); 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 // @ts-ignore
render(<LinksSettings dashboard={dashboard} />); render(<LinksSettings dashboard={dashboard} />);
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('Type')).toBeInTheDocument();
expect(screen.queryByText('Title')).toBeInTheDocument(); expect(screen.queryByText('Title')).toBeInTheDocument();
@@ -151,29 +151,29 @@ describe('LinksSettings', () => {
expect(screen.queryByText('Tooltip')).not.toBeInTheDocument(); expect(screen.queryByText('Tooltip')).not.toBeInTheDocument();
expect(screen.queryByText('Icon')).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.queryAllByText('Dashboards')).toHaveLength(2);
expect(screen.queryByText('Link')).toBeVisible(); expect(screen.queryByText('Link')).toBeVisible();
userEvent.click(screen.getByText('Link')); await userEvent.click(screen.getByText('Link'));
expect(screen.queryByText('URL')).toBeInTheDocument(); expect(screen.queryByText('URL')).toBeInTheDocument();
expect(screen.queryByText('Tooltip')).toBeInTheDocument(); expect(screen.queryByText('Tooltip')).toBeInTheDocument();
expect(screen.queryByText('Icon')).toBeInTheDocument(); expect(screen.queryByText('Icon')).toBeInTheDocument();
userEvent.clear(screen.getByRole('textbox', { name: /title/i })); await userEvent.clear(screen.getByRole('textbox', { name: /title/i }));
userEvent.type(screen.getByRole('textbox', { name: /title/i }), 'New Dashboard Link'); await userEvent.type(screen.getByRole('textbox', { name: /title/i }), 'New Dashboard Link');
userEvent.click( await userEvent.click(
within(screen.getByRole('heading', { name: /dashboard links edit/i })).getByText(/dashboard links/i) within(screen.getByRole('heading', { name: /dashboard links edit/i })).getByText(/dashboard links/i)
); );
expect(getTableBodyRows().length).toBe(links.length + 1); expect(getTableBodyRows().length).toBe(links.length + 1);
expect(within(getTableBody()).queryByText('New Dashboard Link')).toBeInTheDocument(); expect(within(getTableBody()).queryByText('New Dashboard Link')).toBeInTheDocument();
userEvent.click(screen.getAllByText(links[0].type)[0]); await userEvent.click(screen.getAllByText(links[0].type)[0]);
userEvent.clear(screen.getByRole('textbox', { name: /title/i })); await userEvent.clear(screen.getByRole('textbox', { name: /title/i }));
userEvent.type(screen.getByRole('textbox', { name: /title/i }), 'The first dashboard link'); await userEvent.type(screen.getByRole('textbox', { name: /title/i }), 'The first dashboard link');
userEvent.click( await userEvent.click(
within(screen.getByRole('heading', { name: /dashboard links edit/i })).getByText(/dashboard links/i) within(screen.getByRole('heading', { name: /dashboard links edit/i })).getByText(/dashboard links/i)
); );

View File

@@ -6,6 +6,7 @@ import userEvent from '@testing-library/user-event';
import { historySrv } from '../VersionHistory/HistorySrv'; import { historySrv } from '../VersionHistory/HistorySrv';
import { VersionsSettings, VERSIONS_FETCH_LIMIT } from './VersionsSettings'; import { VersionsSettings, VERSIONS_FETCH_LIMIT } from './VersionsSettings';
import { versions, diffs } from './__mocks__/versions'; import { versions, diffs } from './__mocks__/versions';
import { UserEvent } from '@testing-library/user-event/dist/types/setup';
jest.mock('../VersionHistory/HistorySrv'); jest.mock('../VersionHistory/HistorySrv');
@@ -28,8 +29,18 @@ describe('VersionSettings', () => {
getRelativeTime: jest.fn(() => 'time ago'), getRelativeTime: jest.fn(() => 'time ago'),
}; };
let user: UserEvent;
beforeEach(() => { 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.resetAllMocks();
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
}); });
test('renders a header and a loading indicator followed by results in a table', async () => { test('renders a header and a loading indicator followed by results in a table', async () => {
@@ -102,7 +113,9 @@ describe('VersionSettings', () => {
historySrv.getHistoryList historySrv.getHistoryList
// @ts-ignore // @ts-ignore
.mockImplementationOnce(() => Promise.resolve(versions.slice(0, VERSIONS_FETCH_LIMIT))) .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(<VersionsSettings dashboard={dashboard} />); render(<VersionsSettings dashboard={dashboard} />);
@@ -113,14 +126,16 @@ describe('VersionSettings', () => {
expect(within(screen.getAllByRole('rowgroup')[1]).getAllByRole('row').length).toBe(VERSIONS_FETCH_LIMIT); expect(within(screen.getAllByRole('rowgroup')[1]).getAllByRole('row').length).toBe(VERSIONS_FETCH_LIMIT);
const showMoreButton = screen.getByRole('button', { name: /show more versions/i }); const showMoreButton = screen.getByRole('button', { name: /show more versions/i });
userEvent.click(showMoreButton); await user.click(showMoreButton);
expect(historySrv.getHistoryList).toBeCalledTimes(2); 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(() => await waitFor(() => {
expect(within(screen.getAllByRole('rowgroup')[1]).getAllByRole('row').length).toBe(versions.length) 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 () => { 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 compareButton = screen.getByRole('button', { name: /compare versions/i });
const tableBody = screen.getAllByRole('rowgroup')[1]; const tableBody = screen.getAllByRole('rowgroup')[1];
userEvent.click(within(tableBody).getAllByRole('checkbox')[0]); await user.click(within(tableBody).getAllByRole('checkbox')[0]);
userEvent.click(within(tableBody).getAllByRole('checkbox')[VERSIONS_FETCH_LIMIT - 1]); await user.click(within(tableBody).getAllByRole('checkbox')[VERSIONS_FETCH_LIMIT - 1]);
expect(compareButton).toBeEnabled(); expect(compareButton).toBeEnabled();
userEvent.click(within(tableBody).getAllByRole('checkbox')[1]); await user.click(within(tableBody).getAllByRole('checkbox')[1]);
expect(compareButton).toBeDisabled(); expect(compareButton).toBeDisabled();
userEvent.click(within(tableBody).getAllByRole('checkbox')[1]); await user.click(within(tableBody).getAllByRole('checkbox')[1]);
userEvent.click(compareButton); await user.click(compareButton);
await waitFor(() => expect(screen.getByRole('heading', { name: /versions comparing 2 11/i })).toBeInTheDocument()); 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(queryByFullText('version changed')).toBeInTheDocument();
expect(screen.queryByText(/view json diff/i)).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()); await waitFor(() => expect(screen.getByRole('table')).toBeInTheDocument());
}); });

View File

@@ -28,9 +28,9 @@ describe('PanelNotSupported', () => {
}); });
describe('when the back to queries button is clicked', () => { 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({}); 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); expect(locationService.getSearchObject().tab).toBe(PanelEditorTabId.Query);
}); });
}); });

View File

@@ -33,13 +33,13 @@ describe('TransformationsEditor', () => {
options: {}, options: {},
}, },
]); ]);
const editors = screen.getAllByLabelText(/^Transformation editor/g); const editors = screen.getAllByLabelText(/^Transformation editor/);
expect(editors).toHaveLength(1); expect(editors).toHaveLength(1);
}); });
}); });
describe('when Add transformation clicked', () => { describe('when Add transformation clicked', () => {
it('renders transformations picker', () => { it('renders transformations picker', async () => {
const buttonLabel = 'Add transformation'; const buttonLabel = 'Add transformation';
setup([ setup([
{ {
@@ -49,7 +49,7 @@ describe('TransformationsEditor', () => {
]); ]);
const addTransformationButton = screen.getByText(buttonLabel); const addTransformationButton = screen.getByText(buttonLabel);
userEvent.click(addTransformationButton); await userEvent.click(addTransformationButton);
const search = screen.getByLabelText(selectors.components.Transforms.searchInput); const search = screen.getByLabelText(selectors.components.Transforms.searchInput);
expect(search).toBeDefined(); expect(search).toBeDefined();
@@ -58,7 +58,7 @@ describe('TransformationsEditor', () => {
describe('actions', () => { describe('actions', () => {
describe('debug', () => { describe('debug', () => {
it('should show/hide debugger', () => { it('should show/hide debugger', async () => {
setup([ setup([
{ {
id: 'reduce', id: 'reduce',
@@ -70,7 +70,7 @@ describe('TransformationsEditor', () => {
expect(screen.queryByLabelText(debuggerSelector)).toBeNull(); expect(screen.queryByLabelText(debuggerSelector)).toBeNull();
const debugButton = screen.getByLabelText(selectors.components.QueryEditorRow.actionButton('Debug')); const debugButton = screen.getByLabelText(selectors.components.QueryEditorRow.actionButton('Debug'));
userEvent.click(debugButton); await userEvent.click(debugButton);
expect(screen.getByLabelText(debuggerSelector)).toBeInTheDocument(); expect(screen.getByLabelText(debuggerSelector)).toBeInTheDocument();
}); });

View File

@@ -28,7 +28,7 @@ const setup = (children: ReactNode, queries: DataQuery[] = [{ refId: 'A' }]) =>
}; };
const openModal = async () => { 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(); 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 }); const button = await screen.findByRole('button', { name: /add to dashboard/i });
expect(button).toBeDisabled(); expect(button).toBeDisabled();
userEvent.click(button); await userEvent.click(button);
expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
}); });
@@ -71,7 +71,7 @@ describe('AddToDashboardButton', () => {
await openModal(); 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(); expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
}); });
@@ -86,7 +86,7 @@ describe('AddToDashboardButton', () => {
await openModal(); await openModal();
userEvent.click(screen.getByRole('button', { name: /open dashboard$/i })); await userEvent.click(screen.getByRole('button', { name: /open dashboard$/i }));
await waitForAddToDashboardResponse(); await waitForAddToDashboardResponse();
@@ -105,7 +105,7 @@ describe('AddToDashboardButton', () => {
await openModal(); 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(); await waitForAddToDashboardResponse();
@@ -124,7 +124,7 @@ describe('AddToDashboardButton', () => {
await openModal(); 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(); await waitForAddToDashboardResponse();
@@ -138,7 +138,7 @@ describe('AddToDashboardButton', () => {
await openModal(); await openModal();
userEvent.click(screen.getByRole('button', { name: /open dashboard$/i })); await userEvent.click(screen.getByRole('button', { name: /open dashboard$/i }));
await waitForAddToDashboardResponse(); await waitForAddToDashboardResponse();
@@ -157,7 +157,7 @@ describe('AddToDashboardButton', () => {
expect(screen.queryByRole('combobox', { name: /dashboard/ })).not.toBeInTheDocument(); expect(screen.queryByRole('combobox', { name: /dashboard/ })).not.toBeInTheDocument();
userEvent.click(screen.getByRole<HTMLInputElement>('radio', { name: /existing dashboard/i })); await userEvent.click(screen.getByRole<HTMLInputElement>('radio', { name: /existing dashboard/i }));
expect(screen.getByRole('combobox', { name: /dashboard/ })).toBeInTheDocument(); expect(screen.getByRole('combobox', { name: /dashboard/ })).toBeInTheDocument();
}); });
@@ -168,9 +168,9 @@ describe('AddToDashboardButton', () => {
await openModal(); await openModal();
userEvent.click(screen.getByRole<HTMLInputElement>('radio', { name: /existing dashboard/i })); await userEvent.click(screen.getByRole<HTMLInputElement>('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(); await waitForAddToDashboardResponse();
expect(locationService.push).not.toHaveBeenCalled(); expect(locationService.push).not.toHaveBeenCalled();
@@ -203,16 +203,16 @@ describe('AddToDashboardButton', () => {
await openModal(); 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 waitFor(async () => {
await screen.findByLabelText('Select option'); 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 () => { await waitFor(async () => {
expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
@@ -246,16 +246,16 @@ describe('AddToDashboardButton', () => {
await openModal(); 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 waitFor(async () => {
await screen.findByLabelText('Select option'); 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 () => { await waitFor(async () => {
expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
@@ -281,7 +281,7 @@ describe('AddToDashboardButton', () => {
await openModal(); await openModal();
expect(screen.queryByRole('alert')).not.toBeInTheDocument(); 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 () => { await waitFor(async () => {
expect(await screen.findByRole('alert')).toBeInTheDocument(); expect(await screen.findByRole('alert')).toBeInTheDocument();
@@ -300,7 +300,7 @@ describe('AddToDashboardButton', () => {
await openModal(); await openModal();
expect(screen.queryByRole('alert')).not.toBeInTheDocument(); 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 () => { await waitFor(async () => {
expect(await screen.findByRole('alert')).toBeInTheDocument(); expect(await screen.findByRole('alert')).toBeInTheDocument();
@@ -328,16 +328,16 @@ describe('AddToDashboardButton', () => {
await openModal(); await openModal();
expect(screen.queryByRole('alert')).not.toBeInTheDocument(); 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 waitFor(async () => {
await screen.findByLabelText('Select option'); 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 () => { await waitFor(async () => {
expect(await screen.findByRole('alert')).toBeInTheDocument(); expect(await screen.findByRole('alert')).toBeInTheDocument();
@@ -352,7 +352,7 @@ describe('AddToDashboardButton', () => {
await openModal(); await openModal();
expect(screen.queryByRole('alert')).not.toBeInTheDocument(); 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 () => { await waitFor(async () => {
expect(await screen.findByRole('alert')).toBeInTheDocument(); expect(await screen.findByRole('alert')).toBeInTheDocument();

View File

@@ -92,10 +92,10 @@ describe('TraceView', () => {
renderTraceViewNew(); renderTraceViewNew();
expect(screen.queryByText(/Tags/)).toBeFalsy(); expect(screen.queryByText(/Tags/)).toBeFalsy();
const spanView = screen.getAllByText('', { selector: 'div[data-test-id="span-view"]' })[0]; const spanView = screen.getAllByText('', { selector: 'div[data-test-id="span-view"]' })[0];
userEvent.click(spanView); await userEvent.click(spanView);
expect(screen.queryByText(/Tags/)).toBeTruthy(); expect(screen.queryByText(/Tags/)).toBeTruthy();
userEvent.click(spanView); await userEvent.click(spanView);
screen.debug(screen.queryAllByText(/Tags/)); screen.debug(screen.queryAllByText(/Tags/));
expect(screen.queryByText(/Tags/)).toBeFalsy(); expect(screen.queryByText(/Tags/)).toBeFalsy();
}); });
@@ -109,36 +109,36 @@ describe('TraceView', () => {
expect(ticks()).toBe('0μs274.5μs549μs823.5μs1.1ms'); 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(); renderTraceView();
let table: HTMLElement; let table: HTMLElement;
expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(3); 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]; const firstSpan = screen.getAllByText('', { selector: 'div[data-test-id="span-view"]' })[0];
userEvent.click(firstSpan); await userEvent.click(firstSpan);
userEvent.click(screen.getByText(/Process/)); await userEvent.click(screen.getByText(/Process/));
table = screen.getByText('', { selector: 'div[data-test-id="KeyValueTable"]' }); table = screen.getByText('', { selector: 'div[data-test-id="KeyValueTable"]' });
expect(table.innerHTML).toContain('client-uuid-1'); 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]; const secondSpan = screen.getAllByText('', { selector: 'div[data-test-id="span-view"]' })[1];
userEvent.click(secondSpan); await userEvent.click(secondSpan);
userEvent.click(screen.getByText(/Process/)); await userEvent.click(screen.getByText(/Process/));
table = screen.getByText('', { selector: 'div[data-test-id="KeyValueTable"]' }); table = screen.getByText('', { selector: 'div[data-test-id="KeyValueTable"]' });
expect(table.innerHTML).toContain('client-uuid-2'); 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]; const thirdSpan = screen.getAllByText('', { selector: 'div[data-test-id="span-view"]' })[2];
userEvent.click(thirdSpan); await userEvent.click(thirdSpan);
userEvent.click(screen.getByText(/Process/)); await userEvent.click(screen.getByText(/Process/));
table = screen.getByText('', { selector: 'div[data-test-id="KeyValueTable"]' }); table = screen.getByText('', { selector: 'div[data-test-id="KeyValueTable"]' });
expect(table.innerHTML).toContain('client-uuid-3'); 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 { rerender } = render(getTraceView([frameOld]));
const span = screen.getAllByText('', { selector: 'div[data-test-id="span-view"]' })[2]; const span = screen.getAllByText('', { selector: 'div[data-test-id="span-view"]' })[2];
userEvent.click(span); await userEvent.click(span);
//Process is in detail view //Process is in detail view
expect(screen.getByText(/Process/)).toBeInTheDocument(); expect(screen.getByText(/Process/)).toBeInTheDocument();

View File

@@ -35,75 +35,75 @@ function renderTraceViewContainer(frames = [frameOld]) {
} }
describe('TraceViewContainer', () => { describe('TraceViewContainer', () => {
it('toggles children visibility', () => { it('toggles children visibility', async () => {
renderTraceViewContainer(); renderTraceViewContainer();
expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(3); 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); 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); 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(); renderTraceViewContainer();
expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(3); 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); 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); 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(); renderTraceViewContainer();
expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(3); 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); 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); expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(3);
}); });
it('searches for spans', () => { it('searches for spans', async () => {
renderTraceViewContainer(); renderTraceViewContainer();
userEvent.type(screen.getByPlaceholderText('Find...'), '1ed38015486087ca'); await userEvent.type(screen.getByPlaceholderText('Find...'), '1ed38015486087ca');
expect( expect(
(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[0].parentNode! as HTMLElement).className (screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[0].parentNode! as HTMLElement).className
).toContain('rowMatchingFilter'); ).toContain('rowMatchingFilter');
}); });
it('can select next/prev results', () => { it('can select next/prev results', async () => {
renderTraceViewContainer(); 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 nextResultButton = screen.getByTestId('trace-page-search-bar-next-result-button');
const prevResultButton = screen.getByTestId('trace-page-search-bar-prev-result-button'); const prevResultButton = screen.getByTestId('trace-page-search-bar-prev-result-button');
const suffix = screen.getByTestId('trace-page-search-bar-suffix'); const suffix = screen.getByTestId('trace-page-search-bar-suffix');
userEvent.click(nextResultButton); await userEvent.click(nextResultButton);
expect(suffix.textContent).toBe('1 of 2'); expect(suffix.textContent).toBe('1 of 2');
expect( expect(
(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[1].parentNode! as HTMLElement).className (screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[1].parentNode! as HTMLElement).className
).toContain('rowFocused'); ).toContain('rowFocused');
userEvent.click(nextResultButton); await userEvent.click(nextResultButton);
expect(suffix.textContent).toBe('2 of 2'); expect(suffix.textContent).toBe('2 of 2');
expect( expect(
(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[2].parentNode! as HTMLElement).className (screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[2].parentNode! as HTMLElement).className
).toContain('rowFocused'); ).toContain('rowFocused');
userEvent.click(nextResultButton); await userEvent.click(nextResultButton);
expect(suffix.textContent).toBe('1 of 2'); expect(suffix.textContent).toBe('1 of 2');
expect( expect(
(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[1].parentNode! as HTMLElement).className (screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[1].parentNode! as HTMLElement).className
).toContain('rowFocused'); ).toContain('rowFocused');
userEvent.click(prevResultButton); await userEvent.click(prevResultButton);
expect(suffix.textContent).toBe('2 of 2'); expect(suffix.textContent).toBe('2 of 2');
expect( expect(
(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[2].parentNode! as HTMLElement).className (screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[2].parentNode! as HTMLElement).className
).toContain('rowFocused'); ).toContain('rowFocused');
userEvent.click(prevResultButton); await userEvent.click(prevResultButton);
expect(suffix.textContent).toBe('1 of 2'); expect(suffix.textContent).toBe('1 of 2');
expect( expect(
(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[1].parentNode! as HTMLElement).className (screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[1].parentNode! as HTMLElement).className
).toContain('rowFocused'); ).toContain('rowFocused');
userEvent.click(prevResultButton); await userEvent.click(prevResultButton);
expect(suffix.textContent).toBe('2 of 2'); expect(suffix.textContent).toBe('2 of 2');
expect( expect(
(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[2].parentNode! as HTMLElement).className (screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[2].parentNode! as HTMLElement).className

View File

@@ -217,7 +217,7 @@ describe('Wrapper', () => {
}; };
setupExplore({ urlParams }); setupExplore({ urlParams });
const closeButtons = await screen.findAllByTitle(/Close split pane/i); const closeButtons = await screen.findAllByTitle(/Close split pane/i);
userEvent.click(closeButtons[1]); await userEvent.click(closeButtons[1]);
await waitFor(() => { await waitFor(() => {
const logsPanels = screen.queryAllByTitle(/Close split pane/i); const logsPanels = screen.queryAllByTitle(/Close split pane/i);

View File

@@ -11,23 +11,23 @@ export const changeDatasource = async (name: string) => {
fireEvent.click(option); 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' }); const input = withinExplore(exploreId).getByRole('textbox', { name: 'query' });
userEvent.clear(input); await userEvent.clear(input);
userEvent.type(input, query); 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 explore = withinExplore(exploreId);
const toolbar = within(explore.getByLabelText('Explore toolbar')); const toolbar = within(explore.getByLabelText('Explore toolbar'));
const button = toolbar.getByRole('button', { name: /run query/i }); const button = toolbar.getByRole('button', { name: /run query/i });
userEvent.click(button); await userEvent.click(button);
}; };
export const openQueryHistory = async (exploreId: ExploreId = ExploreId.left) => { export const openQueryHistory = async (exploreId: ExploreId = ExploreId.left) => {
const selector = withinExplore(exploreId); const selector = withinExplore(exploreId);
const button = selector.getByRole('button', { name: 'Rich history button' }); const button = selector.getByRole('button', { name: 'Rich history button' });
userEvent.click(button); await userEvent.click(button);
expect( expect(
await selector.findByText('The history is local to your browser and is not shared with others.') await selector.findByText('The history is local to your browser and is not shared with others.')
).toBeInTheDocument(); ).toBeInTheDocument();
@@ -35,26 +35,26 @@ export const openQueryHistory = async (exploreId: ExploreId = ExploreId.left) =>
export const closeQueryHistory = async (exploreId: ExploreId = ExploreId.left) => { export const closeQueryHistory = async (exploreId: ExploreId = ExploreId.left) => {
const closeButton = withinExplore(exploreId).getByRole('button', { name: 'Close query history' }); const closeButton = withinExplore(exploreId).getByRole('button', { name: 'Close query history' });
userEvent.click(closeButton); await userEvent.click(closeButton);
}; };
export const switchToQueryHistoryTab = async ( export const switchToQueryHistoryTab = async (
name: 'Settings' | 'Query History', name: 'Settings' | 'Query History',
exploreId: ExploreId = ExploreId.left 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', { const checkbox = withinExplore(exploreId).getByRole('checkbox', {
name: 'Change the default active tab from “Query history” to “Starred”', 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.*/); 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) => { 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); 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 selector = withinExplore(exploreId);
const buttons = selector.getAllByRole('button', { name: actionAccessibleName }); const buttons = selector.getAllByRole('button', { name: actionAccessibleName });
userEvent.click(buttons[queryIndex]); await userEvent.click(buttons[queryIndex]);
}; };

View File

@@ -49,8 +49,8 @@ describe('Explore: Query History', () => {
await waitForExplore(); await waitForExplore();
// and a user runs a query and opens query history // and a user runs a query and opens query history
inputQuery(USER_INPUT); await inputQuery(USER_INPUT);
runQuery(); await runQuery();
await openQueryHistory(); await openQueryHistory();
// the query that was run is in query history // the query that was run is in query history
@@ -80,8 +80,8 @@ describe('Explore: Query History', () => {
await waitForExplore(); await waitForExplore();
await openQueryHistory(); await openQueryHistory();
inputQuery('query #2'); await inputQuery('query #2');
runQuery(); await runQuery();
await assertQueryHistory(['{"expr":"query #2"}', '{"expr":"query #1"}']); await assertQueryHistory(['{"expr":"query #2"}', '{"expr":"query #1"}']);
}); });
@@ -135,8 +135,8 @@ describe('Explore: Query History', () => {
await switchToQueryHistoryTab('Settings'); await switchToQueryHistoryTab('Settings');
// change settings // change settings
selectStarredTabFirst(); await selectStarredTabFirst();
selectOnlyActiveDataSource(); await selectOnlyActiveDataSource();
await closeQueryHistory(); await closeQueryHistory();
await openQueryHistory(); await openQueryHistory();

View File

@@ -50,19 +50,19 @@ describe('InspectDataTab', () => {
render(<InspectDataTab {...createProps()} />); render(<InspectDataTab {...createProps()} />);
expect(screen.getByText(/Data options/i)).toBeInTheDocument(); expect(screen.getByText(/Data options/i)).toBeInTheDocument();
}); });
it('should show available options', () => { it('should show available options', async () => {
render(<InspectDataTab {...createProps()} />); render(<InspectDataTab {...createProps()} />);
const dataOptions = screen.getByText(/Data options/i); 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(/Show data frame/i)).toBeInTheDocument();
expect(screen.getByText(/Download for Excel/i)).toBeInTheDocument(); expect(screen.getByText(/Download for Excel/i)).toBeInTheDocument();
}); });
it('should show available dataFrame options', () => { it('should show available dataFrame options', async () => {
render(<InspectDataTab {...createProps()} />); render(<InspectDataTab {...createProps()} />);
const dataOptions = screen.getByText(/Data options/i); const dataOptions = screen.getByText(/Data options/i);
userEvent.click(dataOptions); await userEvent.click(dataOptions);
const dataFrameInput = screen.getByRole('combobox', { name: /Select dataframe/i }); const dataFrameInput = screen.getByRole('combobox', { name: /Select dataframe/i });
userEvent.click(dataFrameInput); await userEvent.click(dataFrameInput);
expect(screen.getByText(/Second data frame/i)).toBeInTheDocument(); expect(screen.getByText(/Second data frame/i)).toBeInTheDocument();
}); });
it('should show download logs button if logs data', () => { it('should show download logs button if logs data', () => {

View File

@@ -103,7 +103,7 @@ describe('SignupInvitedPage', () => {
it('then required fields should show error messages and nothing should be posted', async () => { it('then required fields should show error messages and nothing should be posted', async () => {
const { postSpy } = await setupTestContext({ get: { email: '', invitedBy: '', name: '', username: '' } }); 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()); await waitFor(() => expect(screen.getByText(/email is required/i)).toBeInTheDocument());
expect(screen.getByText(/username 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 () => { it('then correct form data should be posted', async () => {
const { postSpy } = await setupTestContext(); const { postSpy } = await setupTestContext();
userEvent.type(screen.getByPlaceholderText(/password/i), 'pass@word1'); await userEvent.type(screen.getByPlaceholderText(/password/i), 'pass@word1');
userEvent.click(screen.getByRole('button', { name: /sign up/i })); await userEvent.click(screen.getByRole('button', { name: /sign up/i }));
await waitFor(() => expect(postSpy).toHaveBeenCalledTimes(1)); await waitFor(() => expect(postSpy).toHaveBeenCalledTimes(1));
expect(postSpy).toHaveBeenCalledWith('/api/user/invite/complete', { expect(postSpy).toHaveBeenCalledWith('/api/user/invite/complete', {

View File

@@ -75,6 +75,7 @@ async function getTestContext(
await waitFor(() => expect(getLibraryPanelsSpy).toHaveBeenCalled()); await waitFor(() => expect(getLibraryPanelsSpy).toHaveBeenCalled());
expect(getLibraryPanelsSpy).toHaveBeenCalledTimes(1); expect(getLibraryPanelsSpy).toHaveBeenCalledTimes(1);
jest.clearAllMocks();
return { rerender, getLibraryPanelsSpy, getSpy, getAllPanelPluginMetaSpy }; return { rerender, getLibraryPanelsSpy, getSpy, getAllPanelPluginMetaSpy };
} }
@@ -91,9 +92,8 @@ describe('LibraryPanelsSearch', () => {
describe('and user searches for library panel by name or description', () => { describe('and user searches for library panel by name or description', () => {
it('should call api with correct params', async () => { it('should call api with correct params', async () => {
const { getLibraryPanelsSpy } = await getTestContext(); 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()); await waitFor(() => expect(getLibraryPanelsSpy).toHaveBeenCalled());
expect(getLibraryPanelsSpy).toHaveBeenCalledTimes(1); expect(getLibraryPanelsSpy).toHaveBeenCalledTimes(1);
expect(getLibraryPanelsSpy).toHaveBeenCalledWith({ expect(getLibraryPanelsSpy).toHaveBeenCalledWith({
@@ -119,18 +119,18 @@ describe('LibraryPanelsSearch', () => {
describe('and user changes sorting', () => { describe('and user changes sorting', () => {
it('should call api with correct params', async () => { it('should call api with correct params', async () => {
const { getLibraryPanelsSpy } = await getTestContext({ showSort: true }); const { getLibraryPanelsSpy } = await getTestContext({ showSort: true });
getLibraryPanelsSpy.mockClear();
userEvent.type(screen.getByText(/sort \(default az\)/i), 'Desc{enter}'); await userEvent.type(screen.getByText(/sort \(default az\)/i), 'Desc{enter}');
await waitFor(() => expect(getLibraryPanelsSpy).toHaveBeenCalledTimes(1)); await waitFor(() =>
expect(getLibraryPanelsSpy).toHaveBeenCalledWith({ expect(getLibraryPanelsSpy).toHaveBeenCalledWith({
searchString: '', searchString: '',
sortDirection: 'alpha-desc', sortDirection: 'alpha-desc',
folderFilter: [], folderFilter: [],
page: 0, page: 0,
typeFilter: [], typeFilter: [],
perPage: 40, perPage: 40,
}); })
);
}); });
}); });
}); });
@@ -147,18 +147,18 @@ describe('LibraryPanelsSearch', () => {
describe('and user changes panel filter', () => { describe('and user changes panel filter', () => {
it('should call api with correct params', async () => { it('should call api with correct params', async () => {
const { getLibraryPanelsSpy } = await getTestContext({ showPanelFilter: true }); const { getLibraryPanelsSpy } = await getTestContext({ showPanelFilter: true });
getLibraryPanelsSpy.mockClear();
userEvent.type(screen.getByRole('combobox', { name: /panel type filter/i }), 'Graph{enter}'); await 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 userEvent.type(screen.getByRole('combobox', { name: /panel type filter/i }), 'Time Series{enter}');
await waitFor(() => expect(getLibraryPanelsSpy).toHaveBeenCalledTimes(1)); await waitFor(() =>
expect(getLibraryPanelsSpy).toHaveBeenCalledWith({ expect(getLibraryPanelsSpy).toHaveBeenCalledWith({
searchString: '', searchString: '',
folderFilter: [], folderFilter: [],
page: 0, page: 0,
typeFilter: ['graph', 'timeseries'], typeFilter: ['graph', 'timeseries'],
perPage: 40, perPage: 40,
}); })
);
}); });
}); });
}); });
@@ -175,20 +175,20 @@ describe('LibraryPanelsSearch', () => {
describe('and user changes folder filter', () => { describe('and user changes folder filter', () => {
it('should call api with correct params', async () => { it('should call api with correct params', async () => {
const { getLibraryPanelsSpy } = await getTestContext({ showFolderFilter: true }); const { getLibraryPanelsSpy } = await getTestContext({ showFolderFilter: true });
getLibraryPanelsSpy.mockClear();
userEvent.click(screen.getByRole('combobox', { name: /folder filter/i })); await userEvent.click(screen.getByRole('combobox', { name: /folder filter/i }));
userEvent.type(screen.getByRole('combobox', { name: /folder filter/i }), '{enter}', { await userEvent.type(screen.getByRole('combobox', { name: /folder filter/i }), '{enter}', {
skipClick: true, skipClick: true,
}); });
await waitFor(() => expect(getLibraryPanelsSpy).toHaveBeenCalledTimes(1)); await waitFor(() =>
expect(getLibraryPanelsSpy).toHaveBeenCalledWith({ expect(getLibraryPanelsSpy).toHaveBeenCalledWith({
searchString: '', searchString: '',
folderFilter: ['0'], folderFilter: ['0'],
page: 0, page: 0,
typeFilter: [], typeFilter: [],
perPage: 40, perPage: 40,
}); })
);
}); });
}); });
}); });

View File

@@ -69,10 +69,10 @@ describe('PlaylistEditPage', () => {
const { putMock } = await getTestContext(); const { putMock } = await getTestContext();
expect(locationService.getLocation().pathname).toEqual('/'); expect(locationService.getLocation().pathname).toEqual('/');
userEvent.clear(screen.getByRole('textbox', { name: /playlist name/i })); await userEvent.clear(screen.getByRole('textbox', { name: /playlist name/i }));
userEvent.type(screen.getByRole('textbox', { name: /playlist name/i }), 'A Name'); await userEvent.type(screen.getByRole('textbox', { name: /playlist name/i }), 'A Name');
userEvent.clear(screen.getByRole('textbox', { name: /playlist interval/i })); await userEvent.clear(screen.getByRole('textbox', { name: /playlist interval/i }));
userEvent.type(screen.getByRole('textbox', { name: /playlist interval/i }), '10s'); await userEvent.type(screen.getByRole('textbox', { name: /playlist interval/i }), '10s');
fireEvent.submit(screen.getByRole('button', { name: /save/i })); fireEvent.submit(screen.getByRole('button', { name: /save/i }));
await waitFor(() => expect(putMock).toHaveBeenCalledTimes(1)); await waitFor(() => expect(putMock).toHaveBeenCalledTimes(1));
expect(putMock).toHaveBeenCalledWith('/api/playlists/1', { expect(putMock).toHaveBeenCalledWith('/api/playlists/1', {

View File

@@ -96,11 +96,11 @@ describe('PlaylistForm', () => {
}); });
describe('when deleting a playlist item', () => { 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); getTestContext(playlist);
expect(rows()).toHaveLength(3); 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); expect(rows()).toHaveLength(2);
expectCorrectRow({ index: 0, type: 'id', title: 'first item', first: true }); expectCorrectRow({ index: 0, type: 'id', title: 'first item', first: true });
expectCorrectRow({ index: 1, type: 'id', title: 'middle item', last: true }); expectCorrectRow({ index: 1, type: 'id', title: 'middle item', last: true });
@@ -108,10 +108,10 @@ describe('PlaylistForm', () => {
}); });
describe('when moving a playlist item up', () => { 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); 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: 0, type: 'id', title: 'first item', first: true });
expectCorrectRow({ index: 1, type: 'tag', title: 'last item' }); expectCorrectRow({ index: 1, type: 'tag', title: 'last item' });
expectCorrectRow({ index: 2, type: 'id', title: 'middle item', last: true }); expectCorrectRow({ index: 2, type: 'id', title: 'middle item', last: true });
@@ -119,10 +119,10 @@ describe('PlaylistForm', () => {
}); });
describe('when moving a playlist item down', () => { 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); 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: 0, type: 'id', title: 'middle item', first: true });
expectCorrectRow({ index: 1, type: 'id', title: 'first item' }); expectCorrectRow({ index: 1, type: 'id', title: 'first item' });
expectCorrectRow({ index: 2, type: 'tag', title: 'last item', last: true }); 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 () => { it('then an alert should appear and nothing should be submitted', async () => {
const { onSubmitMock } = getTestContext(playlist); 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 })); fireEvent.submit(screen.getByRole('button', { name: /save/i }));
expect(await screen.findAllByRole('alert')).toHaveLength(1); expect(await screen.findAllByRole('alert')).toHaveLength(1);
expect(onSubmitMock).not.toHaveBeenCalled(); expect(onSubmitMock).not.toHaveBeenCalled();

View File

@@ -66,7 +66,7 @@ describe('PlaylistNewPage', () => {
const { backendSrvMock } = getTestContext(); const { backendSrvMock } = getTestContext();
expect(locationService.getLocation().pathname).toEqual('/'); 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 })); fireEvent.submit(screen.getByRole('button', { name: /save/i }));
await waitFor(() => expect(backendSrvMock).toHaveBeenCalledTimes(1)); await waitFor(() => expect(backendSrvMock).toHaveBeenCalledTimes(1));
expect(backendSrvMock).toHaveBeenCalledWith('/api/playlists', { expect(backendSrvMock).toHaveBeenCalledWith('/api/playlists', {

View File

@@ -349,7 +349,7 @@ describe('Browse list of plugins', () => {
expect(listOption).not.toBeChecked(); expect(listOption).not.toBeChecked();
// Switch to "list" view // Switch to "list" view
userEvent.click(listOption); await userEvent.click(listOption);
expect(gridOption).not.toBeChecked(); expect(gridOption).not.toBeChecked();
expect(listOption).toBeChecked(); expect(listOption).toBeChecked();

View File

@@ -447,14 +447,14 @@ describe('Plugin details page', () => {
await waitFor(() => expect(queryByText(PluginTabLabels.OVERVIEW)).toBeInTheDocument()); await waitFor(() => expect(queryByText(PluginTabLabels.OVERVIEW)).toBeInTheDocument());
// Open the confirmation modal // 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('Uninstall Akumuli')).toBeInTheDocument();
expect(queryByText('Are you sure you want to uninstall this plugin?')).toBeInTheDocument(); expect(queryByText('Are you sure you want to uninstall this plugin?')).toBeInTheDocument();
expect(api.uninstallPlugin).toHaveBeenCalledTimes(0); expect(api.uninstallPlugin).toHaveBeenCalledTimes(0);
// Confirm the uninstall // 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).toHaveBeenCalledTimes(1);
expect(api.uninstallPlugin).toHaveBeenCalledWith(id); expect(api.uninstallPlugin).toHaveBeenCalledWith(id);
@@ -635,7 +635,7 @@ describe('Plugin details page', () => {
await waitFor(() => queryByText('Uninstall')); await waitFor(() => queryByText('Uninstall'));
// Click on "Enable" // 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 // Check if the API request was initiated
expect(api.updatePluginSettings).toHaveBeenCalledTimes(1); expect(api.updatePluginSettings).toHaveBeenCalledTimes(1);
@@ -675,7 +675,7 @@ describe('Plugin details page', () => {
await waitFor(() => queryByText('Uninstall')); await waitFor(() => queryByText('Uninstall'));
// Click on "Disable" // 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 // Check if the API request was initiated
expect(api.updatePluginSettings).toHaveBeenCalledTimes(1); expect(api.updatePluginSettings).toHaveBeenCalledTimes(1);

View File

@@ -83,9 +83,9 @@ describe('ChangePasswordPage', () => {
it('should call changePassword if change password is valid', async () => { it('should call changePassword if change password is valid', async () => {
const { props } = await getTestContext(); const { props } = await getTestContext();
userEvent.type(screen.getByLabelText('Old password'), 'test'); await userEvent.type(screen.getByLabelText('Old password'), 'test');
userEvent.type(screen.getByLabelText('New password'), 'admin'); await userEvent.type(screen.getByLabelText('New password'), 'admin');
userEvent.type(screen.getByLabelText('Confirm password'), 'admin'); await userEvent.type(screen.getByLabelText('Confirm password'), 'admin');
fireEvent.click(screen.getByRole('button', { name: 'Change Password' })); fireEvent.click(screen.getByRole('button', { name: 'Change Password' }));
await waitFor(() => { await waitFor(() => {
expect(props.changePassword).toHaveBeenCalledTimes(1); expect(props.changePassword).toHaveBeenCalledTimes(1);

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { render, screen, waitFor } from '@testing-library/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 { within } from '@testing-library/dom';
import { OrgRole } from '@grafana/data'; import { OrgRole } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
@@ -239,10 +239,10 @@ describe('UserProfileEditPage', () => {
const { props } = await getTestContext(); const { props } = await getTestContext();
const { email, saveProfile } = getSelectors(); const { email, saveProfile } = getSelectors();
userEvent.clear(email()); await userEvent.clear(email());
userEvent.type(email(), 'test@test.se'); await userEvent.type(email(), 'test@test.se');
// TODO remove skipPointerEventsCheck once https://github.com/jsdom/jsdom/issues/3232 is fixed // 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)); await waitFor(() => expect(props.updateUserProfile).toHaveBeenCalledTimes(1));
expect(props.updateUserProfile).toHaveBeenCalledWith({ expect(props.updateUserProfile).toHaveBeenCalledWith({
@@ -261,7 +261,7 @@ describe('UserProfileEditPage', () => {
name: /select organisation/i, name: /select organisation/i,
}); });
userEvent.click(orgsAdminSelectButton()); await userEvent.click(orgsAdminSelectButton());
await waitFor(() => expect(props.changeUserOrg).toHaveBeenCalledTimes(1)); await waitFor(() => expect(props.changeUserOrg).toHaveBeenCalledTimes(1));
expect(props.changeUserOrg).toHaveBeenCalledWith({ expect(props.changeUserOrg).toHaveBeenCalledWith({
@@ -280,7 +280,7 @@ describe('UserProfileEditPage', () => {
name: /revoke user session/i, name: /revoke user session/i,
}); });
userEvent.click(sessionsRevokeButton()); await userEvent.click(sessionsRevokeButton());
await waitFor(() => expect(props.revokeUserSession).toHaveBeenCalledTimes(1)); await waitFor(() => expect(props.revokeUserSession).toHaveBeenCalledTimes(1));
expect(props.revokeUserSession).toHaveBeenCalledWith(0); expect(props.revokeUserSession).toHaveBeenCalledWith(0);

View File

@@ -20,13 +20,13 @@ describe('AdHocFilter', () => {
const { addFilter } = setup(); const { addFilter } = setup();
// Select key // Select key
userEvent.click(screen.getByLabelText('Add Filter')); await userEvent.click(screen.getByLabelText('Add Filter'));
const selectEl = screen.getByTestId('AdHocFilterKey-add-key-wrapper'); const selectEl = screen.getByTestId('AdHocFilterKey-add-key-wrapper');
expect(selectEl).toBeInTheDocument(); expect(selectEl).toBeInTheDocument();
await selectEvent.select(selectEl, 'key3', { container: document.body }); await selectEvent.select(selectEl, 'key3', { container: document.body });
// Select value // Select value
userEvent.click(screen.getByText('select value')); await userEvent.click(screen.getByText('select value'));
// There are already some filters rendered // There are already some filters rendered
const selectEl2 = screen.getAllByTestId('AdHocFilterValue-value-wrapper')[2]; const selectEl2 = screen.getAllByTestId('AdHocFilterValue-value-wrapper')[2];
await selectEvent.select(selectEl2, 'val3', { container: document.body }); await selectEvent.select(selectEl2, 'val3', { container: document.body });
@@ -39,7 +39,7 @@ describe('AdHocFilter', () => {
const { removeFilter } = setup(); const { removeFilter } = setup();
// Select key // Select key
userEvent.click(screen.getByText('key1')); await userEvent.click(screen.getByText('key1'));
const selectEl = screen.getAllByTestId('AdHocFilterKey-key-wrapper')[0]; const selectEl = screen.getAllByTestId('AdHocFilterKey-key-wrapper')[0];
expect(selectEl).toBeInTheDocument(); expect(selectEl).toBeInTheDocument();
await selectEvent.select(selectEl, '-- remove filter --', { container: document.body }); await selectEvent.select(selectEl, '-- remove filter --', { container: document.body });
@@ -52,7 +52,7 @@ describe('AdHocFilter', () => {
const { changeFilter } = setup(); const { changeFilter } = setup();
// Select key // Select key
userEvent.click(screen.getByText('val1')); await userEvent.click(screen.getByText('val1'));
const selectEl = screen.getAllByTestId('AdHocFilterValue-value-wrapper')[0]; const selectEl = screen.getAllByTestId('AdHocFilterValue-value-wrapper')[0];
expect(selectEl).toBeInTheDocument(); expect(selectEl).toBeInTheDocument();
await selectEvent.select(selectEl, 'val4', { container: document.body }); await selectEvent.select(selectEl, 'val4', { container: document.body });

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import * as runtime from '@grafana/runtime'; 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 userEvent from '@testing-library/user-event';
import { VariablesUnknownTable, VariablesUnknownTableProps } from './VariablesUnknownTable'; import { VariablesUnknownTable, VariablesUnknownTableProps } from './VariablesUnknownTable';
@@ -36,25 +36,17 @@ describe('VariablesUnknownTable', () => {
}); });
describe('when expanding the section', () => { 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 () => { it('then it should call getUnknownsNetwork', async () => {
const { getUnknownsNetworkSpy } = await getTestContext(); 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)); await waitFor(() => expect(getUnknownsNetworkSpy).toHaveBeenCalledTimes(1));
}); });
it('then it should report the interaction', async () => { it('then it should report the interaction', async () => {
const { reportInteractionSpy } = await getTestContext(); const { reportInteractionSpy } = 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.getByText('Loading...')).toBeInTheDocument());
expect(reportInteractionSpy).toHaveBeenCalledTimes(1); expect(reportInteractionSpy).toHaveBeenCalledTimes(1);
expect(reportInteractionSpy).toHaveBeenCalledWith('Unknown variables section expanded'); expect(reportInteractionSpy).toHaveBeenCalledWith('Unknown variables section expanded');
@@ -64,14 +56,14 @@ describe('VariablesUnknownTable', () => {
it('then it should not call getUnknownsNetwork', async () => { it('then it should not call getUnknownsNetwork', async () => {
const { getUnknownsNetworkSpy } = await getTestContext(); 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')); await waitFor(() => expect(screen.getByRole('button')).toHaveAttribute('aria-expanded', 'true'));
expect(getUnknownsNetworkSpy).toHaveBeenCalledTimes(1); 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')); 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')); await waitFor(() => expect(screen.getByRole('button')).toHaveAttribute('aria-expanded', 'true'));
expect(getUnknownsNetworkSpy).toHaveBeenCalledTimes(1); expect(getUnknownsNetworkSpy).toHaveBeenCalledTimes(1);
@@ -82,8 +74,7 @@ describe('VariablesUnknownTable', () => {
it('then it should render the correct message', async () => { it('then it should render the correct message', async () => {
await getTestContext(); 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 waitForElementToBeRemoved(() => screen.getByText('Loading...'));
expect(screen.getByText('No renamed or missing variables found.')).toBeInTheDocument(); expect(screen.getByText('No renamed or missing variables found.')).toBeInTheDocument();
}); });
@@ -95,8 +86,7 @@ describe('VariablesUnknownTable', () => {
const usages = [{ variable, nodes: [], edges: [], showGraph: false }]; const usages = [{ variable, nodes: [], edges: [], showGraph: false }];
const { reportInteractionSpy } = await getTestContext({}, usages); const { reportInteractionSpy } = await getTestContext({}, usages);
userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); await userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i }));
await waitForElementToBeRemoved(() => screen.getByText('Loading...'));
expect(screen.queryByText('No renamed or missing variables found.')).not.toBeInTheDocument(); expect(screen.queryByText('No renamed or missing variables found.')).not.toBeInTheDocument();
expect(screen.getByText('Renamed Variable')).toBeInTheDocument(); expect(screen.getByText('Renamed Variable')).toBeInTheDocument();
@@ -117,16 +107,22 @@ describe('VariablesUnknownTable', () => {
it('then it should report slow expansion', async () => { it('then it should report slow expansion', async () => {
const variable = customBuilder().withId('Renamed Variable').withName('Renamed Variable').build(); const variable = customBuilder().withId('Renamed Variable').withName('Renamed Variable').build();
const usages = [{ variable, nodes: [], edges: [], showGraph: false }]; const usages = [{ variable, nodes: [], edges: [], showGraph: false }];
const { reportInteractionSpy } = await getTestContext({}, usages); const { reportInteractionSpy, rerender } = await getTestContext({}, usages);
const dateNowStart = 1000; const dateNowStart = 1000;
const dateNowStop = 2000; const dateNowStop = 2000;
Date.now = jest.fn().mockReturnValueOnce(dateNowStart).mockReturnValue(dateNowStop); Date.now = jest.fn().mockReturnValueOnce(dateNowStart).mockReturnValue(dateNowStop);
userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i })); await userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i }));
await waitForElementToBeRemoved(() => screen.getByText('Loading...')); const props: VariablesUnknownTableProps = {
variables: [],
dashboard: null,
};
await act(async () => {
rerender(<VariablesUnknownTable {...props} />);
});
// make sure we report the interaction for slow expansion // 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[0][0]).toEqual('Unknown variables section expanded');
expect(reportInteractionSpy.mock.calls[1][0]).toEqual('Slow unknown variables expansion'); expect(reportInteractionSpy.mock.calls[1][0]).toEqual('Slow unknown variables expansion');
expect(reportInteractionSpy.mock.calls[1][1]).toEqual({ elapsed: 1000 }); expect(reportInteractionSpy.mock.calls[1][1]).toEqual({ elapsed: 1000 });

View File

@@ -71,11 +71,11 @@ describe('OptionPicker', () => {
expect(getSubMenu('A + C')).toBeInTheDocument(); expect(getSubMenu('A + C')).toBeInTheDocument();
}); });
it('link text should be clickable', () => { it('link text should be clickable', async () => {
const { dispatch } = setupTestContext(); const { dispatch } = setupTestContext();
dispatch.mockClear(); dispatch.mockClear();
userEvent.click(getSubMenu('A + C')); await userEvent.click(getSubMenu('A + C'));
expect(dispatch).toHaveBeenCalledTimes(1); expect(dispatch).toHaveBeenCalledTimes(1);
}); });
}); });
@@ -89,14 +89,14 @@ describe('OptionPicker', () => {
expect(getSubMenu('A + C')).toBeInTheDocument(); expect(getSubMenu('A + C')).toBeInTheDocument();
}); });
it('link text should be clickable', () => { it('link text should be clickable', async () => {
const { dispatch } = setupTestContext({ const { dispatch } = setupTestContext({
variable: defaultVariable, variable: defaultVariable,
pickerState: { id: 'Other' }, pickerState: { id: 'Other' },
}); });
dispatch.mockClear(); dispatch.mockClear();
userEvent.click(getSubMenu('A + C')); await userEvent.click(getSubMenu('A + C'));
expect(dispatch).toHaveBeenCalledTimes(1); expect(dispatch).toHaveBeenCalledTimes(1);
}); });
}); });
@@ -110,13 +110,13 @@ describe('OptionPicker', () => {
expect(screen.getByLabelText(selectors.components.LoadingIndicator.icon)).toBeInTheDocument(); 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({ const { dispatch } = setupTestContext({
variable: { ...defaultVariable, state: LoadingState.Loading }, variable: { ...defaultVariable, state: LoadingState.Loading },
}); });
dispatch.mockClear(); dispatch.mockClear();
userEvent.click(getSubMenu('A + C')); await userEvent.click(getSubMenu('A + C'));
expect(dispatch).toHaveBeenCalledTimes(0); expect(dispatch).toHaveBeenCalledTimes(0);
}); });
}); });

View File

@@ -69,13 +69,13 @@ describe('QueryVariableEditor', () => {
${'regex'} | ${'onPropChange'} | ${[{ propName: 'regex', propValue: 't', updateOptions: true }]} ${'regex'} | ${'onPropChange'} | ${[{ propName: 'regex', propValue: 't', updateOptions: true }]}
`( `(
'$fieldName field and tabs away then $propName should be called with correct args', '$fieldName field and tabs away then $propName should be called with correct args',
({ fieldName, propName, expectedArgs }) => { async ({ fieldName, propName, expectedArgs }) => {
const { props } = setupTestContext({}); const { props } = setupTestContext({});
const propUnderTest = props[propName]; const propUnderTest = props[propName];
const fieldAccessor = fieldAccessors[fieldName]; const fieldAccessor = fieldAccessors[fieldName];
userEvent.type(fieldAccessor(), 't'); await userEvent.type(fieldAccessor(), 't');
userEvent.tab(); await userEvent.tab();
expect(propUnderTest).toHaveBeenCalledTimes(1); expect(propUnderTest).toHaveBeenCalledTimes(1);
expect(propUnderTest).toHaveBeenCalledWith(...expectedArgs); expect(propUnderTest).toHaveBeenCalledWith(...expectedArgs);
@@ -90,14 +90,14 @@ describe('QueryVariableEditor', () => {
${'regex'} | ${'onPropChange'} ${'regex'} | ${'onPropChange'}
`( `(
'$fieldName field but reverts the change and tabs away then $propName should not be called', '$fieldName field but reverts the change and tabs away then $propName should not be called',
({ fieldName, propName }) => { async ({ fieldName, propName }) => {
const { props } = setupTestContext({}); const { props } = setupTestContext({});
const propUnderTest = props[propName]; const propUnderTest = props[propName];
const fieldAccessor = fieldAccessors[fieldName]; const fieldAccessor = fieldAccessors[fieldName];
userEvent.type(fieldAccessor(), 't'); await userEvent.type(fieldAccessor(), 't');
userEvent.type(fieldAccessor(), '{backspace}'); await userEvent.type(fieldAccessor(), '{backspace}');
userEvent.tab(); await userEvent.tab();
expect(propUnderTest).not.toHaveBeenCalled(); expect(propUnderTest).not.toHaveBeenCalled();
} }

View File

@@ -1,5 +1,5 @@
import React from 'react'; 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 { setupMockedDataSource } from '../../__mocks__/CloudWatchDataSource';
import { CloudWatchMetricsQuery } from '../../types'; import { CloudWatchMetricsQuery } from '../../types';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
@@ -60,7 +60,7 @@ describe('Dimensions', () => {
const onChange = jest.fn(); const onChange = jest.fn();
render(<Dimensions {...props} query={props.query} onChange={onChange} dimensionKeys={[]} />); render(<Dimensions {...props} query={props.query} onChange={onChange} dimensionKeys={[]} />);
userEvent.click(screen.getByLabelText('Add')); await userEvent.click(screen.getByLabelText('Add'));
expect(screen.getByTestId('cloudwatch-dimensions-filter-item')).toBeInTheDocument(); expect(screen.getByTestId('cloudwatch-dimensions-filter-item')).toBeInTheDocument();
expect(onChange).not.toHaveBeenCalled(); expect(onChange).not.toHaveBeenCalled();
}); });
@@ -74,13 +74,13 @@ describe('Dimensions', () => {
<Dimensions {...props} query={props.query} onChange={onChange} dimensionKeys={[]} /> <Dimensions {...props} query={props.query} onChange={onChange} dimensionKeys={[]} />
); );
userEvent.click(screen.getByLabelText('Add')); await userEvent.click(screen.getByLabelText('Add'));
const filterItemElement = screen.getByTestId('cloudwatch-dimensions-filter-item'); const filterItemElement = screen.getByTestId('cloudwatch-dimensions-filter-item');
expect(filterItemElement).toBeInTheDocument(); expect(filterItemElement).toBeInTheDocument();
const keyElement = container.querySelector('#cloudwatch-dimensions-filter-item-key'); const keyElement = container.querySelector('#cloudwatch-dimensions-filter-item-key');
expect(keyElement).toBeInTheDocument(); expect(keyElement).toBeInTheDocument();
userEvent.type(keyElement!, 'my-key'); await userEvent.type(keyElement!, 'my-key');
fireEvent.keyDown(keyElement!, { keyCode: 13 }); fireEvent.keyDown(keyElement!, { keyCode: 13 });
expect(onChange).not.toHaveBeenCalled(); expect(onChange).not.toHaveBeenCalled();
}); });
@@ -95,24 +95,20 @@ describe('Dimensions', () => {
); );
const label = await screen.findByLabelText('Add'); const label = await screen.findByLabelText('Add');
userEvent.click(label); await userEvent.click(label);
const filterItemElement = screen.getByTestId('cloudwatch-dimensions-filter-item'); const filterItemElement = screen.getByTestId('cloudwatch-dimensions-filter-item');
expect(filterItemElement).toBeInTheDocument(); expect(filterItemElement).toBeInTheDocument();
const keyElement = container.querySelector('#cloudwatch-dimensions-filter-item-key'); const keyElement = container.querySelector('#cloudwatch-dimensions-filter-item-key');
expect(keyElement).toBeInTheDocument(); expect(keyElement).toBeInTheDocument();
await act(async () => { await userEvent.type(keyElement!, 'my-key');
userEvent.type(keyElement!, 'my-key'); fireEvent.keyDown(keyElement!, { keyCode: 13 });
fireEvent.keyDown(keyElement!, { keyCode: 13 });
});
expect(onChange).not.toHaveBeenCalled(); expect(onChange).not.toHaveBeenCalled();
const valueElement = container.querySelector('#cloudwatch-dimensions-filter-item-value'); const valueElement = container.querySelector('#cloudwatch-dimensions-filter-item-value');
expect(valueElement).toBeInTheDocument(); expect(valueElement).toBeInTheDocument();
await act(async () => { await userEvent.type(valueElement!, 'my-value');
userEvent.type(valueElement!, 'my-value'); fireEvent.keyDown(valueElement!, { keyCode: 13 });
fireEvent.keyDown(valueElement!, { keyCode: 13 });
});
expect(onChange).not.toHaveBeenCalledWith({ expect(onChange).not.toHaveBeenCalledWith({
...props.query, ...props.query,
dimensions: { dimensions: {

View File

@@ -47,7 +47,7 @@ describe('MetricStatEditor', () => {
const statisticElement = await screen.findByLabelText('Statistic'); const statisticElement = await screen.findByLabelText('Statistic');
expect(statisticElement).toBeInTheDocument(); expect(statisticElement).toBeInTheDocument();
userEvent.type(statisticElement, statistic); await userEvent.type(statisticElement, statistic);
fireEvent.keyDown(statisticElement, { keyCode: 13 }); fireEvent.keyDown(statisticElement, { keyCode: 13 });
expect(onChange).toHaveBeenCalledWith({ ...props.query, statistic }); expect(onChange).toHaveBeenCalledWith({ ...props.query, statistic });
expect(onRunQuery).toHaveBeenCalled(); expect(onRunQuery).toHaveBeenCalled();
@@ -62,7 +62,7 @@ describe('MetricStatEditor', () => {
const statisticElement = await screen.findByLabelText('Statistic'); const statisticElement = await screen.findByLabelText('Statistic');
expect(statisticElement).toBeInTheDocument(); expect(statisticElement).toBeInTheDocument();
userEvent.type(statisticElement, statistic); await userEvent.type(statisticElement, statistic);
fireEvent.keyDown(statisticElement, { keyCode: 13 }); fireEvent.keyDown(statisticElement, { keyCode: 13 });
expect(onChange).not.toHaveBeenCalled(); expect(onChange).not.toHaveBeenCalled();
expect(onRunQuery).not.toHaveBeenCalled(); expect(onRunQuery).not.toHaveBeenCalled();

View File

@@ -79,7 +79,7 @@ describe('DashboardQueryEditor', () => {
); );
const select = screen.getByText('Choose panel'); const select = screen.getByText('Choose panel');
userEvent.click(select); await userEvent.click(select);
const myFirstPanel = await screen.findByText('My first panel'); const myFirstPanel = await screen.findByText('My first panel');
expect(myFirstPanel).toBeInTheDocument(); expect(myFirstPanel).toBeInTheDocument();
@@ -102,7 +102,7 @@ describe('DashboardQueryEditor', () => {
); );
const select = screen.getByText('Choose panel'); const select = screen.getByText('Choose panel');
userEvent.click(select); await userEvent.click(select);
expect(screen.queryByText('My first panel')).not.toBeInTheDocument(); expect(screen.queryByText('My first panel')).not.toBeInTheDocument();

View File

@@ -5,7 +5,7 @@ import { useCreatableSelectPersistedBehaviour } from './useCreatableSelectPersis
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
describe('useCreatableSelectPersistedBehaviour', () => { describe('useCreatableSelectPersistedBehaviour', () => {
it('Should make a Select accept custom values', () => { it('Should make a Select accept custom values', async () => {
const MyComp = (_: { force?: boolean }) => ( const MyComp = (_: { force?: boolean }) => (
<InlineField label="label"> <InlineField label="label">
<Select <Select
@@ -25,28 +25,28 @@ describe('useCreatableSelectPersistedBehaviour', () => {
expect(input).toBeInTheDocument(); expect(input).toBeInTheDocument();
// we open the menu // we open the menu
userEvent.click(input); await userEvent.click(input);
expect(screen.getByText('Option 1')).toBeInTheDocument(); expect(screen.getByText('Option 1')).toBeInTheDocument();
// we type in the input 'Option 2', which should prompt an option creation // we type in the input 'Option 2', which should prompt an option creation
userEvent.type(input, 'Option 2'); await userEvent.type(input, 'Option 2');
const creatableOption = screen.getByLabelText('Select option'); const creatableOption = screen.getByLabelText('Select option');
expect(creatableOption).toHaveTextContent('Create: Option 2'); expect(creatableOption).toHaveTextContent('Create: Option 2');
// we click on the creatable option to trigger its creation // we click on the creatable option to trigger its creation
userEvent.click(creatableOption); await userEvent.click(creatableOption);
// Forcing a rerender // Forcing a rerender
rerender(<MyComp force={true} />); rerender(<MyComp force={true} />);
// we open the menu again // we open the menu again
userEvent.click(input); await userEvent.click(input);
// the created option should be available // the created option should be available
expect(screen.getByText('Option 2')).toBeInTheDocument(); expect(screen.getByText('Option 2')).toBeInTheDocument();
}); });
it('Should handle onChange properly', () => { it('Should handle onChange properly', async () => {
const onChange = jest.fn(); const onChange = jest.fn();
const MyComp = () => ( const MyComp = () => (
<InlineField label="label"> <InlineField label="label">
@@ -67,28 +67,28 @@ describe('useCreatableSelectPersistedBehaviour', () => {
expect(input).toBeInTheDocument(); expect(input).toBeInTheDocument();
// we open the menu // we open the menu
userEvent.click(input); await userEvent.click(input);
const option1 = screen.getByText('Option 1'); const option1 = screen.getByText('Option 1');
expect(option1).toBeInTheDocument(); expect(option1).toBeInTheDocument();
// Should call onChange when selecting an already existing option // Should call onChange when selecting an already existing option
userEvent.click(option1); await userEvent.click(option1);
expect(onChange).toHaveBeenLastCalledWith( expect(onChange).toHaveBeenLastCalledWith(
{ value: 'Option 1', label: 'Option 1' }, { value: 'Option 1', label: 'Option 1' },
{ action: 'select-option', name: undefined, option: undefined } { action: 'select-option', name: undefined, option: undefined }
); );
userEvent.click(input); await userEvent.click(input);
// we type in the input 'Option 2', which should prompt an option creation // we type in the input 'Option 2', which should prompt an option creation
userEvent.type(input, 'Option 2'); await userEvent.type(input, 'Option 2');
userEvent.click(screen.getByLabelText('Select option')); await userEvent.click(screen.getByLabelText('Select option'));
expect(onChange).toHaveBeenLastCalledWith({ value: 'Option 2' }); expect(onChange).toHaveBeenLastCalledWith({ value: 'Option 2' });
}); });
it('Should create an option for value if value is not in options', () => { it('Should create an option for value if value is not in options', async () => {
const MyComp = (_: { force?: boolean }) => ( const MyComp = (_: { force?: boolean }) => (
<InlineField label="label"> <InlineField label="label">
<Select <Select
@@ -109,7 +109,7 @@ describe('useCreatableSelectPersistedBehaviour', () => {
expect(input).toBeInTheDocument(); expect(input).toBeInTheDocument();
// we open the menu // we open the menu
userEvent.click(input); await userEvent.click(input);
// we expect 2 elemnts having "Option 2": the input itself and the option. // we expect 2 elemnts having "Option 2": the input itself and the option.
expect(screen.getAllByText('Option 2')).toHaveLength(2); expect(screen.getAllByText('Option 2')).toHaveLength(2);

View File

@@ -83,9 +83,9 @@ describe('MetricsQueryEditor', () => {
const checkbox = await screen.findByLabelText('web-server'); const checkbox = await screen.findByLabelText('web-server');
expect(checkbox).toBeInTheDocument(); expect(checkbox).toBeInTheDocument();
expect(checkbox).not.toBeChecked(); expect(checkbox).not.toBeChecked();
userEvent.click(checkbox); await userEvent.click(checkbox);
expect(checkbox).toBeChecked(); 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).toBeCalledTimes(1);
expect(onChange).toBeCalledWith( expect(onChange).toBeCalledWith(
@@ -136,9 +136,9 @@ describe('MetricsQueryEditor', () => {
const checkbox = await screen.findByLabelText('db-server'); const checkbox = await screen.findByLabelText('db-server');
expect(checkbox).toBeInTheDocument(); expect(checkbox).toBeInTheDocument();
expect(checkbox).not.toBeChecked(); expect(checkbox).not.toBeChecked();
userEvent.click(checkbox); await userEvent.click(checkbox);
expect(checkbox).toBeChecked(); 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).toBeCalledTimes(1);
expect(onChange).toBeCalledWith( expect(onChange).toBeCalledWith(

View File

@@ -100,7 +100,7 @@ describe('AzureMonitor ResourcePicker', () => {
advancedSection.click(); advancedSection.click();
const advancedInput = await screen.findByLabelText('Resource URI'); 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' }); const applyButton = screen.getByRole('button', { name: 'Apply' });
applyButton.click(); applyButton.click();

View File

@@ -137,7 +137,7 @@ describe('VariableEditor:', () => {
}; };
render(<VariableEditor {...props} />); render(<VariableEditor {...props} />);
await waitFor(() => screen.queryByText('Grafana template variable function')); 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(); expect(screen.getByDisplayValue('Subscriptions()')).toBeInTheDocument();
screen.getByDisplayValue('Subscriptions()').blur(); screen.getByDisplayValue('Subscriptions()').blur();
await waitFor(() => screen.queryByText('None')); await waitFor(() => screen.queryByText('None'));

View File

@@ -91,7 +91,7 @@ describe('Render', () => {
updateOptions: onUpdate, updateOptions: onUpdate,
})); }));
expect(screen.queryByText('is no longer supported', { exact: false })).toBeInTheDocument(); 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(); expect(onUpdate).toHaveBeenCalled();
const newOpts = onUpdate.mock.calls[0][0]({}); const newOpts = onUpdate.mock.calls[0][0]({});
expect(newOpts).toEqual({ jsonData: { azureLogAnalyticsSameAs: true } }); expect(newOpts).toEqual({ jsonData: { azureLogAnalyticsSameAs: true } });

View File

@@ -74,7 +74,7 @@ describe('RawInfluxQLEditor', () => {
// value before // value before
expect(queryTextarea).toHaveValue('test query 1'); 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. // the field should have a new value, but no onChange yet.
expect(queryTextarea).toHaveValue('test query 1new changes'); expect(queryTextarea).toHaveValue('test query 1new changes');
@@ -97,7 +97,7 @@ describe('RawInfluxQLEditor', () => {
// value before // value before
expect(aliasInput).toHaveValue('alias42'); 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. // the field should have a new value, but no onChange yet.
expect(aliasInput).toHaveValue('alias42new changes'); expect(aliasInput).toHaveValue('alias42new changes');

View File

@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { InfluxQuery } from '../../types'; import { InfluxQuery } from '../../types';
import InfluxDatasource from '../../datasource'; 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 userEvent from '@testing-library/user-event';
import { Editor } from './Editor'; import { Editor } from './Editor';
import * as mockedMeta from '../../influxQLMetadataQuery'; import * as mockedMeta from '../../influxQLMetadataQuery';
@@ -95,9 +95,7 @@ describe('InfluxDB InfluxQL Visual Editor field-filtering', () => {
expect(mockedMeta.getTagKeysForMeasurementAndTags).toHaveBeenCalledTimes(1); expect(mockedMeta.getTagKeysForMeasurementAndTags).toHaveBeenCalledTimes(1);
// we click the WHERE/cpu button // we click the WHERE/cpu button
await act(async () => { await userEvent.click(screen.getByRole('button', { name: 'cpu' }));
userEvent.click(screen.getByRole('button', { name: 'cpu' }));
});
// and verify getTagKeysForMeasurementAndTags was called again, // and verify getTagKeysForMeasurementAndTags was called again,
// and in the tags-param we did not receive the `field1` part. // 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); expect((mockedMeta.getTagKeysForMeasurementAndTags as jest.Mock).mock.calls[1][2]).toStrictEqual(ONLY_TAGS);
// now we click on the WHERE/host2 button // now we click on the WHERE/host2 button
await act(async () => { await userEvent.click(screen.getByRole('button', { name: 'host2' }));
userEvent.click(screen.getByRole('button', { name: 'host2' }));
});
// verify `getTagValues` was called once, and in the tags-param we did not receive `field1` // verify `getTagValues` was called once, and in the tags-param we did not receive `field1`
expect(mockedMeta.getTagValues).toHaveBeenCalledTimes(1); expect(mockedMeta.getTagValues).toHaveBeenCalledTimes(1);
expect((mockedMeta.getTagValues as jest.Mock).mock.calls[0][3]).toStrictEqual(ONLY_TAGS); expect((mockedMeta.getTagValues as jest.Mock).mock.calls[0][3]).toStrictEqual(ONLY_TAGS);
// now we click on the FROM/cpudata button // now we click on the FROM/cpudata button
await act(async () => { await userEvent.click(screen.getByRole('button', { name: 'cpudata' }));
userEvent.click(screen.getByRole('button', { name: 'cpudata' }));
});
// verify `getTagValues` was called once, and in the tags-param we did not receive `field1` // verify `getTagValues` was called once, and in the tags-param we did not receive `field1`
expect(mockedMeta.getAllMeasurementsForTags).toHaveBeenCalledTimes(1); expect(mockedMeta.getAllMeasurementsForTags).toHaveBeenCalledTimes(1);

View File

@@ -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 { backendSrv } from 'app/core/services/backend_srv';
import { createFetchResponse } from 'test/helpers/createFetchResponse'; import { createFetchResponse } from 'test/helpers/createFetchResponse';
import { DataQueryRequest, DataSourceInstanceSettings, dateTime, PluginType } from '@grafana/data'; import { DataQueryRequest, DataSourceInstanceSettings, dateTime, PluginType } from '@grafana/data';
@@ -9,6 +9,7 @@ import React from 'react';
import SearchForm from './SearchForm'; import SearchForm from './SearchForm';
import { testResponse } from '../testResponse'; import { testResponse } from '../testResponse';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import { UserEvent } from '@testing-library/user-event/dist/types/setup';
describe('SearchForm', () => { describe('SearchForm', () => {
it('should call the `onChange` function on click of the Input', async () => { 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' })); const asyncServiceSelect = await waitFor(() => screen.getByRole('combobox', { name: 'select-service-name' }));
expect(asyncServiceSelect).toBeInTheDocument(); expect(asyncServiceSelect).toBeInTheDocument();
userEvent.click(asyncServiceSelect); await userEvent.click(asyncServiceSelect);
const jaegerService = await screen.findByText('jaeger-query'); const jaegerService = await screen.findByText('jaeger-query');
expect(jaegerService).toBeInTheDocument(); expect(jaegerService).toBeInTheDocument();
@@ -68,13 +69,21 @@ describe('SearchForm', () => {
}); });
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 () => { it('should show loader if there is a delay fetching options', async () => {
const promise = Promise.resolve(); const handleOnChange = jest.fn();
const handleOnChange = jest.fn(() => {
setTimeout(() => {
return promise;
}, 3000);
});
const query = { const query = {
...defaultQuery, ...defaultQuery,
targets: [ targets: [
@@ -91,12 +100,19 @@ describe('SearchForm', () => {
render(<SearchForm datasource={ds} query={query} onChange={handleOnChange} />); render(<SearchForm datasource={ds} query={query} onChange={handleOnChange} />);
jest.spyOn(ds, 'metadataRequest').mockImplementation(() => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(['jaeger-query']);
}, 3000);
});
});
const asyncServiceSelect = screen.getByRole('combobox', { name: 'select-service-name' }); const asyncServiceSelect = screen.getByRole('combobox', { name: 'select-service-name' });
userEvent.click(asyncServiceSelect); await user.click(asyncServiceSelect);
const loader = screen.getByText('Loading options...'); expect(screen.getByText('Loading options...')).toBeInTheDocument();
expect(loader).toBeInTheDocument(); jest.advanceTimersByTime(3000);
await act(() => promise); await waitFor(() => expect(screen.queryByText('Loading options...')).not.toBeInTheDocument());
}); });
}); });

View File

@@ -1,5 +1,5 @@
import React from 'react'; 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 userEvent from '@testing-library/user-event';
import { createTheme } from '@grafana/data'; import { createTheme } from '@grafana/data';
@@ -118,9 +118,9 @@ describe('LokiLabelBrowser', () => {
}; };
// Clear label selection manually because it's saved in localStorage // Clear label selection manually because it's saved in localStorage
afterEach(() => { afterEach(async () => {
const clearBtn = screen.getByLabelText('Selector clear button'); 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 () => { it('renders and loader shows when empty, and then first set of labels', async () => {
@@ -143,10 +143,10 @@ describe('LokiLabelBrowser', () => {
const props = setupProps(); const props = setupProps();
render(<UnthemedLokiLabelBrowser {...props} />); render(<UnthemedLokiLabelBrowser {...props} />);
// Selecting label2 // 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(); expect(screen.queryByRole('list', { name: /Values/ })).not.toBeInTheDocument();
userEvent.click(label2); await userEvent.click(label2);
expect(screen.queryByRole('option', { name: /label2/, selected: true })).toBeInTheDocument(); expect(screen.queryByRole('option', { name: 'label2', selected: true })).toBeInTheDocument();
// List of values for label2 appears // List of values for label2 appears
expect(await screen.findAllByRole('list')).toHaveLength(1); expect(await screen.findAllByRole('list')).toHaveLength(1);
expect(screen.queryByLabelText(/Values for/)).toHaveTextContent('label2'); expect(screen.queryByLabelText(/Values for/)).toHaveTextContent('label2');
@@ -154,24 +154,24 @@ describe('LokiLabelBrowser', () => {
expect(screen.queryByRole('option', { name: 'value2-2' })).toBeInTheDocument(); expect(screen.queryByRole('option', { name: 'value2-2' })).toBeInTheDocument();
expect(screen.queryByLabelText('selector')).toHaveTextContent('{}'); expect(screen.queryByLabelText('selector')).toHaveTextContent('{}');
// Selecting label1, list for its values appears // Selecting label1, list for its values appears
const label1 = await screen.findByRole('option', { name: /label1/, selected: false }); const label1 = await screen.findByRole('option', { name: 'label1', selected: false });
userEvent.click(label1); await userEvent.click(label1);
expect(screen.queryByRole('option', { name: /label1/, selected: true })).toBeInTheDocument(); expect(screen.queryByRole('option', { name: 'label1', selected: true })).toBeInTheDocument();
await screen.findByLabelText('Values for label1'); await screen.findByLabelText('Values for label1');
expect(await screen.findAllByRole('list')).toHaveLength(2); expect(await screen.findAllByRole('list')).toHaveLength(2);
// Selecting value2-2 of label2 // Selecting value2-2 of label2
const value = await screen.findByRole('option', { name: 'value2-2', selected: false }); 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 }); await screen.findByRole('option', { name: 'value2-2', selected: true });
expect(screen.queryByLabelText('selector')).toHaveTextContent('{label2="value2-2"}'); expect(screen.queryByLabelText('selector')).toHaveTextContent('{label2="value2-2"}');
// Selecting value2-1 of label2, both values now selected // Selecting value2-1 of label2, both values now selected
const value2 = await screen.findByRole('option', { name: 'value2-1', selected: false }); 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.findByRole('option', {name: 'value2-1', selected: true});
await screen.findByText('{label2=~"value2-1|value2-2"}'); await screen.findByText('{label2=~"value2-1|value2-2"}');
// Deselecting value2-2, one value should remain // Deselecting value2-2, one value should remain
const selectedValue = await screen.findByRole('option', { name: 'value2-2', selected: true }); 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-1', selected: true });
await screen.findByRole('option', { name: 'value2-2', selected: false }); await screen.findByRole('option', { name: 'value2-2', selected: false });
expect(screen.queryByLabelText('selector')).toHaveTextContent('{label2="value2-1"}'); expect(screen.queryByLabelText('selector')).toHaveTextContent('{label2="value2-1"}');
@@ -183,28 +183,30 @@ describe('LokiLabelBrowser', () => {
// Selecting label2 // Selecting label2
const label2 = await screen.findByRole('option', { name: /label2/, selected: false }); const label2 = await screen.findByRole('option', { name: /label2/, selected: false });
userEvent.click(label2); await userEvent.click(label2);
// List of values for label2 appears // 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('{}'); expect(screen.queryByLabelText('selector')).toHaveTextContent('{}');
// Selecting label1, list for its values appears // Selecting label1, list for its values appears
const label1 = await screen.findByRole('option', { name: /label1/, selected: false }); 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 label1');
expect(await screen.findAllByRole('list')).toHaveLength(2); expect(await screen.findAllByRole('list')).toHaveLength(2);
// Selecting value2-1 of label2 // Selecting value2-1 of label2
const value2 = await screen.findByRole('option', { name: 'value2-1', selected: false }); const value2 = await screen.findByRole('option', { name: 'value2-1', selected: false });
userEvent.click(value2); await userEvent.click(value2);
await screen.findByText('{label2="value2-1"}'); await screen.findByText('{label2="value2-1"}');
// Selecting value from label1 for combined selector // Selecting value from label1 for combined selector
const value1 = await screen.findByRole('option', { name: 'value1-2', selected: false }); 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.findByRole('option', { name: 'value1-2', selected: true });
await screen.findByText('{label1="value1-2",label2="value2-1"}'); await screen.findByText('{label1="value1-2",label2="value2-1"}');
// Deselect label1 should remove label and value // Deselect label1 should remove label and value
const selectedLabel = (await screen.findAllByRole('option', { name: /label1/, selected: true }))[0]; 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 }); await screen.findByRole('option', { name: /label1/, selected: false });
expect(await screen.findAllByRole('list')).toHaveLength(1); expect(await screen.findAllByRole('list')).toHaveLength(1);
expect(screen.queryByLabelText('selector')).toHaveTextContent('{label2="value2-1"}'); expect(screen.queryByLabelText('selector')).toHaveTextContent('{label2="value2-1"}');
@@ -215,25 +217,27 @@ describe('LokiLabelBrowser', () => {
render(<UnthemedLokiLabelBrowser {...props} />); render(<UnthemedLokiLabelBrowser {...props} />);
// Selecting label2 // Selecting label2
const label2 = await screen.findByRole('option', { name: /label2/, selected: false }); const label2 = await screen.findByRole('option', { name: 'label2', selected: false });
userEvent.click(label2); await userEvent.click(label2);
// List of values for label2 appears // 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('{}'); expect(screen.queryByLabelText('selector')).toHaveTextContent('{}');
// Selecting label1, list for its values appears // Selecting label1, list for its values appears
const label1 = await screen.findByRole('option', { name: /label1/, selected: false }); 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 label1');
expect(await screen.findAllByRole('list')).toHaveLength(2); expect(await screen.findAllByRole('list')).toHaveLength(2);
// Selecting value2-1 of label2 // Selecting value2-1 of label2
const value2 = await screen.findByRole('option', { name: 'value2-1', selected: false }); const value2 = await screen.findByRole('option', { name: 'value2-1', selected: false });
userEvent.click(value2); await userEvent.click(value2);
await screen.findByText('{label2="value2-1"}'); await screen.findByText('{label2="value2-1"}');
// Clear selector // Clear selector
const clearBtn = screen.getByLabelText('Selector clear button'); const clearBtn = screen.getByLabelText('Selector clear button');
userEvent.click(clearBtn); await userEvent.click(clearBtn);
await screen.findByRole('option', { name: /label2/, selected: false }); await screen.findByRole('option', { name: 'label2', selected: false });
expect(screen.queryByLabelText('selector')).toHaveTextContent('{}'); expect(screen.queryByLabelText('selector')).toHaveTextContent('{}');
}); });
@@ -242,14 +246,14 @@ describe('LokiLabelBrowser', () => {
render(<UnthemedLokiLabelBrowser {...props} />); render(<UnthemedLokiLabelBrowser {...props} />);
// Selecting label2 and label1 // Selecting label2 and label1
const label2 = await screen.findByRole('option', { name: /label2/, selected: false }); 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 }); 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 label1');
await screen.findByLabelText('Values for label2'); await screen.findByLabelText('Values for label2');
expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(4); expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(4);
// Typing '1' to filter for values // 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.getByLabelText('Filter expression for values')).toHaveValue('val1');
expect(screen.queryByRole('option', { name: 'value2-2' })).not.toBeInTheDocument(); expect(screen.queryByRole('option', { name: 'value2-2' })).not.toBeInTheDocument();
expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(3); expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(3);
@@ -260,25 +264,25 @@ describe('LokiLabelBrowser', () => {
render(<UnthemedLokiLabelBrowser {...props} />); render(<UnthemedLokiLabelBrowser {...props} />);
// Selecting label2 and label1 // Selecting label2 and label1
const label2 = await screen.findByRole('option', { name: /label2/, selected: false }); 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 }); 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 label1');
await screen.findByLabelText('Values for label2'); await screen.findByLabelText('Values for label2');
expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(4); expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(4);
expect(screen.queryByRole('option', { name: /label3/ })).toHaveTextContent('label3'); expect(screen.queryByRole('option', { name: /label3/ })).toHaveTextContent('label3');
// Click value1-1 which triggers facetting for value3-x, and still show all value1-x // 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 }); const value1 = await screen.findByRole('option', { name: 'value1-1', selected: false });
userEvent.click(value1); await userEvent.click(value1);
await waitForElementToBeRemoved(screen.queryByRole('option', { name: 'value2-2' })); await waitFor(() => expect(screen.queryByRole('option', { name: 'value2-2' })).not.toBeInTheDocument());
expect(screen.queryByRole('option', { name: 'value1-2' })).toBeInTheDocument(); expect(screen.queryByRole('option', { name: 'value1-2' })).toBeInTheDocument();
expect(screen.queryByLabelText('selector')).toHaveTextContent('{label1="value1-1"}'); expect(screen.queryByLabelText('selector')).toHaveTextContent('{label1="value1-1"}');
expect(screen.queryByRole('option', { name: /label3/ })).toHaveTextContent('label3 (1)'); expect(screen.queryByRole('option', { name: /label3/ })).toHaveTextContent('label3 (1)');
// Click value1-2 for which facetting will allow more values for value3-x // Click value1-2 for which facetting will allow more values for value3-x
const value12 = await screen.findByRole('option', { name: 'value1-2', selected: false }); 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: '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'); await screen.findByLabelText('Values for label3');
expect(screen.queryByRole('option', { name: 'value1-1', selected: true })).toBeInTheDocument(); expect(screen.queryByRole('option', { name: 'value1-1', selected: true })).toBeInTheDocument();
expect(screen.queryByRole('option', { name: 'value1-2', selected: true })).toBeInTheDocument(); expect(screen.queryByRole('option', { name: 'value1-2', selected: true })).toBeInTheDocument();

View File

@@ -15,10 +15,10 @@ describe('LokiQueryBuilder', () => {
it('tries to load labels when no labels are selected', async () => { it('tries to load labels when no labels are selected', async () => {
const { datasource } = setup(); const { datasource } = setup();
datasource.languageProvider.fetchSeriesLabels = jest.fn().mockReturnValue({ job: ['a'], instance: ['b'] }); 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 labels = screen.getByText(/Labels/);
const selects = getAllByRole(labels.parentElement!.parentElement!.parentElement!, 'combobox'); const selects = getAllByRole(labels.parentElement!.parentElement!.parentElement!, 'combobox');
userEvent.click(selects[3]); await userEvent.click(selects[3]);
await waitFor(() => expect(screen.getByText('job')).toBeInTheDocument()); await waitFor(() => expect(screen.getByText('job')).toBeInTheDocument());
}); });

View File

@@ -30,7 +30,7 @@ describe('LokiQueryBuilderContainer', () => {
}; };
render(<LokiQueryBuilderContainer {...props} />); render(<LokiQueryBuilderContainer {...props} />);
expect(screen.getByText('testjob')).toBeInTheDocument(); expect(screen.getByText('testjob')).toBeInTheDocument();
addOperation('Range functions', 'Rate'); await addOperation('Range functions', 'Rate');
expect(props.onChange).toBeCalledWith({ expect(props.onChange).toBeCalledWith({
expr: 'rate({job="testjob"} [$__interval])', expr: 'rate({job="testjob"} [$__interval])',
refId: 'A', refId: 'A',

View File

@@ -25,7 +25,7 @@ describe('LokiQueryBuilderOptions', () => {
screen.getByTitle('Click to edit options').click(); screen.getByTitle('Click to edit options').click();
const element = screen.getByLabelText('Legend'); const element = screen.getByLabelText('Legend');
userEvent.type(element, 'asd'); await userEvent.type(element, 'asd');
fireEvent.keyDown(element, { key: 'Enter', code: 'Enter', charCode: 13 }); fireEvent.keyDown(element, { key: 'Enter', code: 'Enter', charCode: 13 });
expect(props.onChange).toHaveBeenCalledWith({ expect(props.onChange).toHaveBeenCalledWith({

View File

@@ -73,7 +73,7 @@ describe('LokiQueryEditorSelector', () => {
it('changes to builder mode', async () => { it('changes to builder mode', async () => {
const { onChange } = renderWithMode(QueryEditorMode.Code); const { onChange } = renderWithMode(QueryEditorMode.Code);
switchToMode(QueryEditorMode.Builder); await switchToMode(QueryEditorMode.Builder);
expect(onChange).toBeCalledWith({ expect(onChange).toBeCalledWith({
refId: 'A', refId: 'A',
expr: defaultQuery.expr, expr: defaultQuery.expr,
@@ -108,7 +108,7 @@ describe('LokiQueryEditorSelector', () => {
it('changes to code mode', async () => { it('changes to code mode', async () => {
const { onChange } = renderWithMode(QueryEditorMode.Builder); const { onChange } = renderWithMode(QueryEditorMode.Builder);
switchToMode(QueryEditorMode.Code); await switchToMode(QueryEditorMode.Code);
expect(onChange).toBeCalledWith({ expect(onChange).toBeCalledWith({
refId: 'A', refId: 'A',
expr: defaultQuery.expr, expr: defaultQuery.expr,
@@ -119,7 +119,7 @@ describe('LokiQueryEditorSelector', () => {
it('changes to explain mode', async () => { it('changes to explain mode', async () => {
const { onChange } = renderWithMode(QueryEditorMode.Code); const { onChange } = renderWithMode(QueryEditorMode.Code);
switchToMode(QueryEditorMode.Explain); await switchToMode(QueryEditorMode.Explain);
expect(onChange).toBeCalledWith({ expect(onChange).toBeCalledWith({
refId: 'A', refId: 'A',
expr: defaultQuery.expr, expr: defaultQuery.expr,
@@ -134,7 +134,7 @@ describe('LokiQueryEditorSelector', () => {
expr: 'rate({instance="host.docker.internal:3000"}[$__interval])', expr: 'rate({instance="host.docker.internal:3000"}[$__interval])',
editorMode: QueryEditorMode.Code, editorMode: QueryEditorMode.Code,
}); });
switchToMode(QueryEditorMode.Builder); await switchToMode(QueryEditorMode.Builder);
rerender( rerender(
<LokiQueryEditorSelector <LokiQueryEditorSelector
{...defaultProps} {...defaultProps}
@@ -178,7 +178,7 @@ function expectExplain() {
expect(screen.getByText(/Fetch all log/)).toBeInTheDocument(); expect(screen.getByText(/Fetch all log/)).toBeInTheDocument();
} }
function switchToMode(mode: QueryEditorMode) { async function switchToMode(mode: QueryEditorMode) {
const label = { const label = {
[QueryEditorMode.Code]: /Code/, [QueryEditorMode.Code]: /Code/,
[QueryEditorMode.Explain]: /Explain/, [QueryEditorMode.Explain]: /Explain/,
@@ -186,5 +186,5 @@ function switchToMode(mode: QueryEditorMode) {
}[mode]; }[mode];
const switchEl = screen.getByLabelText(label); const switchEl = screen.getByLabelText(label);
userEvent.click(switchEl); await userEvent.click(switchEl);
} }

View File

@@ -94,22 +94,22 @@ describe('PromQueryEditorByApp', () => {
expect(queryByTestId(alertingTestIds.editor)).toBeNull(); 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 { getByTestId, onRunQuery } = setup(CoreApp.Explore);
const input = getByTestId('dummy-code-input'); const input = getByTestId('dummy-code-input');
expect(input).toBeInTheDocument(); expect(input).toBeInTheDocument();
userEvent.type(input, 'metric'); await userEvent.type(input, 'metric');
input.blur(); input.blur();
expect(onRunQuery).not.toHaveBeenCalled(); 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 { getByTestId, onRunQuery } = setup(CoreApp.Dashboard);
const input = getByTestId('dummy-code-input'); const input = getByTestId('dummy-code-input');
expect(input).toBeInTheDocument(); expect(input).toBeInTheDocument();
userEvent.type(input, 'metric'); await userEvent.type(input, 'metric');
input.blur(); input.blur();
expect(onRunQuery).toHaveBeenCalled(); expect(onRunQuery).toHaveBeenCalled();
}); });

View File

@@ -1,5 +1,5 @@
import React from 'react'; 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 userEvent from '@testing-library/user-event';
import { getTheme } from '@grafana/ui'; import { getTheme } from '@grafana/ui';
@@ -142,9 +142,9 @@ describe('PrometheusMetricsBrowser', () => {
}; };
// Clear label selection manually because it's saved in localStorage // Clear label selection manually because it's saved in localStorage
afterEach(() => { afterEach(async () => {
const clearBtn = screen.getByLabelText('Selector clear button'); 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 () => { it('renders and loader shows when empty, and then first set of labels', async () => {
@@ -167,35 +167,34 @@ describe('PrometheusMetricsBrowser', () => {
const props = setupProps(); const props = setupProps();
render(<UnthemedPrometheusMetricsBrowser {...props} />); render(<UnthemedPrometheusMetricsBrowser {...props} />);
// Selecting label2 // 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(); expect(screen.queryByRole('list', { name: /Values/ })).not.toBeInTheDocument();
userEvent.click(label2); await userEvent.click(label2);
expect(screen.queryByRole('option', { name: /label2/, selected: true })).toBeInTheDocument(); expect(screen.queryByRole('option', { name: 'label2', selected: true })).toBeInTheDocument();
// List of values for label2 appears // List of values for label2 appears
expect(await screen.findAllByRole('list')).toHaveLength(1);
expect(screen.queryByLabelText(/Values for/)).toHaveTextContent('label2'); expect(screen.queryByLabelText(/Values for/)).toHaveTextContent('label2');
expect(screen.queryByRole('option', { name: 'value2-1' })).toBeInTheDocument(); expect(screen.queryByRole('option', { name: 'value2-1' })).toBeInTheDocument();
expect(screen.queryByRole('option', { name: 'value2-2' })).toBeInTheDocument(); expect(screen.queryByRole('option', { name: 'value2-2' })).toBeInTheDocument();
expect(screen.queryByLabelText('selector')).toHaveTextContent('{}'); expect(screen.queryByLabelText('selector')).toHaveTextContent('{}');
// Selecting label1, list for its values appears // Selecting label1, list for its values appears
const label1 = await screen.findByRole('option', { name: /label1/, selected: false }); const label1 = await screen.findByRole('option', { name: 'label1', selected: false });
userEvent.click(label1); await userEvent.click(label1);
expect(screen.queryByRole('option', { name: /label1/, selected: true })).toBeInTheDocument(); expect(screen.queryByRole('option', { name: 'label1', selected: true })).toBeInTheDocument();
await screen.findByLabelText('Values for label1'); await screen.findByLabelText('Values for label1');
expect(await screen.findAllByRole('list', { name: /Values/ })).toHaveLength(2); expect(await screen.findAllByRole('list', { name: /Values/ })).toHaveLength(2);
// Selecting value2-2 of label2 // Selecting value2-2 of label2
const value = await screen.findByRole('option', { name: 'value2-2', selected: false }); 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 }); await screen.findByRole('option', { name: 'value2-2', selected: true });
expect(screen.queryByLabelText('selector')).toHaveTextContent('{label2="value2-2"}'); expect(screen.queryByLabelText('selector')).toHaveTextContent('{label2="value2-2"}');
// Selecting value2-1 of label2, both values now selected // Selecting value2-1 of label2, both values now selected
const value2 = await screen.findByRole('option', { name: 'value2-1', selected: false }); 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.findByRole('option', {name: 'value2-1', selected: true});
await screen.findByText('{label2=~"value2-1|value2-2"}'); await screen.findByText('{label2=~"value2-1|value2-2"}');
// Deselecting value2-2, one value should remain // Deselecting value2-2, one value should remain
const selectedValue = await screen.findByRole('option', { name: 'value2-2', selected: true }); 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-1', selected: true });
await screen.findByRole('option', { name: 'value2-2', selected: false }); await screen.findByRole('option', { name: 'value2-2', selected: false });
expect(screen.queryByLabelText('selector')).toHaveTextContent('{label2="value2-1"}'); expect(screen.queryByLabelText('selector')).toHaveTextContent('{label2="value2-1"}');
@@ -206,29 +205,31 @@ describe('PrometheusMetricsBrowser', () => {
render(<UnthemedPrometheusMetricsBrowser {...props} />); render(<UnthemedPrometheusMetricsBrowser {...props} />);
// Selecting label2 // Selecting label2
const label2 = await screen.findByRole('option', { name: /label2/, selected: false }); const label2 = await screen.findByRole('option', { name: 'label2', selected: false });
userEvent.click(label2); await userEvent.click(label2);
// List of values for label2 appears // 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('{}'); expect(screen.queryByLabelText('selector')).toHaveTextContent('{}');
// Selecting label1, list for its values appears // Selecting label1, list for its values appears
const label1 = await screen.findByRole('option', { name: /label1/, selected: false }); 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 label1');
expect(await screen.findAllByRole('list', { name: /Values/ })).toHaveLength(2); expect(await screen.findAllByRole('list', { name: /Values/ })).toHaveLength(2);
// Selecting value2-1 of label2 // Selecting value2-1 of label2
const value2 = await screen.findByRole('option', { name: 'value2-1', selected: false }); const value2 = await screen.findByRole('option', { name: 'value2-1', selected: false });
userEvent.click(value2); await userEvent.click(value2);
await screen.findByText('{label2="value2-1"}'); await screen.findByText('{label2="value2-1"}');
// Selecting value from label1 for combined selector // Selecting value from label1 for combined selector
const value1 = await screen.findByRole('option', { name: 'value1-2', selected: false }); 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.findByRole('option', { name: 'value1-2', selected: true });
await screen.findByText('{label1="value1-2",label2="value2-1"}'); await screen.findByText('{label1="value1-2",label2="value2-1"}');
// Deselect label1 should remove label and value // Deselect label1 should remove label and value
const selectedLabel = (await screen.findAllByRole('option', { name: /label1/, selected: true }))[0]; 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 }); await screen.findByRole('option', { name: /label1/, selected: false });
expect(await screen.findAllByRole('list', { name: /Values/ })).toHaveLength(1); expect(await screen.findAllByRole('list', { name: /Values/ })).toHaveLength(1);
expect(screen.queryByLabelText('selector')).toHaveTextContent('{label2="value2-1"}'); expect(screen.queryByLabelText('selector')).toHaveTextContent('{label2="value2-1"}');
@@ -239,25 +240,27 @@ describe('PrometheusMetricsBrowser', () => {
render(<UnthemedPrometheusMetricsBrowser {...props} />); render(<UnthemedPrometheusMetricsBrowser {...props} />);
// Selecting label2 // Selecting label2
const label2 = await screen.findByRole('option', { name: /label2/, selected: false }); const label2 = await screen.findByRole('option', { name: 'label2', selected: false });
userEvent.click(label2); await userEvent.click(label2);
// List of values for label2 appears // 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('{}'); expect(screen.queryByLabelText('selector')).toHaveTextContent('{}');
// Selecting label1, list for its values appears // Selecting label1, list for its values appears
const label1 = await screen.findByRole('option', { name: /label1/, selected: false }); 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 label1');
expect(await screen.findAllByRole('list', { name: /Values/ })).toHaveLength(2); expect(await screen.findAllByRole('list', { name: /Values/ })).toHaveLength(2);
// Selecting value2-1 of label2 // Selecting value2-1 of label2
const value2 = await screen.findByRole('option', { name: 'value2-1', selected: false }); const value2 = await screen.findByRole('option', { name: 'value2-1', selected: false });
userEvent.click(value2); await userEvent.click(value2);
await screen.findByText('{label2="value2-1"}'); await screen.findByText('{label2="value2-1"}');
// Clear selector // Clear selector
const clearBtn = screen.getByLabelText('Selector clear button'); const clearBtn = screen.getByLabelText('Selector clear button');
userEvent.click(clearBtn); await userEvent.click(clearBtn);
await screen.findByRole('option', { name: /label2/, selected: false }); await screen.findByRole('option', { name: 'label2', selected: false });
expect(screen.queryByLabelText('selector')).toHaveTextContent('{}'); expect(screen.queryByLabelText('selector')).toHaveTextContent('{}');
}); });
@@ -266,14 +269,14 @@ describe('PrometheusMetricsBrowser', () => {
render(<UnthemedPrometheusMetricsBrowser {...props} />); render(<UnthemedPrometheusMetricsBrowser {...props} />);
// Selecting label2 and label1 // Selecting label2 and label1
const label2 = await screen.findByRole('option', { name: /label2/, selected: false }); 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 }); 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 label1');
await screen.findByLabelText('Values for label2'); await screen.findByLabelText('Values for label2');
expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(4); expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(4);
// Typing '1' to filter for values // 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(screen.getByLabelText('Filter expression for label values')).toHaveValue('1');
expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(3); expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(3);
expect(screen.queryByRole('option', { name: 'value2-2' })).not.toBeInTheDocument(); expect(screen.queryByRole('option', { name: 'value2-2' })).not.toBeInTheDocument();
@@ -284,26 +287,26 @@ describe('PrometheusMetricsBrowser', () => {
render(<UnthemedPrometheusMetricsBrowser {...props} />); render(<UnthemedPrometheusMetricsBrowser {...props} />);
// Selecting label2 and label1 // Selecting label2 and label1
const label2 = await screen.findByRole('option', { name: /label2/, selected: false }); 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 }); 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 label1');
await screen.findByLabelText('Values for label2'); await screen.findByLabelText('Values for label2');
expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(4); expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(4);
expect(screen.queryByRole('option', { name: /label3/ })).toHaveTextContent('label3'); expect(screen.queryByRole('option', { name: /label3/ })).toHaveTextContent('label3');
// Click value1-1 which triggers facetting for value3-x, and still show all value1-x // 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 }); const value1 = await screen.findByRole('option', { name: 'value1-1', selected: false });
userEvent.click(value1); await userEvent.click(value1);
await waitForElementToBeRemoved(screen.queryByRole('option', { name: 'value2-2' })); await waitFor(() => expect(screen.queryByRole('option', { name: 'value2-2' })).not.toBeInTheDocument());
expect(screen.queryByRole('option', { name: 'value1-2' })).toBeInTheDocument(); expect(screen.queryByRole('option', { name: 'value1-2' })).toBeInTheDocument();
expect(screen.queryByLabelText('selector')).toHaveTextContent('{label1="value1-1"}'); expect(screen.queryByLabelText('selector')).toHaveTextContent('{label1="value1-1"}');
expect(screen.queryByRole('option', { name: /label3/ })).toHaveTextContent('label3 (1)'); expect(screen.queryByRole('option', { name: /label3/ })).toHaveTextContent('label3 (1)');
// Click value1-2 for which facetting will allow more values for value3-x // Click value1-2 for which facetting will allow more values for value3-x
const value12 = await screen.findByRole('option', { name: 'value1-2', selected: false }); 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: 'value1-2', selected: true });
await screen.findByRole('option', { name: /label3/, selected: false }); 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'); await screen.findByLabelText('Values for label3');
expect(screen.queryByRole('option', { name: 'value1-1', selected: true })).toBeInTheDocument(); expect(screen.queryByRole('option', { name: 'value1-1', selected: true })).toBeInTheDocument();
expect(screen.queryByRole('option', { name: 'value1-2', selected: true })).toBeInTheDocument(); expect(screen.queryByRole('option', { name: 'value1-2', selected: true })).toBeInTheDocument();

View File

@@ -29,7 +29,7 @@ describe('MetricSelect', () => {
render(<MetricSelect {...props} />); render(<MetricSelect {...props} />);
await openMetricSelect(); await openMetricSelect();
const input = screen.getByRole('combobox'); const input = screen.getByRole('combobox');
userEvent.type(input, 'new'); await userEvent.type(input, 'new');
await waitFor(() => expect(screen.getByText('Create: new')).toBeInTheDocument()); await waitFor(() => expect(screen.getByText('Create: new')).toBeInTheDocument());
}); });
@@ -37,7 +37,7 @@ describe('MetricSelect', () => {
render(<MetricSelect {...props} />); render(<MetricSelect {...props} />);
await openMetricSelect(); await openMetricSelect();
const input = screen.getByRole('combobox'); const input = screen.getByRole('combobox');
userEvent.type(input, 'unique'); await userEvent.type(input, 'unique');
await waitFor(() => expect(screen.getAllByLabelText('Select option')).toHaveLength(3)); await waitFor(() => expect(screen.getAllByLabelText('Select option')).toHaveLength(3));
}); });
@@ -45,7 +45,7 @@ describe('MetricSelect', () => {
render(<MetricSelect {...props} />); render(<MetricSelect {...props} />);
await openMetricSelect(); await openMetricSelect();
const input = screen.getByRole('combobox'); 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)); await waitFor(() => expect(screen.getAllByLabelText('Select option')).toHaveLength(2));
}); });
@@ -53,7 +53,7 @@ describe('MetricSelect', () => {
render(<MetricSelect {...props} />); render(<MetricSelect {...props} />);
await openMetricSelect(); await openMetricSelect();
const input = screen.getByRole('combobox'); 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)); await waitFor(() => expect(screen.getAllByLabelText('Select option')).toHaveLength(2));
}); });
@@ -61,7 +61,7 @@ describe('MetricSelect', () => {
const { container } = render(<MetricSelect {...props} />); const { container } = render(<MetricSelect {...props} />);
await openMetricSelect(); await openMetricSelect();
const input = screen.getByRole('combobox'); const input = screen.getByRole('combobox');
userEvent.type(input, 'more'); await userEvent.type(input, 'more');
await waitFor(() => expect(container.querySelectorAll('mark')).toHaveLength(1)); await waitFor(() => expect(container.querySelectorAll('mark')).toHaveLength(1));
}); });
@@ -69,7 +69,7 @@ describe('MetricSelect', () => {
const { container } = render(<MetricSelect {...props} />); const { container } = render(<MetricSelect {...props} />);
await openMetricSelect(); await openMetricSelect();
const input = screen.getByRole('combobox'); const input = screen.getByRole('combobox');
userEvent.type(input, 'more metric'); await userEvent.type(input, 'more metric');
await waitFor(() => expect(container.querySelectorAll('mark')).toHaveLength(2)); await waitFor(() => expect(container.querySelectorAll('mark')).toHaveLength(2));
}); });
@@ -77,7 +77,7 @@ describe('MetricSelect', () => {
const { container } = render(<MetricSelect {...props} />); const { container } = render(<MetricSelect {...props} />);
await openMetricSelect(); await openMetricSelect();
const input = screen.getByRole('combobox'); const input = screen.getByRole('combobox');
userEvent.type(input, 'unique metric'); await userEvent.type(input, 'unique metric');
await waitFor(() => expect(container.querySelectorAll('mark')).toHaveLength(4)); await waitFor(() => expect(container.querySelectorAll('mark')).toHaveLength(4));
}); });
@@ -85,12 +85,12 @@ describe('MetricSelect', () => {
const { container } = render(<MetricSelect {...props} />); const { container } = render(<MetricSelect {...props} />);
await openMetricSelect(); await openMetricSelect();
const input = screen.getByRole('combobox'); const input = screen.getByRole('combobox');
userEvent.type(input, 'new'); await userEvent.type(input, 'new');
await waitFor(() => expect(container.querySelector('mark')).not.toBeInTheDocument()); await waitFor(() => expect(container.querySelector('mark')).not.toBeInTheDocument());
}); });
}); });
async function openMetricSelect() { async function openMetricSelect() {
const select = await screen.getByText('Select metric').parentElement!; const select = await screen.getByText('Select metric').parentElement!;
userEvent.click(select); await userEvent.click(select);
} }

View File

@@ -70,7 +70,7 @@ describe('PromQueryBuilder', () => {
it('tries to load metrics without labels', async () => { it('tries to load metrics without labels', async () => {
const { languageProvider, container } = setup(); const { languageProvider, container } = setup();
openMetricSelect(container); await openMetricSelect(container);
await waitFor(() => expect(languageProvider.getLabelValues).toBeCalledWith('__name__')); await waitFor(() => expect(languageProvider.getLabelValues).toBeCalledWith('__name__'));
}); });
@@ -79,27 +79,27 @@ describe('PromQueryBuilder', () => {
...defaultQuery, ...defaultQuery,
labels: [{ label: 'label_name', op: '=', value: 'label_value' }], labels: [{ label: 'label_name', op: '=', value: 'label_value' }],
}); });
openMetricSelect(container); await openMetricSelect(container);
await waitFor(() => expect(languageProvider.getSeries).toBeCalledWith('{label_name="label_value"}', true)); await waitFor(() => expect(languageProvider.getSeries).toBeCalledWith('{label_name="label_value"}', true));
}); });
it('tries to load variables in metric field', async () => { it('tries to load variables in metric field', async () => {
const { datasource, container } = setup(); const { datasource, container } = setup();
datasource.getVariables = jest.fn().mockReturnValue([]); datasource.getVariables = jest.fn().mockReturnValue([]);
openMetricSelect(container); await openMetricSelect(container);
await waitFor(() => expect(datasource.getVariables).toBeCalled()); await waitFor(() => expect(datasource.getVariables).toBeCalled());
}); });
it('tries to load labels when metric selected', async () => { it('tries to load labels when metric selected', async () => {
const { languageProvider } = setup(); const { languageProvider } = setup();
openLabelNameSelect(); await openLabelNameSelect();
await waitFor(() => expect(languageProvider.fetchSeriesLabels).toBeCalledWith('{__name__="random_metric"}')); await waitFor(() => expect(languageProvider.fetchSeriesLabels).toBeCalledWith('{__name__="random_metric"}'));
}); });
it('tries to load variables in label field', async () => { it('tries to load variables in label field', async () => {
const { datasource } = setup(); const { datasource } = setup();
datasource.getVariables = jest.fn().mockReturnValue([]); datasource.getVariables = jest.fn().mockReturnValue([]);
openLabelNameSelect(); await openLabelNameSelect();
await waitFor(() => expect(datasource.getVariables).toBeCalled()); await waitFor(() => expect(datasource.getVariables).toBeCalled());
}); });
@@ -111,7 +111,7 @@ describe('PromQueryBuilder', () => {
{ label: 'foo', op: '=', value: 'bar' }, { label: 'foo', op: '=', value: 'bar' },
], ],
}); });
openLabelNameSelect(1); await openLabelNameSelect(1);
await waitFor(() => await waitFor(() =>
expect(languageProvider.fetchSeriesLabels).toBeCalledWith('{label_name="label_value", __name__="random_metric"}') expect(languageProvider.fetchSeriesLabels).toBeCalledWith('{label_name="label_value", __name__="random_metric"}')
); );
@@ -122,7 +122,7 @@ describe('PromQueryBuilder', () => {
...defaultQuery, ...defaultQuery,
metric: '', metric: '',
}); });
openLabelNameSelect(); await openLabelNameSelect();
await waitFor(() => expect(languageProvider.fetchLabels).toBeCalled()); await waitFor(() => expect(languageProvider.fetchLabels).toBeCalled());
}); });
@@ -132,8 +132,8 @@ describe('PromQueryBuilder', () => {
labels: [], labels: [],
operations: [], operations: [],
}); });
openMetricSelect(container); await openMetricSelect(container);
userEvent.click(screen.getByText('histogram_metric_bucket')); await userEvent.click(screen.getByText('histogram_metric_bucket'));
await waitFor(() => expect(screen.getByText('hint: add histogram_quantile()')).toBeInTheDocument()); await waitFor(() => expect(screen.getByText('hint: add histogram_quantile()')).toBeInTheDocument());
}); });
@@ -143,8 +143,8 @@ describe('PromQueryBuilder', () => {
labels: [], labels: [],
operations: [], operations: [],
}); });
openMetricSelect(container); await openMetricSelect(container);
userEvent.click(screen.getByText('histogram_metric_sum')); await userEvent.click(screen.getByText('histogram_metric_sum'));
await waitFor(() => expect(screen.getByText('hint: add rate()')).toBeInTheDocument()); await waitFor(() => expect(screen.getByText('hint: add rate()')).toBeInTheDocument());
}); });
@@ -154,8 +154,8 @@ describe('PromQueryBuilder', () => {
labels: [], labels: [],
operations: [], operations: [],
}); });
openMetricSelect(container); await openMetricSelect(container);
userEvent.click(screen.getByText('histogram_metric_sum')); await userEvent.click(screen.getByText('histogram_metric_sum'));
await waitFor(() => expect(screen.getByText('hint: add rate()')).toBeInTheDocument()); await waitFor(() => expect(screen.getByText('hint: add rate()')).toBeInTheDocument());
}); });
@@ -176,9 +176,9 @@ describe('PromQueryBuilder', () => {
}, },
data data
); );
openMetricSelect(container); await openMetricSelect(container);
userEvent.click(screen.getByText('histogram_metric_sum')); await userEvent.click(screen.getByText('histogram_metric_sum'));
await waitFor(() => expect(screen.getAllByText(/hint:/g)).toHaveLength(2)); await waitFor(() => expect(screen.getAllByText(/hint:/)).toHaveLength(2));
}); });
}); });
@@ -205,14 +205,14 @@ function setup(query: PromVisualQuery = defaultQuery, data?: PanelData) {
return { languageProvider, datasource, container }; return { languageProvider, datasource, container };
} }
function openMetricSelect(container: HTMLElement) { async function openMetricSelect(container: HTMLElement) {
const select = container.querySelector('#prometheus-metric-select'); const select = container.querySelector('#prometheus-metric-select');
if (select) { if (select) {
userEvent.click(select); await userEvent.click(select);
} }
} }
function openLabelNameSelect(index = 0) { async function openLabelNameSelect(index = 0) {
const { name } = getLabelSelects(index); const { name } = getLabelSelects(index);
userEvent.click(name); await userEvent.click(name);
} }

View File

@@ -14,7 +14,7 @@ describe('PromQueryBuilderContainer', () => {
const { props } = setup({ expr: 'rate(metric_test{job="testjob"}[$__rate_interval])' }); const { props } = setup({ expr: 'rate(metric_test{job="testjob"}[$__rate_interval])' });
expect(screen.getByText('metric_test')).toBeInTheDocument(); expect(screen.getByText('metric_test')).toBeInTheDocument();
addOperation('Range functions', 'Rate'); await addOperation('Range functions', 'Rate');
expect(props.onChange).toBeCalledWith({ expect(props.onChange).toBeCalledWith({
expr: 'rate(metric_test{job="testjob"}[$__rate_interval])', expr: 'rate(metric_test{job="testjob"}[$__rate_interval])',
refId: 'A', refId: 'A',
@@ -23,7 +23,7 @@ describe('PromQueryBuilderContainer', () => {
it('Can add rest param', async () => { it('Can add rest param', async () => {
const { container } = setup({ expr: 'sum(ALERTS)' }); 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(() => { waitFor(() => {
expect(container.querySelector(`${getOperationParamId(0, 0)}`)).toBeInTheDocument(); expect(container.querySelector(`${getOperationParamId(0, 0)}`)).toBeInTheDocument();

View File

@@ -81,7 +81,7 @@ describe('PromQueryEditorSelector', () => {
it('changes to builder mode', async () => { it('changes to builder mode', async () => {
const { onChange } = renderWithMode(QueryEditorMode.Code); const { onChange } = renderWithMode(QueryEditorMode.Code);
switchToMode(QueryEditorMode.Builder); await switchToMode(QueryEditorMode.Builder);
expect(onChange).toBeCalledWith({ expect(onChange).toBeCalledWith({
refId: 'A', refId: 'A',
expr: defaultQuery.expr, expr: defaultQuery.expr,
@@ -116,7 +116,7 @@ describe('PromQueryEditorSelector', () => {
it('changes to code mode', async () => { it('changes to code mode', async () => {
const { onChange } = renderWithMode(QueryEditorMode.Builder); const { onChange } = renderWithMode(QueryEditorMode.Builder);
switchToMode(QueryEditorMode.Code); await switchToMode(QueryEditorMode.Code);
expect(onChange).toBeCalledWith({ expect(onChange).toBeCalledWith({
refId: 'A', refId: 'A',
expr: defaultQuery.expr, expr: defaultQuery.expr,
@@ -127,7 +127,7 @@ describe('PromQueryEditorSelector', () => {
it('changes to explain mode', async () => { it('changes to explain mode', async () => {
const { onChange } = renderWithMode(QueryEditorMode.Code); const { onChange } = renderWithMode(QueryEditorMode.Code);
switchToMode(QueryEditorMode.Explain); await switchToMode(QueryEditorMode.Explain);
expect(onChange).toBeCalledWith({ expect(onChange).toBeCalledWith({
refId: 'A', refId: 'A',
expr: defaultQuery.expr, expr: defaultQuery.expr,
@@ -142,7 +142,7 @@ describe('PromQueryEditorSelector', () => {
expr: 'rate(test_metric{instance="host.docker.internal:3000"}[$__interval])', expr: 'rate(test_metric{instance="host.docker.internal:3000"}[$__interval])',
editorMode: QueryEditorMode.Code, editorMode: QueryEditorMode.Code,
}); });
switchToMode(QueryEditorMode.Builder); await switchToMode(QueryEditorMode.Builder);
rerender( rerender(
<PromQueryEditorSelector <PromQueryEditorSelector
{...defaultProps} {...defaultProps}
@@ -187,7 +187,7 @@ function expectExplain() {
expect(screen.getByText(/Fetch all series/)).toBeInTheDocument(); expect(screen.getByText(/Fetch all series/)).toBeInTheDocument();
} }
function switchToMode(mode: QueryEditorMode) { async function switchToMode(mode: QueryEditorMode) {
const label = { const label = {
[QueryEditorMode.Code]: /Code/, [QueryEditorMode.Code]: /Code/,
[QueryEditorMode.Explain]: /Explain/, [QueryEditorMode.Explain]: /Explain/,
@@ -195,5 +195,5 @@ function switchToMode(mode: QueryEditorMode) {
}[mode]; }[mode];
const switchEl = screen.getByLabelText(label); const switchEl = screen.getByLabelText(label);
userEvent.click(switchEl); await userEvent.click(switchEl);
} }

View File

@@ -31,7 +31,7 @@ describe('LabelFilters', () => {
it('adds new label', async () => { it('adds new label', async () => {
const { onChange } = setup([{ label: 'foo', op: '=', value: 'bar' }]); const { onChange } = setup([{ label: 'foo', op: '=', value: 'bar' }]);
userEvent.click(getAddButton()); await userEvent.click(getAddButton());
expect(screen.getAllByText(/Choose/)).toHaveLength(2); expect(screen.getAllByText(/Choose/)).toHaveLength(2);
const { name, value } = getLabelSelects(1); const { name, value } = getLabelSelects(1);
await selectOptionInTest(name, 'baz'); await selectOptionInTest(name, 'baz');
@@ -44,7 +44,7 @@ describe('LabelFilters', () => {
it('removes label', async () => { it('removes label', async () => {
const { onChange } = setup([{ label: 'foo', op: '=', value: 'bar' }]); const { onChange } = setup([{ label: 'foo', op: '=', value: 'bar' }]);
userEvent.click(screen.getByLabelText(/remove/)); await userEvent.click(screen.getByLabelText(/remove/));
expect(onChange).toBeCalledWith([]); expect(onChange).toBeCalledWith([]);
}); });

View File

@@ -36,7 +36,7 @@ describe('OperationList', () => {
const { onChange } = setup(); const { onChange } = setup();
const removeOperationButtons = screen.getAllByTitle('Remove operation'); const removeOperationButtons = screen.getAllByTitle('Remove operation');
expect(removeOperationButtons).toHaveLength(2); expect(removeOperationButtons).toHaveLength(2);
userEvent.click(removeOperationButtons[1]); await userEvent.click(removeOperationButtons[1]);
expect(onChange).toBeCalledWith({ expect(onChange).toBeCalledWith({
labels: [{ label: 'instance', op: '=', value: 'localhost:9090' }], labels: [{ label: 'instance', op: '=', value: 'localhost:9090' }],
metric: 'random_metric', metric: 'random_metric',
@@ -46,7 +46,7 @@ describe('OperationList', () => {
it('adds an operation', async () => { it('adds an operation', async () => {
const { onChange } = setup(); const { onChange } = setup();
addOperation('Aggregations', 'Min'); await addOperation('Aggregations', 'Min');
expect(onChange).toBeCalledWith({ expect(onChange).toBeCalledWith({
labels: [{ label: 'instance', op: '=', value: 'localhost:9090' }], labels: [{ label: 'instance', op: '=', value: 'localhost:9090' }],
metric: 'random_metric', metric: 'random_metric',

View File

@@ -1,13 +1,13 @@
import { screen, fireEvent } from '@testing-library/react'; import { screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event'; 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'); const addOperationButton = screen.getByTitle('Add operation');
expect(addOperationButton).toBeInTheDocument(); expect(addOperationButton).toBeInTheDocument();
userEvent.click(addOperationButton); await userEvent.click(addOperationButton);
const sectionItem = screen.getByTitle(section); const sectionItem = screen.getByTitle(section);
expect(sectionItem).toBeInTheDocument(); 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. // anywhere when debugging so not sure what style is it picking up.
fireEvent.click(sectionItem.children[0]); fireEvent.click(sectionItem.children[0]);
const opItem = screen.getByTitle(op); const opItem = screen.getByTitle(op);

View File

@@ -1,20 +1,25 @@
import NativeSearch from './NativeSearch'; import NativeSearch from './NativeSearch';
import React from 'react'; 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 { TempoDatasource, TempoQuery } from '../datasource';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import { UserEvent } from '@testing-library/user-event/dist/types/setup';
const getOptions = jest.fn().mockImplementation(() => { const getOptions = jest.fn().mockImplementation(() => {
return Promise.resolve([ return new Promise((resolve) => {
{ setTimeout(() => {
value: 'customer', resolve([
label: 'customer', {
}, value: 'customer',
{ label: 'customer',
value: 'driver', },
label: 'driver', {
}, value: 'driver',
]); label: 'driver',
},
]);
}, 1000);
});
}); });
jest.mock('../language_provider', () => { jest.mock('../language_provider', () => {
@@ -31,6 +36,36 @@ const mockQuery = {
} as TempoQuery; } as TempoQuery;
describe('NativeSearch', () => { 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(
<NativeSearch datasource={{} as TempoDatasource} query={mockQuery} onChange={jest.fn()} onRunQuery={jest.fn()} />
);
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 () => { it('should call the `onChange` function on click of the Input', async () => {
const promise = Promise.resolve(); const promise = Promise.resolve();
const handleOnChange = jest.fn(() => promise); const handleOnChange = jest.fn(() => promise);
@@ -54,56 +89,12 @@ describe('NativeSearch', () => {
const asyncServiceSelect = await screen.findByRole('combobox', { name: 'select-span-name' }); const asyncServiceSelect = await screen.findByRole('combobox', { name: 'select-span-name' });
expect(asyncServiceSelect).toBeInTheDocument(); expect(asyncServiceSelect).toBeInTheDocument();
userEvent.click(asyncServiceSelect); await user.click(asyncServiceSelect);
jest.advanceTimersByTime(1000);
const driverOption = await screen.findByText('driver'); const driverOption = await screen.findByText('driver');
userEvent.click(driverOption); await user.click(driverOption);
expect(handleOnChange).toHaveBeenCalledWith(fakeOptionChoice); 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(
<NativeSearch
datasource={{} as TempoDatasource}
query={mockQuery}
onChange={handleOnChange}
onRunQuery={() => {}}
/>
);
const asyncServiceSelect = screen.getByRole('combobox', { name: 'select-span-name' });
userEvent.click(asyncServiceSelect);
const loader = screen.getByText('Loading options...');
expect(loader).toBeInTheDocument();
await act(() => promise);
});
});

View File

@@ -200,7 +200,7 @@ describe('AnnoListPanel', () => {
getMock.mockClear(); getMock.mockClear();
expect(screen.getByText(/result text/i)).toBeInTheDocument(); 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)); await waitFor(() => expect(getMock).toHaveBeenCalledTimes(1));
expect(getMock).toHaveBeenCalledWith('/api/search', { dashboardIds: 14 }); expect(getMock).toHaveBeenCalledWith('/api/search', { dashboardIds: 14 });
@@ -215,7 +215,7 @@ describe('AnnoListPanel', () => {
getMock.mockClear(); getMock.mockClear();
expect(screen.getByText('Result tag B')).toBeInTheDocument(); 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).toHaveBeenCalledTimes(1);
expect(getMock).toHaveBeenCalledWith( expect(getMock).toHaveBeenCalledWith(
@@ -239,7 +239,7 @@ describe('AnnoListPanel', () => {
getMock.mockClear(); getMock.mockClear();
expect(screen.getByRole('img')).toBeInTheDocument(); expect(screen.getByRole('img')).toBeInTheDocument();
userEvent.click(screen.getByRole('img')); await userEvent.click(screen.getByRole('img'));
expect(getMock).toHaveBeenCalledTimes(1); expect(getMock).toHaveBeenCalledTimes(1);
expect(getMock).toHaveBeenCalledWith( expect(getMock).toHaveBeenCalledWith(

View File

@@ -3,7 +3,6 @@ import { render, screen, fireEvent, waitFor, getByText } from '@testing-library/
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import { NodeGraph } from './NodeGraph'; import { NodeGraph } from './NodeGraph';
import { makeEdgesDataFrame, makeNodesDataFrame } from './utils'; import { makeEdgesDataFrame, makeNodesDataFrame } from './utils';
import { act } from 'react-dom/test-utils';
jest.mock('react-use/lib/useMeasure', () => { jest.mock('react-use/lib/useMeasure', () => {
return { return {
@@ -32,9 +31,9 @@ describe('NodeGraph', () => {
const zoomOut = await screen.findByTitle(/Zoom out/); const zoomOut = await screen.findByTitle(/Zoom out/);
expect(getScale()).toBe(1); expect(getScale()).toBe(1);
userEvent.click(zoomIn); await userEvent.click(zoomIn);
expect(getScale()).toBe(1.5); expect(getScale()).toBe(1.5);
userEvent.click(zoomOut); await userEvent.click(zoomOut);
expect(getScale()).toBe(1); expect(getScale()).toBe(1);
}); });
@@ -84,17 +83,11 @@ describe('NodeGraph', () => {
/> />
); );
const node = await screen.findByLabelText(/Node: service:0/); 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 await userEvent.click(node);
// context menu which is experimental (but supported) property and userEvents does not seem to support that
act(() => {
userEvent.click(node);
});
await screen.findByText(/Node traces/); await screen.findByText(/Node traces/);
const edge = await screen.findByLabelText(/Edge from/); const edge = await screen.findByLabelText(/Edge from/);
act(() => { await userEvent.click(edge);
userEvent.click(edge);
});
await screen.findByText(/Edge traces/); await screen.findByText(/Edge traces/);
}); });
@@ -183,9 +176,7 @@ describe('NodeGraph', () => {
expect(node).toBeInTheDocument(); expect(node).toBeInTheDocument();
const marker = await screen.findByLabelText(/Hidden nodes marker: 3/); const marker = await screen.findByLabelText(/Hidden nodes marker: 3/);
act(() => { await userEvent.click(marker);
userEvent.click(marker);
});
expect(screen.queryByLabelText(/Node: service:0/)).not.toBeInTheDocument(); expect(screen.queryByLabelText(/Node: service:0/)).not.toBeInTheDocument();
expect(screen.getByLabelText(/Node: service:4/)).toBeInTheDocument(); expect(screen.getByLabelText(/Node: service:4/)).toBeInTheDocument();
@@ -210,7 +201,7 @@ describe('NodeGraph', () => {
); );
const button = await screen.findByTitle(/Grid layout/); 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:0', { x: -60, y: -60 });
await expectNodePositionCloseTo('service:1', { x: 60, y: -60 }); await expectNodePositionCloseTo('service:1', { x: 60, y: -60 });

View File

@@ -3997,7 +3997,7 @@ __metadata:
"@testing-library/jest-dom": 5.16.2 "@testing-library/jest-dom": 5.16.2
"@testing-library/react": 12.1.4 "@testing-library/react": 12.1.4
"@testing-library/react-hooks": 7.0.2 "@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/d3-interpolate": ^1.4.0
"@types/history": 4.7.11 "@types/history": 4.7.11
"@types/jest": 27.4.1 "@types/jest": 27.4.1
@@ -4180,7 +4180,7 @@ __metadata:
"@sentry/browser": 6.19.1 "@sentry/browser": 6.19.1
"@testing-library/dom": 8.13.0 "@testing-library/dom": 8.13.0
"@testing-library/react": 12.1.4 "@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/angular": 1.8.4
"@types/history": 4.7.11 "@types/history": 4.7.11
"@types/jest": 27.4.1 "@types/jest": 27.4.1
@@ -4387,7 +4387,7 @@ __metadata:
"@testing-library/jest-dom": 5.16.2 "@testing-library/jest-dom": 5.16.2
"@testing-library/react": 12.1.4 "@testing-library/react": 12.1.4
"@testing-library/react-hooks": 7.0.2 "@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/classnames": 2.3.0
"@types/common-tags": ^1.8.0 "@types/common-tags": ^1.8.0
"@types/d3": 7.1.0 "@types/d3": 7.1.0
@@ -9030,7 +9030,7 @@ __metadata:
languageName: node languageName: node
linkType: hard 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 version: 8.13.0
resolution: "@testing-library/dom@npm:8.13.0" resolution: "@testing-library/dom@npm:8.13.0"
dependencies: dependencies:
@@ -9046,22 +9046,6 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "@testing-library/jest-dom@npm:5.16.2":
version: 5.16.2 version: 5.16.2
resolution: "@testing-library/jest-dom@npm:5.16.2" resolution: "@testing-library/jest-dom@npm:5.16.2"
@@ -9115,14 +9099,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@testing-library/user-event@npm:13.5.0, @testing-library/user-event@npm:^13.5.0": "@testing-library/user-event@npm:14.0.0":
version: 13.5.0 version: 14.0.0
resolution: "@testing-library/user-event@npm:13.5.0" resolution: "@testing-library/user-event@npm:14.0.0"
dependencies:
"@babel/runtime": ^7.12.5
peerDependencies: peerDependencies:
"@testing-library/dom": ">=7.21.4" "@testing-library/dom": ">=7.21.4"
checksum: 16319de685fbb7008f1ba667928f458b2d08196918002daca56996de80ef35e6d9de26e9e1ece7d00a004692b95a597cf9142fff0dc53f2f51606a776584f549 checksum: 171a40e680616e6a34299f9fb783dcadcd36f255256a53fe06cb71f4149715d8e5b84e65ea1c144af179f2e4b1799af92c79a8fc0c4c0ee2e94a713f2c476efd
languageName: node languageName: node
linkType: hard linkType: hard
@@ -20472,7 +20454,7 @@ __metadata:
"@testing-library/jest-dom": 5.16.2 "@testing-library/jest-dom": 5.16.2
"@testing-library/react": 12.1.4 "@testing-library/react": 12.1.4
"@testing-library/react-hooks": 7.0.2 "@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": 1.8.4
"@types/angular-route": 1.7.2 "@types/angular-route": 1.7.2
"@types/classnames": 2.3.0 "@types/classnames": 2.3.0