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>
pull/47008/head
Ashley Harrison 4 years ago committed by GitHub
parent 9ed7e48454
commit d832bde270
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      package.json
  2. 2
      packages/grafana-data/package.json
  3. 2
      packages/grafana-runtime/package.json
  4. 2
      packages/grafana-runtime/src/components/DataSourcePicker.test.tsx
  5. 2
      packages/grafana-ui/package.json
  6. 67
      packages/grafana-ui/src/components/Cascader/Cascader.test.tsx
  7. 30
      packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.test.tsx
  8. 18
      packages/grafana-ui/src/components/ConfirmButton/ConfirmButton.test.tsx
  9. 16
      packages/grafana-ui/src/components/DataSourceSettings/CustomHeadersSettings.test.tsx
  10. 8
      packages/grafana-ui/src/components/SecretInput/SecretInput.test.tsx
  11. 10
      packages/grafana-ui/src/components/Select/SelectBase.test.tsx
  12. 6
      packages/grafana-ui/src/components/Table/Table.test.tsx
  13. 10
      public/app/core/components/ForgottenPassword/ChangePasswordPage.test.tsx
  14. 4
      public/app/core/components/ForgottenPassword/SendResetMailPage.test.tsx
  15. 8
      public/app/core/components/Login/LoginPage.test.tsx
  16. 99
      public/app/core/components/NavBar/NavBarItem.test.tsx
  17. 4
      public/app/core/components/NavBar/NavBarMenu.test.tsx
  18. 2
      public/app/core/components/Select/ReadonlyFolderPicker/ReadonlyFolderPicker.test.tsx
  19. 18
      public/app/core/components/Signup/SignupPage.test.tsx
  20. 6
      public/app/core/components/Signup/VerifyEmailPage.test.tsx
  21. 4
      public/app/core/specs/OrgSwitcher.test.tsx
  22. 4
      public/app/features/alerting/components/UnifiedAlertingPromotion.test.tsx
  23. 14
      public/app/features/alerting/unified/AlertGroups.test.tsx
  24. 8
      public/app/features/alerting/unified/AlertsFolderView.test.tsx
  25. 34
      public/app/features/alerting/unified/AmRoutes.test.tsx
  26. 34
      public/app/features/alerting/unified/MuteTimings.test.tsx
  27. 54
      public/app/features/alerting/unified/Receivers.test.tsx
  28. 84
      public/app/features/alerting/unified/RuleEditor.test.tsx
  29. 55
      public/app/features/alerting/unified/RuleList.test.tsx
  30. 49
      public/app/features/alerting/unified/Silences.test.tsx
  31. 10
      public/app/features/alerting/unified/components/admin/AlertmanagerConfig.test.tsx
  32. 4
      public/app/features/alerting/unified/components/rules/RulesGroup.test.tsx
  33. 44
      public/app/features/api-keys/ApiKeysPage.test.tsx
  34. 42
      public/app/features/dashboard/components/DashboardSettings/AnnotationsSettings.test.tsx
  35. 22
      public/app/features/dashboard/components/DashboardSettings/AutoRefreshIntervals.test.tsx
  36. 4
      public/app/features/dashboard/components/DashboardSettings/GeneralSettings.test.tsx
  37. 40
      public/app/features/dashboard/components/DashboardSettings/LinksSettings.test.tsx
  38. 39
      public/app/features/dashboard/components/DashboardSettings/VersionsSettings.test.tsx
  39. 4
      public/app/features/dashboard/components/PanelEditor/PanelNotSupported.test.tsx
  40. 10
      public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.test.tsx
  41. 50
      public/app/features/explore/AddToDashboard/index.test.tsx
  42. 26
      public/app/features/explore/TraceView/TraceView.test.tsx
  43. 38
      public/app/features/explore/TraceView/TraceViewContainer.test.tsx
  44. 2
      public/app/features/explore/Wrapper.test.tsx
  45. 28
      public/app/features/explore/spec/helper/interactions.ts
  46. 12
      public/app/features/explore/spec/queryHistory.test.tsx
  47. 10
      public/app/features/inspector/InspectDataTab.test.tsx
  48. 6
      public/app/features/invites/SignupInvited.test.tsx
  49. 70
      public/app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch.test.tsx
  50. 8
      public/app/features/playlist/PlaylistEditPage.test.tsx
  51. 14
      public/app/features/playlist/PlaylistForm.test.tsx
  52. 2
      public/app/features/playlist/PlaylistNewPage.test.tsx
  53. 2
      public/app/features/plugins/admin/pages/Browse.test.tsx
  54. 8
      public/app/features/plugins/admin/pages/PluginDetails.test.tsx
  55. 6
      public/app/features/profile/ChangePasswordPage.test.tsx
  56. 12
      public/app/features/profile/UserProfileEditPage.test.tsx
  57. 8
      public/app/features/variables/adhoc/picker/AdHocFilter.test.tsx
  58. 40
      public/app/features/variables/inspect/VariablesUnknownTable.test.tsx
  59. 12
      public/app/features/variables/pickers/OptionsPicker/OptionPicker.test.tsx
  60. 14
      public/app/features/variables/query/QueryVariableEditor.test.tsx
  61. 22
      public/app/plugins/datasource/cloudwatch/components/Dimensions/Dimensions.test.tsx
  62. 4
      public/app/plugins/datasource/cloudwatch/components/MetricStatEditor/MetricStatEditor.test.tsx
  63. 4
      public/app/plugins/datasource/dashboard/DashboardQueryEditor.test.tsx
  64. 26
      public/app/plugins/datasource/elasticsearch/components/hooks/useCreatableSelectPersistedBehaviour.test.tsx
  65. 8
      public/app/plugins/datasource/grafana-azure-monitor-datasource/components/NewMetricsQueryEditor/MetricsQueryEditor.test.tsx
  66. 2
      public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ResourcePicker/ResourcePicker.test.tsx
  67. 2
      public/app/plugins/datasource/grafana-azure-monitor-datasource/components/VariableEditor/VariableEditor.test.tsx
  68. 2
      public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/AnalyticsConfig.test.tsx
  69. 4
      public/app/plugins/datasource/influxdb/components/RawInfluxQLEditor.test.tsx
  70. 14
      public/app/plugins/datasource/influxdb/components/VisualInfluxQLEditor/Editor.tags.test.tsx
  71. 40
      public/app/plugins/datasource/jaeger/components/SearchForm.test.tsx
  72. 76
      public/app/plugins/datasource/loki/components/LokiLabelBrowser.test.tsx
  73. 4
      public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.test.tsx
  74. 2
      public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderContainer.test.tsx
  75. 2
      public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.test.tsx
  76. 12
      public/app/plugins/datasource/loki/querybuilder/components/LokiQueryEditorSelector.test.tsx
  77. 8
      public/app/plugins/datasource/prometheus/components/PromQueryEditorByApp.test.tsx
  78. 79
      public/app/plugins/datasource/prometheus/components/PrometheusMetricsBrowser.test.tsx
  79. 18
      public/app/plugins/datasource/prometheus/querybuilder/components/MetricSelect.test.tsx
  80. 40
      public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilder.test.tsx
  81. 4
      public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilderContainer.test.tsx
  82. 12
      public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryEditorSelector.test.tsx
  83. 4
      public/app/plugins/datasource/prometheus/querybuilder/shared/LabelFilters.test.tsx
  84. 4
      public/app/plugins/datasource/prometheus/querybuilder/shared/OperationList.test.tsx
  85. 6
      public/app/plugins/datasource/prometheus/querybuilder/shared/OperationList.testUtils.ts
  86. 107
      public/app/plugins/datasource/tempo/QueryEditor/NativeSearch.test.tsx
  87. 6
      public/app/plugins/panel/annolist/AnnoListPanel.test.tsx
  88. 21
      public/app/plugins/panel/nodeGraph/NodeGraph.test.tsx
  89. 36
      yarn.lock

@ -113,7 +113,7 @@
"@testing-library/jest-dom": "5.16.2",
"@testing-library/react": "12.1.4",
"@testing-library/react-hooks": "7.0.2",
"@testing-library/user-event": "13.5.0",
"@testing-library/user-event": "14.0.0",
"@types/angular": "1.8.4",
"@types/angular-route": "1.7.2",
"@types/classnames": "2.3.0",

@ -51,7 +51,7 @@
"@testing-library/jest-dom": "5.16.2",
"@testing-library/react": "12.1.4",
"@testing-library/react-hooks": "7.0.2",
"@testing-library/user-event": "13.5.0",
"@testing-library/user-event": "14.0.0",
"@types/history": "4.7.11",
"@types/jest": "27.4.1",
"@types/jquery": "3.5.14",

@ -40,7 +40,7 @@
"@rollup/plugin-node-resolve": "13.1.3",
"@testing-library/dom": "8.13.0",
"@testing-library/react": "12.1.4",
"@testing-library/user-event": "^13.5.0",
"@testing-library/user-event": "14.0.0",
"@types/angular": "1.8.4",
"@types/history": "4.7.11",
"@types/jest": "27.4.1",

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

@ -118,7 +118,7 @@
"@testing-library/jest-dom": "5.16.2",
"@testing-library/react": "12.1.4",
"@testing-library/react-hooks": "7.0.2",
"@testing-library/user-event": "13.5.0",
"@testing-library/user-event": "14.0.0",
"@types/classnames": "2.3.0",
"@types/common-tags": "^1.8.0",
"@types/d3": "7.1.0",

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

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

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

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

@ -33,7 +33,7 @@ describe('<SecretInput />', () => {
expect(screen.queryByRole('button', { name: RESET_BUTTON_TEXT })).toBeInTheDocument();
});
it('should be possible to reset a configured secret', () => {
it('should be possible to reset a configured secret', async () => {
const onReset = jest.fn();
render(<SecretInput isConfigured={true} onChange={() => {}} onReset={onReset} placeholder={PLACEHOLDER_TEXT} />);
@ -43,12 +43,12 @@ describe('<SecretInput />', () => {
expect(screen.queryByRole('button', { name: RESET_BUTTON_TEXT })).toBeInTheDocument();
// Click on "Reset"
userEvent.click(screen.getByRole('button', { name: RESET_BUTTON_TEXT }));
await userEvent.click(screen.getByRole('button', { name: RESET_BUTTON_TEXT }));
expect(onReset).toHaveBeenCalledTimes(1);
});
it('should be possible to change the value of the secret', () => {
it('should be possible to change the value of the secret', async () => {
const onChange = jest.fn();
render(<SecretInput isConfigured={false} onChange={onChange} onReset={() => {}} placeholder={PLACEHOLDER_TEXT} />);
@ -57,7 +57,7 @@ describe('<SecretInput />', () => {
expect(input).toHaveValue('');
userEvent.type(input, 'Foo');
await userEvent.type(input, 'Foo');
expect(onChange).toHaveBeenCalled();
expect(input).toHaveValue('Foo');

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

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

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

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

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

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

@ -23,10 +23,10 @@ describe('NavBarMenu', () => {
expect(closeButton).toBeInTheDocument();
});
it('clicking the close button calls the onClose callback', () => {
it('clicking the close button calls the onClose callback', async () => {
const closeButton = screen.getByRole('button', { name: 'Close navigation menu' });
expect(closeButton).toBeInTheDocument();
userEvent.click(closeButton);
await userEvent.click(closeButton);
expect(mockOnClose).toHaveBeenCalled();
});
});

@ -76,7 +76,7 @@ describe('ReadonlyFolderPicker', () => {
expect(within(selectors.container.get()).getByRole('combobox')).toBeInTheDocument();
getFoldersAsOptionsSpy.mockClear();
userEvent.type(within(selectors.container.get()).getByRole('combobox'), 'A');
await userEvent.type(within(selectors.container.get()).getByRole('combobox'), 'A');
await waitFor(() => expect(getFoldersAsOptionsSpy).toHaveBeenCalledTimes(1));
expect(getFoldersAsOptionsSpy).toHaveBeenCalledWith({

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

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

@ -53,7 +53,7 @@ describe('OrgSwitcher', () => {
it('should switch orgId in call to backend', async () => {
const row = screen.getByRole('row', { name: /org 2 admin switch to/i });
const switchToButton = within(row).getByText(/switch to/i);
userEvent.click(switchToButton);
await userEvent.click(switchToButton);
await waitFor(() => expect(setUserOrgSpy).toBeCalledWith({ orgId: 2, name: 'Org 2', role: 'Admin' }));
});
@ -63,7 +63,7 @@ describe('OrgSwitcher', () => {
const row = screen.getByRole('row', { name: /org 2 admin switch to/i });
const switchToButton = within(row).getByText(/switch to/i);
userEvent.click(switchToButton);
await userEvent.click(switchToButton);
await waitFor(() => expect(window.location.href).toEqual('/subUrl/?orgId=2'));
});

@ -14,12 +14,12 @@ describe('Unified Alerting promotion', () => {
expect(screen.queryByText('Try out the Grafana 8 alerting!')).toBeInTheDocument();
});
it('should be hidden if dismissed', () => {
it('should be hidden if dismissed', async () => {
const promotion = render(<UnifiedAlertingPromotion />);
expect(window.localStorage.getItem(LOCAL_STORAGE_KEY)).toBe('true');
const dismissButton = promotion.getByRole('button');
userEvent.click(dismissButton);
await userEvent.click(dismissButton);
expect(screen.queryByText('Try out the Grafana 8 alerting!')).not.toBeInTheDocument();
expect(window.localStorage.getItem(LOCAL_STORAGE_KEY)).toBe('false');

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -48,9 +48,9 @@ describe('General Settings', () => {
describe('when timezone is changed', () => {
it('should call update function', async () => {
const { props } = setupTestContext({});
userEvent.click(screen.getByTestId(selectors.components.TimeZonePicker.containerV2));
await userEvent.click(screen.getByTestId(selectors.components.TimeZonePicker.containerV2));
const timeZonePicker = screen.getByTestId(selectors.components.TimeZonePicker.containerV2);
userEvent.click(byRole('combobox').get(timeZonePicker));
await userEvent.click(byRole('combobox').get(timeZonePicker));
await selectOptionInTest(timeZonePicker, 'Browser Time');
expect(props.updateTimeZone).toHaveBeenCalledWith('browser');
expect(props.dashboard.timezone).toBe('browser');

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

@ -6,6 +6,7 @@ import userEvent from '@testing-library/user-event';
import { historySrv } from '../VersionHistory/HistorySrv';
import { VersionsSettings, VERSIONS_FETCH_LIMIT } from './VersionsSettings';
import { versions, diffs } from './__mocks__/versions';
import { UserEvent } from '@testing-library/user-event/dist/types/setup';
jest.mock('../VersionHistory/HistorySrv');
@ -28,8 +29,18 @@ describe('VersionSettings', () => {
getRelativeTime: jest.fn(() => 'time ago'),
};
let user: UserEvent;
beforeEach(() => {
// Need to use delay: null here to work with fakeTimers
// see https://github.com/testing-library/user-event/issues/833
user = userEvent.setup({ delay: null });
jest.resetAllMocks();
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
test('renders a header and a loading indicator followed by results in a table', async () => {
@ -102,7 +113,9 @@ describe('VersionSettings', () => {
historySrv.getHistoryList
// @ts-ignore
.mockImplementationOnce(() => Promise.resolve(versions.slice(0, VERSIONS_FETCH_LIMIT)))
.mockImplementationOnce(() => Promise.resolve(versions.slice(VERSIONS_FETCH_LIMIT, versions.length)));
.mockImplementationOnce(
() => new Promise((resolve) => setTimeout(() => resolve(versions.slice(VERSIONS_FETCH_LIMIT)), 1000))
);
render(<VersionsSettings dashboard={dashboard} />);
@ -113,14 +126,16 @@ describe('VersionSettings', () => {
expect(within(screen.getAllByRole('rowgroup')[1]).getAllByRole('row').length).toBe(VERSIONS_FETCH_LIMIT);
const showMoreButton = screen.getByRole('button', { name: /show more versions/i });
userEvent.click(showMoreButton);
await user.click(showMoreButton);
expect(historySrv.getHistoryList).toBeCalledTimes(2);
expect(screen.queryByText(/Fetching more entries/i)).toBeInTheDocument();
expect(screen.getByText(/Fetching more entries/i)).toBeInTheDocument();
jest.advanceTimersByTime(1000);
await waitFor(() =>
expect(within(screen.getAllByRole('rowgroup')[1]).getAllByRole('row').length).toBe(versions.length)
);
await waitFor(() => {
expect(screen.queryByText(/Fetching more entries/i)).not.toBeInTheDocument();
expect(within(screen.getAllByRole('rowgroup')[1]).getAllByRole('row').length).toBe(versions.length);
});
});
test('selecting two versions and clicking compare button should render compare view', async () => {
@ -139,17 +154,17 @@ describe('VersionSettings', () => {
const compareButton = screen.getByRole('button', { name: /compare versions/i });
const tableBody = screen.getAllByRole('rowgroup')[1];
userEvent.click(within(tableBody).getAllByRole('checkbox')[0]);
userEvent.click(within(tableBody).getAllByRole('checkbox')[VERSIONS_FETCH_LIMIT - 1]);
await user.click(within(tableBody).getAllByRole('checkbox')[0]);
await user.click(within(tableBody).getAllByRole('checkbox')[VERSIONS_FETCH_LIMIT - 1]);
expect(compareButton).toBeEnabled();
userEvent.click(within(tableBody).getAllByRole('checkbox')[1]);
await user.click(within(tableBody).getAllByRole('checkbox')[1]);
expect(compareButton).toBeDisabled();
userEvent.click(within(tableBody).getAllByRole('checkbox')[1]);
userEvent.click(compareButton);
await user.click(within(tableBody).getAllByRole('checkbox')[1]);
await user.click(compareButton);
await waitFor(() => expect(screen.getByRole('heading', { name: /versions comparing 2 11/i })).toBeInTheDocument());
@ -170,7 +185,7 @@ describe('VersionSettings', () => {
expect(queryByFullText('version changed')).toBeInTheDocument();
expect(screen.queryByText(/view json diff/i)).toBeInTheDocument();
userEvent.click(screen.getByText(/view json diff/i));
await user.click(screen.getByText(/view json diff/i));
await waitFor(() => expect(screen.getByRole('table')).toBeInTheDocument());
});

@ -28,9 +28,9 @@ describe('PanelNotSupported', () => {
});
describe('when the back to queries button is clicked', () => {
it('then correct action should be dispatched', () => {
it('then correct action should be dispatched', async () => {
setupTestContext({});
userEvent.click(screen.getByRole('button', { name: /go back to queries/i }));
await userEvent.click(screen.getByRole('button', { name: /go back to queries/i }));
expect(locationService.getSearchObject().tab).toBe(PanelEditorTabId.Query);
});
});

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

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

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

@ -35,75 +35,75 @@ function renderTraceViewContainer(frames = [frameOld]) {
}
describe('TraceViewContainer', () => {
it('toggles children visibility', () => {
it('toggles children visibility', async () => {
renderTraceViewContainer();
expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(3);
userEvent.click(screen.getAllByText('', { selector: 'span[data-test-id="SpanTreeOffset--indentGuide"]' })[0]);
await userEvent.click(screen.getAllByText('', { selector: 'span[data-test-id="SpanTreeOffset--indentGuide"]' })[0]);
expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(1);
userEvent.click(screen.getAllByText('', { selector: 'span[data-test-id="SpanTreeOffset--indentGuide"]' })[0]);
await userEvent.click(screen.getAllByText('', { selector: 'span[data-test-id="SpanTreeOffset--indentGuide"]' })[0]);
expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(3);
});
it('toggles collapses and expands one level of spans', () => {
it('toggles collapses and expands one level of spans', async () => {
renderTraceViewContainer();
expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(3);
userEvent.click(screen.getByLabelText('Collapse +1'));
await userEvent.click(screen.getByLabelText('Collapse +1'));
expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(2);
userEvent.click(screen.getByLabelText('Expand +1'));
await userEvent.click(screen.getByLabelText('Expand +1'));
expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(3);
});
it('toggles collapses and expands all levels', () => {
it('toggles collapses and expands all levels', async () => {
renderTraceViewContainer();
expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(3);
userEvent.click(screen.getByLabelText('Collapse All'));
await userEvent.click(screen.getByLabelText('Collapse All'));
expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(1);
userEvent.click(screen.getByLabelText('Expand All'));
await userEvent.click(screen.getByLabelText('Expand All'));
expect(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' }).length).toBe(3);
});
it('searches for spans', () => {
it('searches for spans', async () => {
renderTraceViewContainer();
userEvent.type(screen.getByPlaceholderText('Find...'), '1ed38015486087ca');
await userEvent.type(screen.getByPlaceholderText('Find...'), '1ed38015486087ca');
expect(
(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[0].parentNode! as HTMLElement).className
).toContain('rowMatchingFilter');
});
it('can select next/prev results', () => {
it('can select next/prev results', async () => {
renderTraceViewContainer();
userEvent.type(screen.getByPlaceholderText('Find...'), 'logproto');
await userEvent.type(screen.getByPlaceholderText('Find...'), 'logproto');
const nextResultButton = screen.getByTestId('trace-page-search-bar-next-result-button');
const prevResultButton = screen.getByTestId('trace-page-search-bar-prev-result-button');
const suffix = screen.getByTestId('trace-page-search-bar-suffix');
userEvent.click(nextResultButton);
await userEvent.click(nextResultButton);
expect(suffix.textContent).toBe('1 of 2');
expect(
(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[1].parentNode! as HTMLElement).className
).toContain('rowFocused');
userEvent.click(nextResultButton);
await userEvent.click(nextResultButton);
expect(suffix.textContent).toBe('2 of 2');
expect(
(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[2].parentNode! as HTMLElement).className
).toContain('rowFocused');
userEvent.click(nextResultButton);
await userEvent.click(nextResultButton);
expect(suffix.textContent).toBe('1 of 2');
expect(
(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[1].parentNode! as HTMLElement).className
).toContain('rowFocused');
userEvent.click(prevResultButton);
await userEvent.click(prevResultButton);
expect(suffix.textContent).toBe('2 of 2');
expect(
(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[2].parentNode! as HTMLElement).className
).toContain('rowFocused');
userEvent.click(prevResultButton);
await userEvent.click(prevResultButton);
expect(suffix.textContent).toBe('1 of 2');
expect(
(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[1].parentNode! as HTMLElement).className
).toContain('rowFocused');
userEvent.click(prevResultButton);
await userEvent.click(prevResultButton);
expect(suffix.textContent).toBe('2 of 2');
expect(
(screen.queryAllByText('', { selector: 'div[data-test-id="span-view"]' })[2].parentNode! as HTMLElement).className

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

@ -11,23 +11,23 @@ export const changeDatasource = async (name: string) => {
fireEvent.click(option);
};
export const inputQuery = (query: string, exploreId: ExploreId = ExploreId.left) => {
export const inputQuery = async (query: string, exploreId: ExploreId = ExploreId.left) => {
const input = withinExplore(exploreId).getByRole('textbox', { name: 'query' });
userEvent.clear(input);
userEvent.type(input, query);
await userEvent.clear(input);
await userEvent.type(input, query);
};
export const runQuery = (exploreId: ExploreId = ExploreId.left) => {
export const runQuery = async (exploreId: ExploreId = ExploreId.left) => {
const explore = withinExplore(exploreId);
const toolbar = within(explore.getByLabelText('Explore toolbar'));
const button = toolbar.getByRole('button', { name: /run query/i });
userEvent.click(button);
await userEvent.click(button);
};
export const openQueryHistory = async (exploreId: ExploreId = ExploreId.left) => {
const selector = withinExplore(exploreId);
const button = selector.getByRole('button', { name: 'Rich history button' });
userEvent.click(button);
await userEvent.click(button);
expect(
await selector.findByText('The history is local to your browser and is not shared with others.')
).toBeInTheDocument();
@ -35,26 +35,26 @@ export const openQueryHistory = async (exploreId: ExploreId = ExploreId.left) =>
export const closeQueryHistory = async (exploreId: ExploreId = ExploreId.left) => {
const closeButton = withinExplore(exploreId).getByRole('button', { name: 'Close query history' });
userEvent.click(closeButton);
await userEvent.click(closeButton);
};
export const switchToQueryHistoryTab = async (
name: 'Settings' | 'Query History',
exploreId: ExploreId = ExploreId.left
) => {
userEvent.click(withinExplore(exploreId).getByRole('tab', { name: `Tab ${name}` }));
await userEvent.click(withinExplore(exploreId).getByRole('tab', { name: `Tab ${name}` }));
};
export const selectStarredTabFirst = (exploreId: ExploreId = ExploreId.left) => {
export const selectStarredTabFirst = async (exploreId: ExploreId = ExploreId.left) => {
const checkbox = withinExplore(exploreId).getByRole('checkbox', {
name: 'Change the default active tab from “Query history” to “Starred”',
});
userEvent.click(checkbox);
await userEvent.click(checkbox);
};
export const selectOnlyActiveDataSource = (exploreId: ExploreId = ExploreId.left) => {
export const selectOnlyActiveDataSource = async (exploreId: ExploreId = ExploreId.left) => {
const checkbox = withinExplore(exploreId).getByLabelText(/Only show queries for data source currently active.*/);
userEvent.click(checkbox);
await userEvent.click(checkbox);
};
export const starQueryHistory = (queryIndex: number, exploreId: ExploreId = ExploreId.left) => {
@ -65,8 +65,8 @@ export const deleteQueryHistory = (queryIndex: number, exploreId: ExploreId = Ex
invokeAction(queryIndex, 'Delete query', exploreId);
};
const invokeAction = (queryIndex: number, actionAccessibleName: string, exploreId: ExploreId) => {
const invokeAction = async (queryIndex: number, actionAccessibleName: string, exploreId: ExploreId) => {
const selector = withinExplore(exploreId);
const buttons = selector.getAllByRole('button', { name: actionAccessibleName });
userEvent.click(buttons[queryIndex]);
await userEvent.click(buttons[queryIndex]);
};

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

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

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

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

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

@ -96,11 +96,11 @@ describe('PlaylistForm', () => {
});
describe('when deleting a playlist item', () => {
it('then the item should be removed and other items should be correct', () => {
it('then the item should be removed and other items should be correct', async () => {
getTestContext(playlist);
expect(rows()).toHaveLength(3);
userEvent.click(within(rows()[2]).getByRole('button', { name: /delete playlist item/i }));
await userEvent.click(within(rows()[2]).getByRole('button', { name: /delete playlist item/i }));
expect(rows()).toHaveLength(2);
expectCorrectRow({ index: 0, type: 'id', title: 'first item', first: true });
expectCorrectRow({ index: 1, type: 'id', title: 'middle item', last: true });
@ -108,10 +108,10 @@ describe('PlaylistForm', () => {
});
describe('when moving a playlist item up', () => {
it('then the item should be removed and other items should be correct', () => {
it('then the item should be removed and other items should be correct', async () => {
getTestContext(playlist);
userEvent.click(within(rows()[2]).getByRole('button', { name: /move playlist item order up/i }));
await userEvent.click(within(rows()[2]).getByRole('button', { name: /move playlist item order up/i }));
expectCorrectRow({ index: 0, type: 'id', title: 'first item', first: true });
expectCorrectRow({ index: 1, type: 'tag', title: 'last item' });
expectCorrectRow({ index: 2, type: 'id', title: 'middle item', last: true });
@ -119,10 +119,10 @@ describe('PlaylistForm', () => {
});
describe('when moving a playlist item down', () => {
it('then the item should be removed and other items should be correct', () => {
it('then the item should be removed and other items should be correct', async () => {
getTestContext(playlist);
userEvent.click(within(rows()[0]).getByRole('button', { name: /move playlist item order down/i }));
await userEvent.click(within(rows()[0]).getByRole('button', { name: /move playlist item order down/i }));
expectCorrectRow({ index: 0, type: 'id', title: 'middle item', first: true });
expectCorrectRow({ index: 1, type: 'id', title: 'first item' });
expectCorrectRow({ index: 2, type: 'tag', title: 'last item', last: true });
@ -152,7 +152,7 @@ describe('PlaylistForm', () => {
it('then an alert should appear and nothing should be submitted', async () => {
const { onSubmitMock } = getTestContext(playlist);
userEvent.clear(screen.getByRole('textbox', { name: /playlist interval/i }));
await userEvent.clear(screen.getByRole('textbox', { name: /playlist interval/i }));
fireEvent.submit(screen.getByRole('button', { name: /save/i }));
expect(await screen.findAllByRole('alert')).toHaveLength(1);
expect(onSubmitMock).not.toHaveBeenCalled();

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -83,9 +83,9 @@ describe('MetricsQueryEditor', () => {
const checkbox = await screen.findByLabelText('web-server');
expect(checkbox).toBeInTheDocument();
expect(checkbox).not.toBeChecked();
userEvent.click(checkbox);
await userEvent.click(checkbox);
expect(checkbox).toBeChecked();
userEvent.click(await screen.findByRole('button', { name: 'Apply' }));
await userEvent.click(await screen.findByRole('button', { name: 'Apply' }));
expect(onChange).toBeCalledTimes(1);
expect(onChange).toBeCalledWith(
@ -136,9 +136,9 @@ describe('MetricsQueryEditor', () => {
const checkbox = await screen.findByLabelText('db-server');
expect(checkbox).toBeInTheDocument();
expect(checkbox).not.toBeChecked();
userEvent.click(checkbox);
await userEvent.click(checkbox);
expect(checkbox).toBeChecked();
userEvent.click(await screen.findByRole('button', { name: 'Apply' }));
await userEvent.click(await screen.findByRole('button', { name: 'Apply' }));
expect(onChange).toBeCalledTimes(1);
expect(onChange).toBeCalledWith(

@ -100,7 +100,7 @@ describe('AzureMonitor ResourcePicker', () => {
advancedSection.click();
const advancedInput = await screen.findByLabelText('Resource URI');
userEvent.type(advancedInput, '/subscriptions/def-123');
await userEvent.type(advancedInput, '/subscriptions/def-123');
const applyButton = screen.getByRole('button', { name: 'Apply' });
applyButton.click();

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

@ -91,7 +91,7 @@ describe('Render', () => {
updateOptions: onUpdate,
}));
expect(screen.queryByText('is no longer supported', { exact: false })).toBeInTheDocument();
userEvent.click(screen.getByText('Clear Azure Monitor Logs Credentials'));
await userEvent.click(screen.getByText('Clear Azure Monitor Logs Credentials'));
expect(onUpdate).toHaveBeenCalled();
const newOpts = onUpdate.mock.calls[0][0]({});
expect(newOpts).toEqual({ jsonData: { azureLogAnalyticsSameAs: true } });

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

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

@ -1,4 +1,4 @@
import { act, render, screen, waitFor } from '@testing-library/react';
import { render, screen, waitFor } from '@testing-library/react';
import { backendSrv } from 'app/core/services/backend_srv';
import { createFetchResponse } from 'test/helpers/createFetchResponse';
import { DataQueryRequest, DataSourceInstanceSettings, dateTime, PluginType } from '@grafana/data';
@ -9,6 +9,7 @@ import React from 'react';
import SearchForm from './SearchForm';
import { testResponse } from '../testResponse';
import userEvent from '@testing-library/user-event';
import { UserEvent } from '@testing-library/user-event/dist/types/setup';
describe('SearchForm', () => {
it('should call the `onChange` function on click of the Input', async () => {
@ -38,7 +39,7 @@ describe('SearchForm', () => {
const asyncServiceSelect = await waitFor(() => screen.getByRole('combobox', { name: 'select-service-name' }));
expect(asyncServiceSelect).toBeInTheDocument();
userEvent.click(asyncServiceSelect);
await userEvent.click(asyncServiceSelect);
const jaegerService = await screen.findByText('jaeger-query');
expect(jaegerService).toBeInTheDocument();
@ -68,13 +69,21 @@ describe('SearchForm', () => {
});
describe('SearchForm', () => {
let user: UserEvent;
beforeEach(() => {
jest.useFakeTimers();
// Need to use delay: null here to work with fakeTimers
// see https://github.com/testing-library/user-event/issues/833
user = userEvent.setup({ delay: null });
});
afterEach(() => {
jest.useRealTimers();
});
it('should show loader if there is a delay fetching options', async () => {
const promise = Promise.resolve();
const handleOnChange = jest.fn(() => {
setTimeout(() => {
return promise;
}, 3000);
});
const handleOnChange = jest.fn();
const query = {
...defaultQuery,
targets: [
@ -91,12 +100,19 @@ describe('SearchForm', () => {
render(<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' });
userEvent.click(asyncServiceSelect);
const loader = screen.getByText('Loading options...');
await user.click(asyncServiceSelect);
expect(screen.getByText('Loading options...')).toBeInTheDocument();
expect(loader).toBeInTheDocument();
await act(() => promise);
jest.advanceTimersByTime(3000);
await waitFor(() => expect(screen.queryByText('Loading options...')).not.toBeInTheDocument());
});
});

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

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

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

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

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

@ -94,22 +94,22 @@ describe('PromQueryEditorByApp', () => {
expect(queryByTestId(alertingTestIds.editor)).toBeNull();
});
it('should not run query onBlur in explore', () => {
it('should not run query onBlur in explore', async () => {
const { getByTestId, onRunQuery } = setup(CoreApp.Explore);
const input = getByTestId('dummy-code-input');
expect(input).toBeInTheDocument();
userEvent.type(input, 'metric');
await userEvent.type(input, 'metric');
input.blur();
expect(onRunQuery).not.toHaveBeenCalled();
});
it('should run query onBlur in dashboard', () => {
it('should run query onBlur in dashboard', async () => {
const { getByTestId, onRunQuery } = setup(CoreApp.Dashboard);
const input = getByTestId('dummy-code-input');
expect(input).toBeInTheDocument();
userEvent.type(input, 'metric');
await userEvent.type(input, 'metric');
input.blur();
expect(onRunQuery).toHaveBeenCalled();
});

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

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

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

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

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

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

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

@ -1,13 +1,13 @@
import { screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
export function addOperation(section: string, op: string) {
export async function addOperation(section: string, op: string) {
const addOperationButton = screen.getByTitle('Add operation');
expect(addOperationButton).toBeInTheDocument();
userEvent.click(addOperationButton);
await userEvent.click(addOperationButton);
const sectionItem = screen.getByTitle(section);
expect(sectionItem).toBeInTheDocument();
// Weirdly the userEvent.click doesn't work here, it reports the item has pointer-events: none. Don't see that
// Weirdly the await userEvent.click doesn't work here, it reports the item has pointer-events: none. Don't see that
// anywhere when debugging so not sure what style is it picking up.
fireEvent.click(sectionItem.children[0]);
const opItem = screen.getByTitle(op);

@ -1,20 +1,25 @@
import NativeSearch from './NativeSearch';
import React from 'react';
import { act, render, screen } from '@testing-library/react';
import { render, screen, waitFor } from '@testing-library/react';
import { TempoDatasource, TempoQuery } from '../datasource';
import userEvent from '@testing-library/user-event';
import { UserEvent } from '@testing-library/user-event/dist/types/setup';
const getOptions = jest.fn().mockImplementation(() => {
return Promise.resolve([
{
value: 'customer',
label: 'customer',
},
{
value: 'driver',
label: 'driver',
},
]);
return new Promise((resolve) => {
setTimeout(() => {
resolve([
{
value: 'customer',
label: 'customer',
},
{
value: 'driver',
label: 'driver',
},
]);
}, 1000);
});
});
jest.mock('../language_provider', () => {
@ -31,6 +36,36 @@ const mockQuery = {
} as TempoQuery;
describe('NativeSearch', () => {
let user: UserEvent;
beforeEach(() => {
jest.useFakeTimers();
// Need to use delay: null here to work with fakeTimers
// see https://github.com/testing-library/user-event/issues/833
user = userEvent.setup({ delay: null });
});
afterEach(() => {
jest.useRealTimers();
});
it('should show loader when there is a delay', async () => {
render(
<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 () => {
const promise = Promise.resolve();
const handleOnChange = jest.fn(() => promise);
@ -54,56 +89,12 @@ describe('NativeSearch', () => {
const asyncServiceSelect = await screen.findByRole('combobox', { name: 'select-span-name' });
expect(asyncServiceSelect).toBeInTheDocument();
userEvent.click(asyncServiceSelect);
await user.click(asyncServiceSelect);
jest.advanceTimersByTime(1000);
const driverOption = await screen.findByText('driver');
userEvent.click(driverOption);
await user.click(driverOption);
expect(handleOnChange).toHaveBeenCalledWith(fakeOptionChoice);
});
});
describe('TempoLanguageProvider with delay', () => {
const getOptions2 = jest.fn().mockImplementation(() => {
return Promise.resolve([
{
value: 'customer',
label: 'customer',
},
{
value: 'driver',
label: 'driver',
},
]);
});
jest.mock('../language_provider', () => {
return jest.fn().mockImplementation(() => {
setTimeout(() => {
return { getOptions2 };
}, 3000);
});
});
it('should show loader', async () => {
const promise = Promise.resolve();
const handleOnChange = jest.fn(() => promise);
render(
<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);
});
});

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

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

@ -3997,7 +3997,7 @@ __metadata:
"@testing-library/jest-dom": 5.16.2
"@testing-library/react": 12.1.4
"@testing-library/react-hooks": 7.0.2
"@testing-library/user-event": 13.5.0
"@testing-library/user-event": 14.0.0
"@types/d3-interpolate": ^1.4.0
"@types/history": 4.7.11
"@types/jest": 27.4.1
@ -4180,7 +4180,7 @@ __metadata:
"@sentry/browser": 6.19.1
"@testing-library/dom": 8.13.0
"@testing-library/react": 12.1.4
"@testing-library/user-event": ^13.5.0
"@testing-library/user-event": 14.0.0
"@types/angular": 1.8.4
"@types/history": 4.7.11
"@types/jest": 27.4.1
@ -4387,7 +4387,7 @@ __metadata:
"@testing-library/jest-dom": 5.16.2
"@testing-library/react": 12.1.4
"@testing-library/react-hooks": 7.0.2
"@testing-library/user-event": 13.5.0
"@testing-library/user-event": 14.0.0
"@types/classnames": 2.3.0
"@types/common-tags": ^1.8.0
"@types/d3": 7.1.0
@ -9030,7 +9030,7 @@ __metadata:
languageName: node
linkType: hard
"@testing-library/dom@npm:8.13.0":
"@testing-library/dom@npm:8.13.0, @testing-library/dom@npm:>=7, @testing-library/dom@npm:^8.0.0":
version: 8.13.0
resolution: "@testing-library/dom@npm:8.13.0"
dependencies:
@ -9046,22 +9046,6 @@ __metadata:
languageName: node
linkType: hard
"@testing-library/dom@npm:>=7, @testing-library/dom@npm:^8.0.0":
version: 8.10.1
resolution: "@testing-library/dom@npm:8.10.1"
dependencies:
"@babel/code-frame": ^7.10.4
"@babel/runtime": ^7.12.5
"@types/aria-query": ^4.2.0
aria-query: ^5.0.0
chalk: ^4.1.0
dom-accessibility-api: ^0.5.9
lz-string: ^1.4.4
pretty-format: ^27.0.2
checksum: c11da16d981d4479fa18b2e90a62d2c19871da414016a4f80136394cf35e833ca4de4b56a64225f6bd5d3a13842176236d3508d217e3858c5e2ef35a3f391d5b
languageName: node
linkType: hard
"@testing-library/jest-dom@npm:5.16.2":
version: 5.16.2
resolution: "@testing-library/jest-dom@npm:5.16.2"
@ -9115,14 +9099,12 @@ __metadata:
languageName: node
linkType: hard
"@testing-library/user-event@npm:13.5.0, @testing-library/user-event@npm:^13.5.0":
version: 13.5.0
resolution: "@testing-library/user-event@npm:13.5.0"
dependencies:
"@babel/runtime": ^7.12.5
"@testing-library/user-event@npm:14.0.0":
version: 14.0.0
resolution: "@testing-library/user-event@npm:14.0.0"
peerDependencies:
"@testing-library/dom": ">=7.21.4"
checksum: 16319de685fbb7008f1ba667928f458b2d08196918002daca56996de80ef35e6d9de26e9e1ece7d00a004692b95a597cf9142fff0dc53f2f51606a776584f549
checksum: 171a40e680616e6a34299f9fb783dcadcd36f255256a53fe06cb71f4149715d8e5b84e65ea1c144af179f2e4b1799af92c79a8fc0c4c0ee2e94a713f2c476efd
languageName: node
linkType: hard
@ -20472,7 +20454,7 @@ __metadata:
"@testing-library/jest-dom": 5.16.2
"@testing-library/react": 12.1.4
"@testing-library/react-hooks": 7.0.2
"@testing-library/user-event": 13.5.0
"@testing-library/user-event": 14.0.0
"@types/angular": 1.8.4
"@types/angular-route": 1.7.2
"@types/classnames": 2.3.0

Loading…
Cancel
Save