diff --git a/public/app/features/alerting/.eslintrc b/public/app/features/alerting/.eslintrc index 82beed01b71..00287bc0bb8 100644 --- a/public/app/features/alerting/.eslintrc +++ b/public/app/features/alerting/.eslintrc @@ -10,6 +10,9 @@ { "files": ["**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)"], "extends": ["plugin:testing-library/react"], + "rules": { + "testing-library/prefer-user-event": "error", + }, }, ], } diff --git a/public/app/features/alerting/unified/AlertGroups.test.tsx b/public/app/features/alerting/unified/AlertGroups.test.tsx index 79aa3dc39b8..1243359be61 100644 --- a/public/app/features/alerting/unified/AlertGroups.test.tsx +++ b/public/app/features/alerting/unified/AlertGroups.test.tsx @@ -1,6 +1,4 @@ -import { render, waitFor, waitForElementToBeRemoved } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { TestProvider } from 'test/helpers/TestProvider'; +import { render, waitFor, waitForElementToBeRemoved } from 'test/test-utils'; import { byRole, byTestId, byText } from 'testing-library-selector'; import { selectors } from '@grafana/e2e-selectors'; @@ -28,11 +26,9 @@ const mocks = { const renderAmNotifications = () => { return render( - - - - - + + + ); }; @@ -84,7 +80,7 @@ describe('AlertGroups', () => { ]); }); - renderAmNotifications(); + const { user } = renderAmNotifications(); await waitFor(() => expect(mocks.api.fetchAlertGroups).toHaveBeenCalled()); @@ -96,10 +92,10 @@ describe('AlertGroups', () => { expect(labels[0]).toHaveTextContent('severitywarning'); expect(labels[1]).toHaveTextContent('regionUS-Central'); - await userEvent.click(ui.groupCollapseToggle.get(groups[0])); + await user.click(ui.groupCollapseToggle.get(groups[0])); expect(ui.groupTable.get()).toBeDefined(); - await userEvent.click(ui.collapseToggle.get(ui.groupTable.get())); + await user.click(ui.collapseToggle.get(ui.groupTable.get())); expect(ui.silenceButton.get(ui.groupTable.get())).toBeDefined(); expect(ui.sourceButton.get(ui.groupTable.get())).toBeDefined(); }); @@ -123,7 +119,7 @@ describe('AlertGroups', () => { return Promise.resolve(groups); }); - renderAmNotifications(); + const { user } = renderAmNotifications(); await waitFor(() => expect(mocks.api.fetchAlertGroups).toHaveBeenCalled()); let groups = await ui.group.findAll(); const groupByInput = ui.groupByInput.get(); @@ -134,7 +130,7 @@ describe('AlertGroups', () => { expect(groups[1]).toHaveTextContent('regionEMEA'); expect(groups[2]).toHaveTextContent('regionAPAC'); - await userEvent.type(groupByInput, 'appName{enter}'); + await user.type(groupByInput, 'appName{enter}'); await waitFor(() => expect(groupByWrapper).toHaveTextContent('appName')); @@ -146,10 +142,10 @@ describe('AlertGroups', () => { expect(groups[1]).toHaveTextContent('appNameauth'); expect(groups[2]).toHaveTextContent('appNamefrontend'); - await userEvent.click(ui.clearButton.get()); + await user.click(ui.clearButton.get()); await waitFor(() => expect(groupByWrapper).not.toHaveTextContent('appName')); - await userEvent.type(groupByInput, 'env{enter}'); + await user.type(groupByInput, 'env{enter}'); await waitFor(() => expect(groupByWrapper).toHaveTextContent('env')); groups = await ui.group.findAll(); @@ -158,10 +154,10 @@ describe('AlertGroups', () => { expect(groups[0]).toHaveTextContent('envproduction'); expect(groups[1]).toHaveTextContent('envstaging'); - await userEvent.click(ui.clearButton.get()); + await user.click(ui.clearButton.get()); await waitFor(() => expect(groupByWrapper).not.toHaveTextContent('env')); - await userEvent.type(groupByInput, 'uniqueLabel{enter}'); + await user.type(groupByInput, 'uniqueLabel{enter}'); await waitFor(() => expect(groupByWrapper).toHaveTextContent('uniqueLabel')); groups = await ui.group.findAll(); @@ -183,9 +179,7 @@ describe('AlertGroups', () => { ]; mocks.api.fetchAlertGroups.mockResolvedValue(amGroups); - const user = userEvent.setup(); - - renderAmNotifications(); + const { user } = renderAmNotifications(); await waitForElementToBeRemoved(ui.loadingIndicator.query()); // reset the input of the MultiSelect component diff --git a/public/app/features/alerting/unified/AlertsFolderView.test.tsx b/public/app/features/alerting/unified/AlertsFolderView.test.tsx index ec1d3a27c5f..05f88cff4c5 100644 --- a/public/app/features/alerting/unified/AlertsFolderView.test.tsx +++ b/public/app/features/alerting/unified/AlertsFolderView.test.tsx @@ -1,6 +1,4 @@ -import { render } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { TestProvider } from 'test/helpers/TestProvider'; +import { render } from 'test/test-utils'; import { byTestId } from 'testing-library-selector'; import { FolderState } from 'app/types'; @@ -74,11 +72,7 @@ describe('AlertsFolderView tests', () => { combinedNamespaceMock.mockReturnValue([grafanaNamespace]); // Act - render( - - - - ); + render(); // Assert const alertRows = ui.ruleList.row.queryAll(); @@ -114,11 +108,7 @@ describe('AlertsFolderView tests', () => { combinedNamespaceMock.mockReturnValue([grafanaNamespace]); // Act - render( - - - - ); + render(); // Assert expect(ui.ruleList.row.queryAll()).toHaveLength(0); @@ -144,13 +134,9 @@ describe('AlertsFolderView tests', () => { combinedNamespaceMock.mockReturnValue([grafanaNamespace]); // Act - render( - - - - ); + const { user } = render(); - await userEvent.type(ui.filter.name.get(), 'cpu'); + await user.type(ui.filter.name.get(), 'cpu'); // Assert expect(ui.ruleList.row.queryAll()).toHaveLength(1); @@ -180,13 +166,9 @@ describe('AlertsFolderView tests', () => { combinedNamespaceMock.mockReturnValue([grafanaNamespace]); // Act - render( - - - - ); + const { user } = render(); - await userEvent.type(ui.filter.label.get(), 'severity=critical'); + await user.type(ui.filter.label.get(), 'severity=critical'); // Assert expect(ui.ruleList.row.queryAll()).toHaveLength(1); diff --git a/public/app/features/alerting/unified/CloneRuleEditor.test.tsx b/public/app/features/alerting/unified/CloneRuleEditor.test.tsx index 3e5d853cb72..38155aa917a 100644 --- a/public/app/features/alerting/unified/CloneRuleEditor.test.tsx +++ b/public/app/features/alerting/unified/CloneRuleEditor.test.tsx @@ -1,7 +1,6 @@ -import { render, waitFor, waitForElementToBeRemoved, within } from '@testing-library/react'; import * as React from 'react'; import { FormProvider, useForm } from 'react-hook-form'; -import { TestProvider } from 'test/helpers/TestProvider'; +import { getWrapper, render, waitFor, waitForElementToBeRemoved, within } from 'test/test-utils'; import { byRole, byTestId, byText } from 'testing-library-selector'; import { selectors } from '@grafana/e2e-selectors/src'; @@ -68,12 +67,13 @@ const ui = { loadingIndicator: byText('Loading the rule...'), }; +const Providers = getWrapper({ renderWithRouter: true }); function Wrapper({ children }: React.PropsWithChildren<{}>) { const formApi = useForm({ defaultValues: getDefaultFormValues() }); return ( - + {children} - + ); } diff --git a/public/app/features/alerting/unified/GrafanaRuleQueryViewer.test.tsx b/public/app/features/alerting/unified/GrafanaRuleQueryViewer.test.tsx index b057ab9186e..a764fa0c20a 100644 --- a/public/app/features/alerting/unified/GrafanaRuleQueryViewer.test.tsx +++ b/public/app/features/alerting/unified/GrafanaRuleQueryViewer.test.tsx @@ -1,5 +1,4 @@ -import { render, waitFor, screen } from '@testing-library/react'; -import { TestProvider } from 'test/helpers/TestProvider'; +import { render, waitFor, screen } from 'test/test-utils'; import { DataSourceRef } from '@grafana/schema'; import { AlertQuery } from 'app/types/unified-alerting-dto'; @@ -73,9 +72,7 @@ describe('GrafanaRuleQueryViewer', () => { getExpression('C', { type: '' }), getExpression('D', { type: '' }), ]; - render(, { - wrapper: TestProvider, - }); + render(); await waitFor(() => expect(screen.getByTestId('queries-container')).toHaveStyle('flex-wrap: wrap')); expect(screen.getByTestId('expressions-container')).toHaveStyle('flex-wrap: wrap'); diff --git a/public/app/features/alerting/unified/NotificationPolicies.test.tsx b/public/app/features/alerting/unified/NotificationPolicies.test.tsx index e72488f1bf0..919a19d2d78 100644 --- a/public/app/features/alerting/unified/NotificationPolicies.test.tsx +++ b/public/app/features/alerting/unified/NotificationPolicies.test.tsx @@ -1,10 +1,8 @@ -import { render, waitFor, within } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { TestProvider } from 'test/helpers/TestProvider'; import { selectOptionInTest } from 'test/helpers/selectOptionInTest'; +import { render, userEvent, waitFor, within } from 'test/test-utils'; import { byLabelText, byRole, byTestId, byText } from 'testing-library-selector'; -import { DataSourceSrv, locationService, setDataSourceSrv } from '@grafana/runtime'; +import { DataSourceSrv, setDataSourceSrv } from '@grafana/runtime'; import { contextSrv } from 'app/core/services/context_srv'; import { AlertManagerCortexConfig, @@ -50,16 +48,14 @@ const mocks = { const useGetGrafanaReceiverTypeCheckerMock = jest.spyOn(grafanaApp, 'useGetGrafanaReceiverTypeChecker'); const renderNotificationPolicies = (alertManagerSourceName?: string) => { - locationService.push(location); - locationService.push( - '/alerting/routes' + (alertManagerSourceName ? `?${ALERTMANAGER_NAME_QUERY_KEY}=${alertManagerSourceName}` : '') - ); - - return render( - - - - ); + return render(, { + historyOptions: { + initialEntries: [ + '/alerting/routes' + + (alertManagerSourceName ? `?${ALERTMANAGER_NAME_QUERY_KEY}=${alertManagerSourceName}` : ''), + ], + }, + }); }; const dataSources = { @@ -227,7 +223,7 @@ describe('NotificationPolicies', () => { template_files: {}, }); - await renderNotificationPolicies(); + renderNotificationPolicies(); await waitFor(() => expect(mocks.api.fetchAlertManagerConfig).toHaveBeenCalledTimes(1)); @@ -282,30 +278,30 @@ describe('NotificationPolicies', () => { return Promise.resolve(currentConfig.current); }); - await renderNotificationPolicies(); + const { user } = renderNotificationPolicies(); expect(await ui.rootReceiver.find()).toHaveTextContent('default'); expect(ui.rootGroupBy.get()).toHaveTextContent('alertname'); // open root route for editing const rootRouteContainer = await ui.rootRouteContainer.find(); - await userEvent.click(ui.editButton.get(rootRouteContainer)); + await user.click(ui.editButton.get(rootRouteContainer)); // configure receiver & group by const receiverSelect = await ui.receiverSelect.find(); await clickSelectOption(receiverSelect, 'critical'); const groupSelect = ui.groupSelect.get(); - await userEvent.type(byRole('combobox').get(groupSelect), 'namespace{enter}'); + await user.type(byRole('combobox').get(groupSelect), 'namespace{enter}'); // configure timing intervals - await userEvent.click(byText('Timing options').get(rootRouteContainer)); + await user.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 - await userEvent.click(ui.saveButton.get(rootRouteContainer)); + await user.click(ui.saveButton.get(rootRouteContainer)); // wait for it to go out of edit mode await waitFor(() => expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument()); @@ -344,21 +340,21 @@ describe('NotificationPolicies', () => { template_files: {}, }); - await renderNotificationPolicies(); + const { user } = renderNotificationPolicies(); // open root route for editing const rootRouteContainer = await ui.rootRouteContainer.find(); - await userEvent.click(ui.editButton.get(rootRouteContainer)); + await user.click(ui.editButton.get(rootRouteContainer)); // configure receiver & group by const receiverSelect = await ui.receiverSelect.find(); await clickSelectOption(receiverSelect, 'default'); const groupSelect = ui.groupSelect.get(); - await userEvent.type(byRole('combobox').get(groupSelect), 'severity{enter}'); - await userEvent.type(byRole('combobox').get(groupSelect), 'namespace{enter}'); + await user.type(byRole('combobox').get(groupSelect), 'severity{enter}'); + await user.type(byRole('combobox').get(groupSelect), 'namespace{enter}'); //save - await userEvent.click(ui.saveButton.get(rootRouteContainer)); + await user.click(ui.saveButton.get(rootRouteContainer)); // wait for it to go out of edit mode await waitFor(() => expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument()); @@ -408,7 +404,7 @@ describe('NotificationPolicies', () => { refetch: jest.fn(), })); - await renderNotificationPolicies(); + renderNotificationPolicies(); await waitFor(() => expect(mocks.api.fetchAlertManagerConfig).toHaveBeenCalledTimes(1)); expect(await byText("Alertmanager has exploded. it's gone. Forget about it.").find()).toBeInTheDocument(); expect(ui.rootReceiver.query()).not.toBeInTheDocument(); @@ -443,14 +439,14 @@ describe('NotificationPolicies', () => { return Promise.resolve(currentConfig.current); }); - await renderNotificationPolicies(GRAFANA_RULES_SOURCE_NAME); + const { user } = renderNotificationPolicies(GRAFANA_RULES_SOURCE_NAME); expect(await ui.rootReceiver.find()).toHaveTextContent('default'); expect(mocks.api.fetchAlertManagerConfig).toHaveBeenCalled(); // Toggle a save to test new object_matchers const rootRouteContainer = await ui.rootRouteContainer.find(); - await userEvent.click(ui.editButton.get(rootRouteContainer)); - await userEvent.click(ui.saveButton.get(rootRouteContainer)); + await user.click(ui.editButton.get(rootRouteContainer)); + await user.click(ui.saveButton.get(rootRouteContainer)); await waitFor(() => expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument()); @@ -513,18 +509,18 @@ describe('NotificationPolicies', () => { mocks.api.updateAlertManagerConfig.mockResolvedValue(Promise.resolve()); - await renderNotificationPolicies(GRAFANA_RULES_SOURCE_NAME); + const { user } = renderNotificationPolicies(GRAFANA_RULES_SOURCE_NAME); await waitFor(() => expect(mocks.api.fetchAlertManagerConfig).toHaveBeenCalled()); const deleteButtons = await ui.deleteRouteButton.findAll(); expect(deleteButtons).toHaveLength(1); - await userEvent.click(deleteButtons[0]); + await user.click(deleteButtons[0]); const confirmDeleteButton = ui.confirmDeleteButton.get(ui.confirmDeleteModal.get()); expect(confirmDeleteButton).toBeInTheDocument(); - await userEvent.click(confirmDeleteButton); + await user.click(confirmDeleteButton); expect(mocks.api.updateAlertManagerConfig).toHaveBeenCalledWith<[string, AlertManagerCortexConfig]>( GRAFANA_RULES_SOURCE_NAME, @@ -569,14 +565,14 @@ describe('NotificationPolicies', () => { return Promise.resolve(currentConfig.current); }); - await renderNotificationPolicies(dataSources.am.name); + const { user } = renderNotificationPolicies(dataSources.am.name); expect(await ui.rootReceiver.find()).toHaveTextContent('default'); expect(mocks.api.fetchAlertManagerConfig).toHaveBeenCalled(); // Toggle a save to test new object_matchers const rootRouteContainer = await ui.rootRouteContainer.find(); - await userEvent.click(ui.editButton.get(rootRouteContainer)); - await userEvent.click(ui.saveButton.get(rootRouteContainer)); + await user.click(ui.editButton.get(rootRouteContainer)); + await user.click(ui.saveButton.get(rootRouteContainer)); await waitFor(() => expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument()); @@ -615,7 +611,7 @@ describe('NotificationPolicies', () => { ...someCloudAlertManagerStatus, config: someCloudAlertManagerConfig.alertmanager_config, }); - await renderNotificationPolicies(dataSources.promAlertManager.name); + renderNotificationPolicies(dataSources.promAlertManager.name); const rootRouteContainer = await ui.rootRouteContainer.find(); expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument(); const rows = await ui.row.findAll(); @@ -645,7 +641,7 @@ describe('NotificationPolicies', () => { refetch: jest.fn(), })); - await renderNotificationPolicies(dataSources.promAlertManager.name); + renderNotificationPolicies(dataSources.promAlertManager.name); const rootRouteContainer = await ui.rootRouteContainer.find(); await waitFor(() => expect(within(rootRouteContainer).getByTestId('matching-instances')).toHaveTextContent('0instances') @@ -684,10 +680,10 @@ describe('NotificationPolicies', () => { mocks.api.fetchAlertManagerConfig.mockResolvedValue(defaultConfig); - await renderNotificationPolicies(dataSources.am.name); + const { user } = renderNotificationPolicies(dataSources.am.name); const rows = await ui.row.findAll(); expect(rows).toHaveLength(1); - await userEvent.click(ui.editRouteButton.get(rows[0])); + await user.click(ui.editRouteButton.get(rows[0])); const muteTimingSelect = ui.muteTimingSelect.get(); await clickSelectOption(muteTimingSelect, 'default-mute'); @@ -696,7 +692,7 @@ describe('NotificationPolicies', () => { const savePolicyButton = ui.savePolicyButton.get(); expect(savePolicyButton).toBeInTheDocument(); - await userEvent.click(savePolicyButton); + await user.click(savePolicyButton); await waitFor(() => expect(savePolicyButton).not.toBeInTheDocument()); @@ -730,7 +726,7 @@ describe('NotificationPolicies', () => { message: 'alertmanager storage object not found', }); - await renderNotificationPolicies(); + renderNotificationPolicies(); await waitFor(() => expect(mocks.api.fetchAlertManagerConfig).toHaveBeenCalledTimes(1)); @@ -812,15 +808,17 @@ describe('findRoutesMatchingFilters', () => { }); const clickSelectOption = async (selectElement: HTMLElement, optionText: string): Promise => { - await userEvent.click(byRole('combobox').get(selectElement)); + const user = userEvent.setup(); + await user.click(byRole('combobox').get(selectElement)); await selectOptionInTest(selectElement, optionText); }; const updateTiming = async (selectElement: HTMLElement, value: string, timeUnit: string): Promise => { + const user = userEvent.setup(); const input = byRole('textbox').get(selectElement); const select = byRole('combobox').get(selectElement); - await userEvent.clear(input); - await userEvent.type(input, value); - await userEvent.click(select); + await user.clear(input); + await user.type(input, value); + await user.click(select); await selectOptionInTest(selectElement, timeUnit); }; diff --git a/public/app/features/alerting/unified/PanelAlertTabContent.test.tsx b/public/app/features/alerting/unified/PanelAlertTabContent.test.tsx index 71d76da285e..339c30c2ecf 100644 --- a/public/app/features/alerting/unified/PanelAlertTabContent.test.tsx +++ b/public/app/features/alerting/unified/PanelAlertTabContent.test.tsx @@ -1,5 +1,4 @@ -import { render } from '@testing-library/react'; -import { TestProvider } from 'test/helpers/TestProvider'; +import { render } from 'test/test-utils'; import { byTestId, byText } from 'testing-library-selector'; import { DataSourceApi } from '@grafana/data'; @@ -8,7 +7,6 @@ import { setDataSourceSrv, setPluginExtensionsHook } from '@grafana/runtime'; import * as ruleActionButtons from 'app/features/alerting/unified/components/rules/RuleActionsButtons'; import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; -import { configureStore } from 'app/store/configureStore'; import { AccessControlAction } from 'app/types'; import { AlertQuery, PromRulesResponse } from 'app/types/unified-alerting-dto'; @@ -60,16 +58,8 @@ const mocks = { rulerBuilderMock: jest.mocked(apiRuler.rulerUrlBuilder), }; -const renderAlertTabContent = ( - dashboard: DashboardModel, - panel: PanelModel, - initialStore?: ReturnType -) => { - render( - - - - ); +const renderAlertTabContent = (dashboard: DashboardModel, panel: PanelModel) => { + render(); }; const promResponse: PromRulesResponse = { diff --git a/public/app/features/alerting/unified/RedirectToRuleViewer.test.tsx b/public/app/features/alerting/unified/RedirectToRuleViewer.test.tsx index 9e71045b785..aae12f66c77 100644 --- a/public/app/features/alerting/unified/RedirectToRuleViewer.test.tsx +++ b/public/app/features/alerting/unified/RedirectToRuleViewer.test.tsx @@ -1,9 +1,7 @@ -import { render, screen } from '@testing-library/react'; import { useLocation } from 'react-use'; -import { TestProvider } from 'test/helpers/TestProvider'; +import { render, screen } from 'test/test-utils'; import { DataSourceJsonData, PluginMeta } from '@grafana/data'; -import { locationService } from '@grafana/runtime'; import { CombinedRule, Rule } from '../../../types/unified-alerting'; import { PromRuleType } from '../../../types/unified-alerting-dto'; @@ -27,13 +25,7 @@ jest.mock('react-use', () => ({ const renderRedirectToRuleViewer = (pathname: string, search?: string) => { jest.mocked(useLocation).mockReturnValue({ pathname, trigger: '', search }); - locationService.push(pathname); - - return render( - - - - ); + return render(, { historyOptions: { initialEntries: [pathname] } }); }; const mockRuleSourceByName = () => { diff --git a/public/app/features/alerting/unified/RuleEditorCloudOnlyAllowed.test.tsx b/public/app/features/alerting/unified/RuleEditorCloudOnlyAllowed.test.tsx index 477324181e0..10f71e526d3 100644 --- a/public/app/features/alerting/unified/RuleEditorCloudOnlyAllowed.test.tsx +++ b/public/app/features/alerting/unified/RuleEditorCloudOnlyAllowed.test.tsx @@ -1,6 +1,5 @@ -import { screen, waitForElementToBeRemoved } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import { renderRuleEditor, ui } from 'test/helpers/alertingRuleEditor'; +import { screen, waitForElementToBeRemoved } from 'test/test-utils'; import { byText } from 'testing-library-selector'; import { setDataSourceSrv } from '@grafana/runtime'; @@ -182,7 +181,7 @@ describe('RuleEditor cloud: checking editable data sources', () => { mocks.searchFolders.mockResolvedValue([]); // render rule editor, select mimir/loki managed alerts - renderRuleEditor(); + const { user } = renderRuleEditor(); await waitForElementToBeRemoved(screen.queryAllByTestId('Spinner')); await ui.inputs.name.find(); @@ -190,14 +189,14 @@ describe('RuleEditor cloud: checking editable data sources', () => { const switchToCloudButton = screen.getByText('Data source-managed'); expect(switchToCloudButton).toBeInTheDocument(); - await userEvent.click(switchToCloudButton); + await user.click(switchToCloudButton); //expressions are removed after switching to data-source managed expect(screen.queryAllByLabelText('Remove expression')).toHaveLength(0); // check that only rules sources that have ruler available are there const dataSourceSelect = ui.inputs.dataSource.get(); - await userEvent.click(dataSourceSelect); + await user.click(dataSourceSelect); expect(byText('cortex with ruler').query()).toBeInTheDocument(); expect(byText('loki with ruler').query()).toBeInTheDocument(); diff --git a/public/app/features/alerting/unified/RuleEditorCloudRules.test.tsx b/public/app/features/alerting/unified/RuleEditorCloudRules.test.tsx index f3d8cf4e126..307bf53b438 100644 --- a/public/app/features/alerting/unified/RuleEditorCloudRules.test.tsx +++ b/public/app/features/alerting/unified/RuleEditorCloudRules.test.tsx @@ -1,8 +1,8 @@ -import { screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import * as React from 'react'; import { renderRuleEditor, ui } from 'test/helpers/alertingRuleEditor'; import { clickSelectOption } from 'test/helpers/selectOptionInTest'; +import { screen, waitFor, waitForElementToBeRemoved } from 'test/test-utils'; import { selectors } from '@grafana/e2e-selectors'; import { contextSrv } from 'app/core/services/context_srv'; diff --git a/public/app/features/alerting/unified/RuleEditorExisting.test.tsx b/public/app/features/alerting/unified/RuleEditorExisting.test.tsx index 72108f78ce4..66edcec66cb 100644 --- a/public/app/features/alerting/unified/RuleEditorExisting.test.tsx +++ b/public/app/features/alerting/unified/RuleEditorExisting.test.tsx @@ -1,11 +1,8 @@ -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import * as React from 'react'; import { Route } from 'react-router-dom'; -import { TestProvider } from 'test/helpers/TestProvider'; import { ui } from 'test/helpers/alertingRuleEditor'; +import { render, screen, waitFor } from 'test/test-utils'; -import { locationService } from '@grafana/runtime'; import { contextSrv } from 'app/core/services/context_srv'; import { DashboardSearchHit, DashboardSearchItemType } from 'app/features/search/types'; @@ -53,13 +50,9 @@ const mocks = { setupMswServer(); function renderRuleEditor(identifier?: string) { - locationService.push(identifier ? `/alerting/${identifier}/edit` : `/alerting/new`); - - return render( - - - - ); + return render(, { + historyOptions: { initialEntries: [identifier ? `/alerting/${identifier}/edit` : `/alerting/new`] }, + }); } describe('RuleEditor grafana managed rules', () => { @@ -121,7 +114,7 @@ describe('RuleEditor grafana managed rules', () => { // mocks.api.fetchRulerRulesNamespace.mockResolvedValue([]); mocks.searchFolders.mockResolvedValue([folder, slashedFolder] as DashboardSearchHit[]); - renderRuleEditor(grafanaRulerRule.grafana_alert.uid); + const { user } = renderRuleEditor(grafanaRulerRule.grafana_alert.uid); // check that it's filled in const nameInput = await ui.inputs.name.find(); @@ -138,18 +131,18 @@ describe('RuleEditor grafana managed rules', () => { //todo: move this test to a unit test in FolderAndGroup unit test // const folderInput = await ui.inputs.folderContainer.find(); // expect(within(folderInput).queryByText("Folders with '/' character are not allowed.")).not.toBeInTheDocument(); - // await userEvent.type(within(folderInput).getByRole('combobox'), 'new slashed //'); + // await user.type(within(folderInput).getByRole('combobox'), 'new slashed //'); // expect(within(folderInput).getByText("Folders with '/' character are not allowed.")).toBeInTheDocument(); - // await userEvent.keyboard('{backspace} {backspace}{backspace}'); + // await user.keyboard('{backspace} {backspace}{backspace}'); // expect(within(folderInput).queryByText("Folders with '/' character are not allowed.")).not.toBeInTheDocument(); // add an annotation - await userEvent.click(screen.getByText('Add custom annotation')); - await userEvent.type(screen.getByPlaceholderText('Enter custom annotation name...'), 'custom'); - await userEvent.type(screen.getByPlaceholderText('Enter custom annotation content...'), 'value'); + await user.click(screen.getByText('Add custom annotation')); + await user.type(screen.getByPlaceholderText('Enter custom annotation name...'), 'custom'); + await user.type(screen.getByPlaceholderText('Enter custom annotation content...'), 'value'); // save and check what was sent to backend - await userEvent.click(ui.buttons.save.get()); + await user.click(ui.buttons.save.get()); await waitFor(() => expect(mocks.api.setRulerRuleGroup).toHaveBeenCalled()); mocks.searchFolders.mockResolvedValue([] as DashboardSearchHit[]); diff --git a/public/app/features/alerting/unified/components/notification-policies/EditDefaultPolicyForm.test.tsx b/public/app/features/alerting/unified/components/notification-policies/EditDefaultPolicyForm.test.tsx index cb18c7c188e..3c16817ebb2 100644 --- a/public/app/features/alerting/unified/components/notification-policies/EditDefaultPolicyForm.test.tsx +++ b/public/app/features/alerting/unified/components/notification-policies/EditDefaultPolicyForm.test.tsx @@ -1,11 +1,9 @@ -import { render } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import { noop } from 'lodash'; +import { render } from 'test/test-utils'; import { byRole } from 'testing-library-selector'; import { Button } from '@grafana/ui'; -import { TestProvider } from '../../../../../../test/helpers/TestProvider'; import { RouteWithID } from '../../../../../plugins/datasource/alertmanager/types'; import * as grafanaApp from '../../components/receivers/grafanaAppReceivers/grafanaApp'; import { FormAmRoute } from '../../types/amroutes'; @@ -30,9 +28,7 @@ useGetGrafanaReceiverTypeCheckerMock.mockReturnValue(() => undefined); describe('EditDefaultPolicyForm', function () { describe('Timing options', function () { it('should render prometheus duration strings in form inputs', async function () { - const user = userEvent.setup(); - - renderRouteForm({ + const { user } = renderRouteForm({ id: '0', group_wait: '1m30s', group_interval: '2d4h30m35s', @@ -45,10 +41,8 @@ describe('EditDefaultPolicyForm', function () { expect(ui.repeatIntervalInput.get()).toHaveValue('1w2d6h'); }); it('should allow submitting valid prometheus duration strings', async function () { - const user = userEvent.setup(); - const onSubmit = jest.fn(); - renderRouteForm( + const { user } = renderRouteForm( { id: '0', receiver: 'default', @@ -78,10 +72,8 @@ describe('EditDefaultPolicyForm', function () { }); it('should show an error if repeat interval is lower than group interval', async function () { - const user = userEvent.setup(); - const onSubmit = jest.fn(); - renderRouteForm( + const { user } = renderRouteForm( { id: '0', receiver: 'default', @@ -104,10 +96,8 @@ describe('EditDefaultPolicyForm', function () { }); it('should allow resetting existing timing options', async function () { - const user = userEvent.setup(); - const onSubmit = jest.fn(); - renderRouteForm( + const { user } = renderRouteForm( { id: '0', receiver: 'default', @@ -143,14 +133,13 @@ function renderRouteForm( receivers: AmRouteReceiver[] = [], onSubmit: (route: Partial) => void = noop ) { - render( + return render( Update default policy} onSubmit={onSubmit} receivers={receivers} route={route} - />, - { wrapper: TestProvider } + /> ); } diff --git a/public/app/features/alerting/unified/components/notification-policies/EditNotificationPolicyForm.test.tsx b/public/app/features/alerting/unified/components/notification-policies/EditNotificationPolicyForm.test.tsx index 429cddead45..72c7d5ea602 100644 --- a/public/app/features/alerting/unified/components/notification-policies/EditNotificationPolicyForm.test.tsx +++ b/public/app/features/alerting/unified/components/notification-policies/EditNotificationPolicyForm.test.tsx @@ -1,11 +1,9 @@ -import { render } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import { noop } from 'lodash'; +import { render } from 'test/test-utils'; import { byRole } from 'testing-library-selector'; import { Button } from '@grafana/ui'; -import { TestProvider } from '../../../../../../test/helpers/TestProvider'; import { RouteWithID } from '../../../../../plugins/datasource/alertmanager/types'; import * as grafanaApp from '../../components/receivers/grafanaAppReceivers/grafanaApp'; import { AlertmanagerProvider } from '../../state/AlertmanagerContext'; @@ -44,10 +42,8 @@ describe('EditNotificationPolicyForm', function () { }); it('should allow submitting valid prometheus duration strings', async function () { - const user = userEvent.setup(); - const onSubmit = jest.fn(); - renderRouteForm( + const { user } = renderRouteForm( { id: '1', receiver: 'default', @@ -77,10 +73,8 @@ describe('EditNotificationPolicyForm', function () { }); it('should show an error if repeat interval is lower than group interval', async function () { - const user = userEvent.setup(); - const onSubmit = jest.fn(); - renderRouteForm( + const { user } = renderRouteForm( { id: '1', receiver: 'default', @@ -103,10 +97,8 @@ describe('EditNotificationPolicyForm', function () { }); it('should allow resetting existing timing options', async function () { - const user = userEvent.setup(); - const onSubmit = jest.fn(); - renderRouteForm( + const { user } = renderRouteForm( { id: '0', receiver: 'default', @@ -141,7 +133,7 @@ function renderRouteForm( receivers: AmRouteReceiver[] = [], onSubmit: (route: Partial) => void = noop ) { - render( + return render( Update default policy} @@ -149,7 +141,6 @@ function renderRouteForm( receivers={receivers} route={route} /> - , - { wrapper: TestProvider } + ); } diff --git a/public/app/features/alerting/unified/components/receivers/PayloadEditor.test.tsx b/public/app/features/alerting/unified/components/receivers/PayloadEditor.test.tsx index 2feb251e8b6..b337e6198d2 100644 --- a/public/app/features/alerting/unified/components/receivers/PayloadEditor.test.tsx +++ b/public/app/features/alerting/unified/components/receivers/PayloadEditor.test.tsx @@ -1,8 +1,6 @@ -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import { useState } from 'react'; import { Props } from 'react-virtualized-auto-sizer'; -import { TestProvider } from 'test/helpers/TestProvider'; +import { render, screen, waitFor } from 'test/test-utils'; import { PayloadEditor, RESET_TO_DEFAULT } from './PayloadEditor'; @@ -47,13 +45,10 @@ const PayloadEditorWithState = () => { /> ); }; -const renderWithProvider = () => { - render(, { wrapper: TestProvider }); -}; describe('Payload editor', () => { it('Should render default payload by default', async () => { - renderWithProvider(); + render(); await waitFor(() => { expect(screen.getByTestId('mockeditor')).toHaveValue( `[ { "annotations": { "summary": "Instance instance1 has been down for more than 5 minutes" }, "labels": { "instance": "instance1" }, "startsAt": "2023-04-25T15:28:56.440Z" }]` @@ -62,20 +57,20 @@ describe('Payload editor', () => { }); it('Should render default payload after clicking reset to default button', async () => { - renderWithProvider(); + const { user } = render(); await waitFor(() => { expect(screen.getByTestId('mockeditor')).toHaveValue( '[ { "annotations": { "summary": "Instance instance1 has been down for more than 5 minutes" }, "labels": { "instance": "instance1" }, "startsAt": "2023-04-25T15:28:56.440Z" }]' ); }); - await userEvent.type(screen.getByTestId('mockeditor'), 'this is the something'); + await user.type(screen.getByTestId('mockeditor'), 'this is the something'); expect(screen.getByTestId('mockeditor')).toHaveValue( '[ { "annotations": { "summary": "Instance instance1 has been down for more than 5 minutes" }, "labels": { "instance": "instance1" }, "startsAt": "2023-04-25T15:28:56.440Z" }]this is the something' ); // click edit payload > reset to defaults - await userEvent.click(screen.getByRole('button', { name: 'Edit payload' })); - await userEvent.click(screen.getByRole('menuitem', { name: RESET_TO_DEFAULT })); + await user.click(screen.getByRole('button', { name: 'Edit payload' })); + await user.click(screen.getByRole('menuitem', { name: RESET_TO_DEFAULT })); await waitFor(() => expect(screen.queryByTestId('mockeditor')).toHaveValue( '[ { "annotations": { "summary": "Instance instance1 has been down for more than 5 minutes" }, "labels": { "instance": "instance1" }, "startsAt": "2023-04-25T15:28:56.440Z" }]' diff --git a/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/onCall/useOnCallIntegration.test.ts b/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/onCall/useOnCallIntegration.test.ts index a45bfd7fd88..26531e46242 100644 --- a/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/onCall/useOnCallIntegration.test.ts +++ b/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/onCall/useOnCallIntegration.test.ts @@ -1,5 +1,4 @@ -import { renderHook, waitFor } from '@testing-library/react'; -import { TestProvider } from 'test/helpers/TestProvider'; +import { renderHook, waitFor, getWrapper } from 'test/test-utils'; import { setupMswServer } from 'app/features/alerting/unified/mockApi'; import { disablePlugin } from 'app/features/alerting/unified/mocks/server/configure'; @@ -16,6 +15,8 @@ import { OnCallIntegrationSetting, OnCallIntegrationType, useOnCallIntegration } setupMswServer(); +const wrapper = () => getWrapper({ renderWithRouter: true }); + describe('useOnCallIntegration', () => { beforeEach(() => { setOnCallIntegrations([ @@ -32,7 +33,7 @@ describe('useOnCallIntegration', () => { describe('When OnCall Alerting V2 integration enabled', () => { it('extendOnCallReceivers should add new settings to the oncall receiver', async () => { - const { result } = renderHook(() => useOnCallIntegration(), { wrapper: TestProvider }); + const { result } = renderHook(() => useOnCallIntegration(), { wrapper: wrapper() }); await waitFor(() => expect(result.current.isLoadingOnCallIntegration).toBe(false)); @@ -62,7 +63,7 @@ describe('useOnCallIntegration', () => { }); it('createOnCallIntegrations should provide integration name and url validators', async () => { - const { result } = renderHook(() => useOnCallIntegration(), { wrapper: TestProvider }); + const { result } = renderHook(() => useOnCallIntegration(), { wrapper: wrapper() }); await waitFor(() => expect(result.current.isLoadingOnCallIntegration).toBe(false)); @@ -83,7 +84,7 @@ describe('useOnCallIntegration', () => { }); it('extendOnCallNotifierFeatures should add integration type and name options and swap url to a select option', async () => { - const { result } = renderHook(() => useOnCallIntegration(), { wrapper: TestProvider }); + const { result } = renderHook(() => useOnCallIntegration(), { wrapper: wrapper() }); await waitFor(() => expect(result.current.isLoadingOnCallIntegration).toBe(false)); @@ -120,7 +121,7 @@ describe('useOnCallIntegration', () => { }); it('extendOnCalReceivers should not add new settings to the oncall receiver', async () => { - const { result } = renderHook(() => useOnCallIntegration(), { wrapper: TestProvider }); + const { result } = renderHook(() => useOnCallIntegration(), { wrapper: wrapper() }); await waitFor(() => expect(result.current.isLoadingOnCallIntegration).toBe(false)); @@ -147,7 +148,7 @@ describe('useOnCallIntegration', () => { }); it('extendConCallNotifierFeatures should not extend notifier', async () => { - const { result } = renderHook(() => useOnCallIntegration(), { wrapper: TestProvider }); + const { result } = renderHook(() => useOnCallIntegration(), { wrapper: wrapper() }); await waitFor(() => expect(result.current.isLoadingOnCallIntegration).toBe(false)); @@ -172,7 +173,7 @@ describe('useOnCallIntegration', () => { }); it('extendOnCalReceivers should not add new settings to the oncall receiver', async () => { - const { result } = renderHook(() => useOnCallIntegration(), { wrapper: TestProvider }); + const { result } = renderHook(() => useOnCallIntegration(), { wrapper: wrapper() }); await waitFor(() => expect(result.current.isLoadingOnCallIntegration).toBe(false)); @@ -199,7 +200,7 @@ describe('useOnCallIntegration', () => { }); it('extendConCallNotifierFeatures should not extend notifier', async () => { - const { result } = renderHook(() => useOnCallIntegration(), { wrapper: TestProvider }); + const { result } = renderHook(() => useOnCallIntegration(), { wrapper: wrapper() }); await waitFor(() => expect(result.current.isLoadingOnCallIntegration).toBe(false)); diff --git a/public/app/features/alerting/unified/components/rule-editor/DashboardPicker.test.tsx b/public/app/features/alerting/unified/components/rule-editor/DashboardPicker.test.tsx index 814fa6ee017..dcf8b06dd91 100644 --- a/public/app/features/alerting/unified/components/rule-editor/DashboardPicker.test.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/DashboardPicker.test.tsx @@ -1,11 +1,10 @@ -import { render } from '@testing-library/react'; import { noop } from 'lodash'; import { Props } from 'react-virtualized-auto-sizer'; +import { render } from 'test/test-utils'; import { byRole } from 'testing-library-selector'; import 'core-js/stable/structured-clone'; -import { TestProvider } from '../../../../../../test/helpers/TestProvider'; import { DashboardSearchItemType } from '../../../../search/types'; import { mockDashboardApi, setupMswServer } from '../../mockApi'; import { mockDashboardDto, mockDashboardSearchItem } from '../../mocks'; @@ -59,9 +58,7 @@ const ui = { describe('DashboardPicker', () => { it('Renders panels without ids', async () => { - render(, { - wrapper: TestProvider, - }); + render(); expect(await ui.dashboardButton(/Dashboard 1/).find()).toBeInTheDocument(); expect(ui.dashboardButton(/Dashboard 2/).get()).toBeInTheDocument(); diff --git a/public/app/features/alerting/unified/components/rule-editor/labels/LabelsField.test.tsx b/public/app/features/alerting/unified/components/rule-editor/labels/LabelsField.test.tsx index 8aa889e99e2..83fd5d378b1 100644 --- a/public/app/features/alerting/unified/components/rule-editor/labels/LabelsField.test.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/labels/LabelsField.test.tsx @@ -1,8 +1,6 @@ -import { render, screen, waitFor, within } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import * as React from 'react'; import { FormProvider, useForm } from 'react-hook-form'; -import { TestProvider } from 'test/helpers/TestProvider'; +import { render, screen, waitFor, within } from 'test/test-utils'; import { clearPluginSettingsCache } from 'app/features/plugins/pluginSettings'; @@ -27,20 +25,18 @@ const SubFormProviderWrapper = ({ children }: React.PropsWithChildren<{}>) => { }; function renderAlertLabels() { - render( + return render( - , - { wrapper: TestProvider } + ); } function renderLabelsWithSuggestions() { - render( + return render( - , - { wrapper: TestProvider } + ); } @@ -90,24 +86,24 @@ describe('LabelsField with suggestions', () => { }); it('Should delete a key-value combination', async () => { - renderLabelsWithSuggestions(); + const { user } = renderLabelsWithSuggestions(); await waitFor(() => expect(screen.getAllByTestId('alertlabel-key-picker')).toHaveLength(2)); expect(screen.getAllByTestId('alertlabel-key-picker')).toHaveLength(2); expect(screen.getAllByTestId('alertlabel-value-picker')).toHaveLength(2); - await userEvent.click(screen.getByTestId('delete-label-1')); + await user.click(screen.getByTestId('delete-label-1')); expect(screen.getAllByTestId('alertlabel-key-picker')).toHaveLength(1); expect(screen.getAllByTestId('alertlabel-value-picker')).toHaveLength(1); }); it('Should add new key-value dropdowns', async () => { - renderLabelsWithSuggestions(); + const { user } = renderLabelsWithSuggestions(); await waitFor(() => expect(screen.getByText('Add more')).toBeVisible()); - await userEvent.click(screen.getByText('Add more')); + await user.click(screen.getByText('Add more')); expect(screen.getAllByTestId('alertlabel-key-picker')).toHaveLength(3); @@ -123,22 +119,22 @@ describe('LabelsField with suggestions', () => { }); it('Should be able to write new keys and values using the dropdowns', async () => { - renderLabelsWithSuggestions(); + const { user } = renderLabelsWithSuggestions(); await waitFor(() => expect(screen.getByText('Add more')).toBeVisible()); - await userEvent.click(screen.getByText('Add more')); + await user.click(screen.getByText('Add more')); const LastKeyDropdown = within(screen.getByTestId('labelsInSubform-key-2')); const LastValueDropdown = within(screen.getByTestId('labelsInSubform-value-2')); - await userEvent.type(LastKeyDropdown.getByRole('combobox'), 'key3{enter}'); - await userEvent.type(LastValueDropdown.getByRole('combobox'), 'value3{enter}'); + await user.type(LastKeyDropdown.getByRole('combobox'), 'key3{enter}'); + await user.type(LastValueDropdown.getByRole('combobox'), 'value3{enter}'); expect(screen.getByTestId('labelsInSubform-key-2')).toHaveTextContent('key3'); expect(screen.getByTestId('labelsInSubform-value-2')).toHaveTextContent('value3'); }); it('Should be able to write new keys and values using the dropdowns, case sensitive', async () => { - renderLabelsWithSuggestions(); + const { user } = renderLabelsWithSuggestions(); await waitFor(() => expect(screen.getAllByTestId('alertlabel-key-picker')).toHaveLength(2)); expect(screen.getByTestId('labelsInSubform-key-0')).toHaveTextContent('key1'); @@ -149,11 +145,11 @@ describe('LabelsField with suggestions', () => { const LastKeyDropdown = within(screen.getByTestId('labelsInSubform-key-1')); const LastValueDropdown = within(screen.getByTestId('labelsInSubform-value-1')); - await userEvent.type(LastKeyDropdown.getByRole('combobox'), 'KEY2{enter}'); + await user.type(LastKeyDropdown.getByRole('combobox'), 'KEY2{enter}'); expect(screen.getByTestId('labelsInSubform-key-0')).toHaveTextContent('key1'); expect(screen.getByTestId('labelsInSubform-key-1')).toHaveTextContent('KEY2'); - await userEvent.type(LastValueDropdown.getByRole('combobox'), 'VALUE2{enter}'); + await user.type(LastValueDropdown.getByRole('combobox'), 'VALUE2{enter}'); expect(screen.getByTestId('labelsInSubform-value-0')).toHaveTextContent('value1'); expect(screen.getByTestId('labelsInSubform-value-1')).toHaveTextContent('VALUE2'); }); diff --git a/public/app/features/alerting/unified/components/rules/RulesFilter.test.tsx b/public/app/features/alerting/unified/components/rules/RulesFilter.test.tsx index 84d0dc752f5..b4d5205e574 100644 --- a/public/app/features/alerting/unified/components/rules/RulesFilter.test.tsx +++ b/public/app/features/alerting/unified/components/rules/RulesFilter.test.tsx @@ -1,6 +1,4 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { TestProvider } from 'test/helpers/TestProvider'; +import { render, screen } from 'test/test-utils'; import { byLabelText, byRole } from 'testing-library-selector'; import { locationService, setDataSourceSrv } from '@grafana/runtime'; @@ -47,9 +45,7 @@ beforeEach(() => { describe('RulesFilter', () => { it('Should apply state filter to the search input', async () => { - const user = userEvent.setup(); - - render(, { wrapper: TestProvider }); + const { user } = render(); await user.click(ui.stateFilter.firing.get()); @@ -57,9 +53,7 @@ describe('RulesFilter', () => { }); it('Should apply multiple UI-based filters to the search input', async () => { - const user = userEvent.setup(); - - render(, { wrapper: TestProvider }); + const { user } = render(); await user.click(ui.health.ok.get()); await user.click(ui.ruleType.alert.get()); @@ -69,9 +63,7 @@ describe('RulesFilter', () => { }); it('Should combine UI filters and typed expressions', async () => { - const user = userEvent.setup(); - - render(, { wrapper: TestProvider }); + const { user } = render(); await user.type(ui.searchInput.get(), 'cpu{Enter}'); await user.click(ui.health.ok.get()); @@ -83,11 +75,11 @@ describe('RulesFilter', () => { describe('Analytics', () => { it('Sends log info when clicking alert state filters', async () => { - render(, { wrapper: TestProvider }); + const { user } = render(); const button = screen.getByText('Pending'); - await userEvent.click(button); + await user.click(button); expect(analytics.logInfo).toHaveBeenCalledWith(analytics.LogMessages.clickingAlertStateFilters); }); diff --git a/public/app/features/alerting/unified/hooks/useAbilities.test.tsx b/public/app/features/alerting/unified/hooks/useAbilities.test.tsx index 9b24cabbffe..510e5390379 100644 --- a/public/app/features/alerting/unified/hooks/useAbilities.test.tsx +++ b/public/app/features/alerting/unified/hooks/useAbilities.test.tsx @@ -1,9 +1,5 @@ -import { renderHook, waitFor } from '@testing-library/react'; -import { createBrowserHistory } from 'history'; import { PropsWithChildren } from 'react'; -import { Router } from 'react-router-dom'; -import { TestProvider } from 'test/helpers/TestProvider'; -import { render, screen } from 'test/test-utils'; +import { render, screen, renderHook, waitFor, getWrapper } from 'test/test-utils'; import { setupMswServer } from 'app/features/alerting/unified/mockApi'; import { setFolderAccessControl } from 'app/features/alerting/unified/mocks/server/configure'; @@ -143,6 +139,8 @@ describe('alertmanager abilities', () => { setupMswServer(); +const wrapper = () => getWrapper({ renderWithRouter: true }); + /** * Render the hook result in a component so we can more reliably check that the result has settled * after API requests. Without this approach, the hook might return `[false, false]` whilst @@ -163,7 +161,7 @@ describe('AlertRule abilities', () => { it('should report that all actions are supported for a Grafana Managed alert rule', async () => { const rule = getGrafanaRule(); - const { result } = renderHook(() => useAllAlertRuleAbilities(rule), { wrapper: TestProvider }); + const { result } = renderHook(() => useAllAlertRuleAbilities(rule), { wrapper: wrapper() }); await waitFor(() => { const results = Object.values(result.current); @@ -201,7 +199,7 @@ describe('AlertRule abilities', () => { it('should report no permissions while we are loading data for cloud rule', async () => { const rule = getCloudRule(); - const { result } = renderHook(() => useAllAlertRuleAbilities(rule), { wrapper: TestProvider }); + const { result } = renderHook(() => useAllAlertRuleAbilities(rule), { wrapper: wrapper() }); await waitFor(() => { expect(result.current).not.toBeUndefined(); @@ -216,13 +214,14 @@ describe('AlertRule abilities', () => { }); function createAlertmanagerWrapper(alertmanagerSourceName: string) { - const wrapper = (props: PropsWithChildren) => ( - + const ProviderWrapper = getWrapper({ renderWithRouter: true }); + const wrapperComponent = (props: PropsWithChildren) => ( + {props.children} - + ); - return wrapper; + return wrapperComponent; } diff --git a/public/app/features/alerting/unified/hooks/useExternalAMSelector.test.tsx b/public/app/features/alerting/unified/hooks/useExternalAMSelector.test.tsx index a8f2c3224ff..85fc9d892cc 100644 --- a/public/app/features/alerting/unified/hooks/useExternalAMSelector.test.tsx +++ b/public/app/features/alerting/unified/hooks/useExternalAMSelector.test.tsx @@ -1,7 +1,7 @@ import { renderHook, waitFor } from '@testing-library/react'; import { http, HttpResponse } from 'msw'; import { SetupServer } from 'msw/node'; -import { TestProvider } from 'test/helpers/TestProvider'; +import { getWrapper } from 'test/test-utils'; import { DataSourceSettings } from '@grafana/data'; import { setupMswServer } from 'app/features/alerting/unified/mockApi'; @@ -13,6 +13,8 @@ import { normalizeDataSourceURL, useExternalDataSourceAlertmanagers } from './us const server = setupMswServer(); +const wrapper = () => getWrapper({ renderWithRouter: true }); + describe('useExternalDataSourceAlertmanagers', () => { it('Should get the correct data source settings', async () => { // Arrange @@ -20,7 +22,7 @@ describe('useExternalDataSourceAlertmanagers', () => { mockAlertmanagersResponse(server, { data: { activeAlertManagers: [], droppedAlertManagers: [] } }); // Act - const { result } = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper: TestProvider }); + const { result } = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper: wrapper() }); await waitFor(() => { // Assert expect(result.current).toHaveLength(1); @@ -35,7 +37,7 @@ describe('useExternalDataSourceAlertmanagers', () => { mockAlertmanagersResponse(server, { data: { activeAlertManagers: [], droppedAlertManagers: [] } }); // Act - const { result } = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper: TestProvider }); + const { result } = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper: wrapper() }); await waitFor(() => { // Assert expect(result.current).toHaveLength(1); @@ -54,7 +56,7 @@ describe('useExternalDataSourceAlertmanagers', () => { }); // Act - const { result } = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper: TestProvider }); + const { result } = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper: wrapper() }); await waitFor(() => { // Assert expect(result.current).toHaveLength(1); @@ -73,7 +75,7 @@ describe('useExternalDataSourceAlertmanagers', () => { }); // Act - const { result } = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper: TestProvider }); + const { result } = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper: wrapper() }); await waitFor(() => { // Assert @@ -93,7 +95,7 @@ describe('useExternalDataSourceAlertmanagers', () => { }); // Act - const { result } = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper: TestProvider }); + const { result } = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper: wrapper() }); await waitFor(() => { // Assert @@ -113,7 +115,7 @@ describe('useExternalDataSourceAlertmanagers', () => { }); // Act - const { result } = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper: TestProvider }); + const { result } = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper: wrapper() }); await waitFor(() => { // Assert @@ -136,7 +138,7 @@ describe('useExternalDataSourceAlertmanagers', () => { // Act const { result } = renderHook(() => useExternalDataSourceAlertmanagers(), { - wrapper: TestProvider, + wrapper: wrapper(), }); await waitFor(() => { @@ -159,7 +161,7 @@ describe('useExternalDataSourceAlertmanagers', () => { // Act const { result } = renderHook(() => useExternalDataSourceAlertmanagers(), { - wrapper: TestProvider, + wrapper: wrapper(), }); await waitFor(() => { diff --git a/public/test/helpers/alertingRuleEditor.tsx b/public/test/helpers/alertingRuleEditor.tsx index adf840775bd..b40cdb66d64 100644 --- a/public/test/helpers/alertingRuleEditor.tsx +++ b/public/test/helpers/alertingRuleEditor.tsx @@ -1,13 +1,10 @@ -import { render } from '@testing-library/react'; import { Route } from 'react-router-dom'; +import { render } from 'test/test-utils'; import { byRole, byTestId, byText } from 'testing-library-selector'; import { selectors } from '@grafana/e2e-selectors'; -import { locationService } from '@grafana/runtime'; import RuleEditor from 'app/features/alerting/unified/RuleEditor'; -import { TestProvider } from './TestProvider'; - export const ui = { loadingIndicator: byText('Loading rule...'), inputs: { @@ -38,15 +35,11 @@ export const ui = { }; export function renderRuleEditor(identifier?: string, recording = false) { - if (identifier) { - locationService.push(`/alerting/${identifier}/edit`); - } else { - locationService.push(`/alerting/new/${recording ? 'recording' : 'alerting'}`); - } - - return render( - - - - ); + return render(, { + historyOptions: { + initialEntries: [ + identifier ? `/alerting/${identifier}/edit` : `/alerting/new/${recording ? 'recording' : 'alerting'}`, + ], + }, + }); } diff --git a/public/test/test-utils.tsx b/public/test/test-utils.tsx index 8083093c7a4..dae68331554 100644 --- a/public/test/test-utils.tsx +++ b/public/test/test-utils.tsx @@ -8,7 +8,7 @@ import { Provider } from 'react-redux'; import { Router } from 'react-router-dom'; import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock'; -import { HistoryWrapper, setLocationService } from '@grafana/runtime'; +import { HistoryWrapper, LocationServiceProvider, setLocationService } from '@grafana/runtime'; import { GrafanaContext, GrafanaContextType } from 'app/core/context/GrafanaContext'; import { ModalsContextProvider } from 'app/core/context/ModalsContextProvider'; import { configureStore } from 'app/store/configureStore'; @@ -74,7 +74,9 @@ const getWrapper = ({ - {children} + + {children} + @@ -91,11 +93,14 @@ const customRender = ( ui: React.ReactElement, { renderWithRouter = true, ...renderOptions }: ExtendedRenderOptions = {} ) => { + const user = userEvent.setup(); const store = renderOptions.preloadedState ? configureStore(renderOptions?.preloadedState) : undefined; const AllTheProviders = renderOptions.wrapper || getWrapper({ store, renderWithRouter, ...renderOptions }); return { ...render(ui, { wrapper: AllTheProviders, ...renderOptions }), + /** Instance of `userEvent.setup()` ready for use to interact with rendered component */ + user, store, }; };