From d91e1a4e3af7b90247a964f0d518b25a05b25df1 Mon Sep 17 00:00:00 2001 From: Tom Ratcliffe Date: Mon, 8 Apr 2024 15:56:56 +0100 Subject: [PATCH] Refactor tab query param logic and add tests --- .../contact-points/ContactPoints.test.tsx | 72 ++++++++++++------- .../contact-points/ContactPoints.tsx | 25 +++++-- 2 files changed, 65 insertions(+), 32 deletions(-) diff --git a/public/app/features/alerting/unified/components/contact-points/ContactPoints.test.tsx b/public/app/features/alerting/unified/components/contact-points/ContactPoints.test.tsx index 6f24aa82073..626399aa627 100644 --- a/public/app/features/alerting/unified/components/contact-points/ContactPoints.test.tsx +++ b/public/app/features/alerting/unified/components/contact-points/ContactPoints.test.tsx @@ -1,7 +1,7 @@ import { render, screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { noop } from 'lodash'; -import React, { PropsWithChildren } from 'react'; +import React, { ComponentProps, PropsWithChildren } from 'react'; import { TestProvider } from 'test/helpers/TestProvider'; import { selectors } from '@grafana/e2e-selectors'; @@ -40,6 +40,18 @@ import setupVanillaAlertmanagerFlavoredServer, { */ const server = setupMswServer(); +const renderWithProvider = ( + providerProps?: Partial>, + contactPointProps?: Partial> +) => + render( + + + + + + ); + describe('contact points', () => { beforeEach(() => { // The location service is stateful between tests - `TestProvider` uses the same instance between each test @@ -58,13 +70,37 @@ describe('contact points', () => { setupGrafanaManagedServer(server); }); + describe('tabs behaviour', () => { + test('loads contact points tab', async () => { + locationService.push('/?tab=contact_points'); + renderWithProvider(); + + expect(await screen.findByText(/add contact point/i)).toBeInTheDocument(); + }); + + test('loads templates tab', async () => { + locationService.push('/?tab=templates'); + renderWithProvider(); + + expect(await screen.findByText(/add notification template/i)).toBeInTheDocument(); + }); + + test('defaults to contact points tab with invalid query param', async () => { + locationService.push('/?tab=foo_bar'); + renderWithProvider(); + + expect(await screen.findByText(/add contact point/i)).toBeInTheDocument(); + }); + + test('defaults to contact points tab with no query param', async () => { + renderWithProvider(); + + expect(await screen.findByText(/add contact point/i)).toBeInTheDocument(); + }); + }); + it('should show / hide loading states, have all actions enabled', async () => { - render( - - - , - { wrapper: TestProvider } - ); + renderWithProvider(); await waitFor(async () => { expect(screen.getByText('Loading...')).toBeInTheDocument(); @@ -103,12 +139,7 @@ describe('contact points', () => { it('should disable certain actions if the user has no write permissions', async () => { grantUserPermissions([AccessControlAction.AlertingNotificationsRead]); - render( - - - , - { wrapper: TestProvider } - ); + renderWithProvider(); // wait for loading to be done await waitFor(async () => { @@ -211,12 +242,7 @@ describe('contact points', () => { }); it('should be able to search', async () => { - render( - - - , - { wrapper: TestProvider } - ); + renderWithProvider(); const searchInput = screen.getByRole('textbox', { name: 'search contact points' }); await userEvent.type(searchInput, 'slack'); @@ -254,13 +280,7 @@ describe('contact points', () => { }); it('should show / hide loading states, have the right actions enabled', async () => { - render( - - - - - - ); + renderWithProvider({ alertmanagerSourceName: MIMIR_DATASOURCE_UID }); await waitFor(async () => { expect(screen.getByText('Loading...')).toBeInTheDocument(); diff --git a/public/app/features/alerting/unified/components/contact-points/ContactPoints.tsx b/public/app/features/alerting/unified/components/contact-points/ContactPoints.tsx index 1d696cbb7da..f87d4185cbf 100644 --- a/public/app/features/alerting/unified/components/contact-points/ContactPoints.tsx +++ b/public/app/features/alerting/unified/components/contact-points/ContactPoints.tsx @@ -25,8 +25,8 @@ import { Tooltip, useStyles2, } from '@grafana/ui'; -import { useQueryParams } from 'app/core/hooks/useQueryParams'; import ConditionalWrap from 'app/features/alerting/unified/components/ConditionalWrap'; +import { useURLSearchParams } from 'app/features/alerting/unified/hooks/useURLSearchParams'; import { receiverTypeNames } from 'app/plugins/datasource/alertmanager/consts'; import { GrafanaManagedReceiverConfig } from 'app/plugins/datasource/alertmanager/types'; import { GrafanaNotifierType, NotifierStatus } from 'app/types/alerting'; @@ -69,12 +69,25 @@ export enum ActiveTab { const DEFAULT_PAGE_SIZE = 10; +const useTabQueryParam = () => { + const [queryParams] = useURLSearchParams(); + return useMemo(() => { + const queryParam = queryParams.get('tab'); + + if (!queryParam || !Object.values(ActiveTab).map(String).includes(queryParam)) { + return ActiveTab.ContactPoints; + } + + return queryParam || ActiveTab.ContactPoints; + }, [queryParams]); +}; + const ContactPoints = () => { const { selectedAlertmanager } = useAlertmanager(); - const [queryParams, setQueryParams] = useQueryParams(); - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - const initialActiveTab = queryParams.tab as ActiveTab; - const [activeTab, setActiveTab] = useState(initialActiveTab || ActiveTab.ContactPoints); + const [queryParams, setQueryParams] = useURLSearchParams(); + const activeTabVal = useTabQueryParam(); + + const [activeTab, setActiveTab] = useState(activeTabVal); let { isLoading, error, contactPoints } = useContactPointsWithStatus(); const { deleteTrigger, updateAlertmanagerState } = useDeleteContactPoint(selectedAlertmanager!); const [addContactPointSupported, addContactPointAllowed] = useAlertmanagerAbility( @@ -90,7 +103,7 @@ const ContactPoints = () => { const [DeleteModal, showDeleteModal] = useDeleteContactPointModal(deleteTrigger, updateAlertmanagerState.isLoading); const [ExportDrawer, showExportDrawer] = useExportContactPoint(); - const search = String(queryParams.search || ''); + const search = queryParams.get('search'); const showingContactPoints = activeTab === ActiveTab.ContactPoints; const showNotificationTemplates = activeTab === ActiveTab.NotificationTemplates;