From d1b9543189253a7bb18b36a6066c236bf78d5ddc Mon Sep 17 00:00:00 2001 From: Tom Ratcliffe Date: Tue, 21 Oct 2025 10:12:40 +0100 Subject: [PATCH] Chore: Move feature toggles test util to `test/test-utils` (#111173) --- .../RuleEditorGrafanaRecordingRules.test.tsx | 7 ++- .../import-to-gma/ImportToGMARules.test.tsx | 5 +- .../SimplifiedRuleEditor.test.tsx | 5 +- .../rules/deleted-rules/DeletedRules.test.tsx | 8 ++-- .../unified/rule-editor/formDefaults.test.ts | 5 +- .../unified/rule-list/RuleList.v2.test.tsx | 7 ++- .../rule-list/RuleListPageTitle.test.tsx | 7 ++- .../alerting/unified/test/test-utils.ts | 37 --------------- .../SubMenu/DashboardLinksDashboard.test.tsx | 5 +- .../plugins/panel/dashlist/DashList.test.tsx | 5 +- .../gettingstarted/GettingStarted.test.tsx | 5 +- public/test/test-utils.tsx | 46 +++++++++++++++++++ 12 files changed, 73 insertions(+), 69 deletions(-) diff --git a/public/app/features/alerting/unified/RuleEditorGrafanaRecordingRules.test.tsx b/public/app/features/alerting/unified/RuleEditorGrafanaRecordingRules.test.tsx index f026e8f3f46..c15f3713095 100644 --- a/public/app/features/alerting/unified/RuleEditorGrafanaRecordingRules.test.tsx +++ b/public/app/features/alerting/unified/RuleEditorGrafanaRecordingRules.test.tsx @@ -2,7 +2,7 @@ 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 } from 'test/test-utils'; +import { screen, testWithFeatureToggles } from 'test/test-utils'; import { byRole } from 'testing-library-selector'; import { FeatureToggles } from '@grafana/data'; @@ -15,7 +15,6 @@ import { grantUserPermissions, mockDataSource } from './mocks'; import { grafanaRulerGroup } from './mocks/grafanaRulerApi'; import { captureRequests, serializeRequests } from './mocks/server/events'; import { FOLDER_TITLE_HAPPY_PATH } from './mocks/server/handlers/search'; -import { testWithFeatureToggles } from './test/test-utils'; import { setupDataSources } from './testSetup/datasources'; import { setupPluginsExtensionsHook } from './testSetup/plugins'; @@ -72,7 +71,7 @@ describe('RuleEditor grafana recording rules', () => { }); const testCreateGrafanaRR = (featureToggles: Array, testName: string) => { - testWithFeatureToggles(featureToggles); + testWithFeatureToggles({ enable: featureToggles }); it(testName, async () => { const capture = captureRequests((r) => r.method === 'POST' && r.url.includes('/api/ruler/')); @@ -98,7 +97,7 @@ describe('RuleEditor grafana recording rules', () => { }; const testCreateGrafanaRRWithInvalidMetricName = (featureToggles: Array, testName: string) => { - testWithFeatureToggles(featureToggles); + testWithFeatureToggles({ enable: featureToggles }); it(testName, async () => { const capture = captureRequests((r) => r.method === 'POST' && r.url.includes('/api/ruler/')); diff --git a/public/app/features/alerting/unified/components/import-to-gma/ImportToGMARules.test.tsx b/public/app/features/alerting/unified/components/import-to-gma/ImportToGMARules.test.tsx index da2fbd63528..9185fb295c7 100644 --- a/public/app/features/alerting/unified/components/import-to-gma/ImportToGMARules.test.tsx +++ b/public/app/features/alerting/unified/components/import-to-gma/ImportToGMARules.test.tsx @@ -1,4 +1,4 @@ -import { render, waitFor } from 'test/test-utils'; +import { render, testWithFeatureToggles, waitFor } from 'test/test-utils'; import { byLabelText, byRole } from 'testing-library-selector'; import { setPluginComponentsHook, setPluginLinksHook } from '@grafana/runtime'; @@ -7,7 +7,6 @@ import { AccessControlAction } from 'app/types/accessControl'; import { setupMswServer } from '../../mockApi'; import { grantUserPermissions } from '../../mocks'; import { alertingFactory } from '../../mocks/server/db'; -import { testWithFeatureToggles } from '../../test/test-utils'; import ImportToGMARules from './ImportToGMARules'; @@ -49,7 +48,7 @@ alertingFactory.dataSource.mimir().build({ meta: { alerting: true } }); describe('ImportToGMARules', () => { grantUserPermissions([AccessControlAction.AlertingRuleExternalRead, AccessControlAction.AlertingRuleCreate]); - testWithFeatureToggles(['alertingImportYAMLUI', 'alertingMigrationUI']); + testWithFeatureToggles({ enable: ['alertingImportYAMLUI', 'alertingMigrationUI'] }); it('should render the import source options', () => { render(); diff --git a/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/SimplifiedRuleEditor.test.tsx b/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/SimplifiedRuleEditor.test.tsx index c5b364a20d9..e336d7852bf 100644 --- a/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/SimplifiedRuleEditor.test.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/SimplifiedRuleEditor.test.tsx @@ -2,7 +2,7 @@ import { UserEvent } from '@testing-library/user-event'; import { ReactNode } from 'react'; import { GrafanaRuleFormStep, renderRuleEditor, ui } from 'test/helpers/alertingRuleEditor'; import { clickSelectOption } from 'test/helpers/selectOptionInTest'; -import { screen, waitFor, within } from 'test/test-utils'; +import { screen, testWithFeatureToggles, waitFor, within } from 'test/test-utils'; import { byRole } from 'testing-library-selector'; import { setPluginLinksHook } from '@grafana/runtime'; @@ -13,7 +13,6 @@ import { setAlertmanagerChoices } from 'app/features/alerting/unified/mocks/serv import { PROMETHEUS_DATASOURCE_UID } from 'app/features/alerting/unified/mocks/server/constants'; import { captureRequests, serializeRequests } from 'app/features/alerting/unified/mocks/server/events'; import { FOLDER_TITLE_HAPPY_PATH } from 'app/features/alerting/unified/mocks/server/handlers/search'; -import { testWithFeatureToggles } from 'app/features/alerting/unified/test/test-utils'; import { setupDataSources } from 'app/features/alerting/unified/testSetup/datasources'; import { DataSourceType } from 'app/features/alerting/unified/utils/datasource'; import { MANUAL_ROUTING_KEY, SIMPLIFIED_QUERY_EDITOR_KEY } from 'app/features/alerting/unified/utils/rule-form'; @@ -171,7 +170,7 @@ describe('Can create a new grafana managed alert using simplified routing', () = }); describe('switch modes enabled', () => { - testWithFeatureToggles(['alertingQueryAndExpressionsStepMode', 'alertingNotificationsStepMode']); + testWithFeatureToggles({ enable: ['alertingQueryAndExpressionsStepMode', 'alertingNotificationsStepMode'] }); it('can create the new grafana-managed rule with default modes', async () => { const contactPointName = 'lotsa-emails'; diff --git a/public/app/features/alerting/unified/components/rules/deleted-rules/DeletedRules.test.tsx b/public/app/features/alerting/unified/components/rules/deleted-rules/DeletedRules.test.tsx index dd0d74c503a..0da93c8d8fe 100644 --- a/public/app/features/alerting/unified/components/rules/deleted-rules/DeletedRules.test.tsx +++ b/public/app/features/alerting/unified/components/rules/deleted-rules/DeletedRules.test.tsx @@ -1,5 +1,5 @@ import { produce } from 'immer'; -import { render, screen } from 'test/test-utils'; +import { render, screen, testWithFeatureToggles } from 'test/test-utils'; import { AppNotificationList } from 'app/core/components/AppNotifications/AppNotificationList'; import { DashboardSearchItemType } from 'app/features/search/types'; @@ -9,12 +9,14 @@ import { setupMswServer } from '../../../mockApi'; import { grantUserRole, mockFolder } from '../../../mocks'; import { grafanaRulerRule } from '../../../mocks/grafanaRulerApi'; import { setFolderResponse } from '../../../mocks/server/configure'; -import { grantPermissionsHelper, testWithFeatureToggles } from '../../../test/test-utils'; +import { grantPermissionsHelper } from '../../../test/test-utils'; import { DeletedRules } from './DeletedRules'; setupMswServer(); -testWithFeatureToggles(['alertingRulePermanentlyDelete', 'alertingRuleRecoverDeleted', 'alertRuleRestore']); +testWithFeatureToggles({ + enable: ['alertingRulePermanentlyDelete', 'alertingRuleRecoverDeleted', 'alertRuleRestore'], +}); beforeEach(() => { grantUserRole('Admin'); grantPermissionsHelper([ diff --git a/public/app/features/alerting/unified/rule-editor/formDefaults.test.ts b/public/app/features/alerting/unified/rule-editor/formDefaults.test.ts index 8cdff983960..9bfe9d4e593 100644 --- a/public/app/features/alerting/unified/rule-editor/formDefaults.test.ts +++ b/public/app/features/alerting/unified/rule-editor/formDefaults.test.ts @@ -1,7 +1,8 @@ +import { testWithFeatureToggles } from 'test/test-utils'; + import { config, getDataSourceSrv } from '@grafana/runtime'; import { mockAlertQuery, mockDataSource, mockReduceExpression, mockThresholdExpression } from '../mocks'; -import { testWithFeatureToggles } from '../test/test-utils'; import { RuleFormType } from '../types/rule-form'; import { Annotation } from '../utils/constants'; import { DataSourceType, getDefaultOrFirstCompatibleDataSource } from '../utils/datasource'; @@ -76,7 +77,7 @@ describe('formValuesFromQueryParams', () => { }); describe('when simplified query editor is enabled', () => { - testWithFeatureToggles(['alertingQueryAndExpressionsStepMode']); + testWithFeatureToggles({ enable: ['alertingQueryAndExpressionsStepMode'] }); it('should enable simplified query editor if queries are transformable to simple condition', () => { const result = formValuesFromQueryParams( diff --git a/public/app/features/alerting/unified/rule-list/RuleList.v2.test.tsx b/public/app/features/alerting/unified/rule-list/RuleList.v2.test.tsx index 33024dd123a..d4c445a2772 100644 --- a/public/app/features/alerting/unified/rule-list/RuleList.v2.test.tsx +++ b/public/app/features/alerting/unified/rule-list/RuleList.v2.test.tsx @@ -1,5 +1,5 @@ import { HttpResponse } from 'msw'; -import { render } from 'test/test-utils'; +import { render, testWithFeatureToggles } from 'test/test-utils'; import { byRole, byTestId } from 'testing-library-selector'; import { OrgRole } from '@grafana/data'; @@ -11,7 +11,6 @@ import { grantUserPermissions, grantUserRole } from '../mocks'; import { setGrafanaRuleGroupExportResolver } from '../mocks/server/configure'; import { alertingFactory } from '../mocks/server/db'; import { RulesFilter } from '../search/rulesSearchParser'; -import { testWithFeatureToggles } from '../test/test-utils'; import RuleList, { RuleListActions } from './RuleList.v2'; @@ -39,7 +38,7 @@ setPluginLinksHook(() => ({ links: [], isLoading: false })); setPluginComponentsHook(() => ({ components: [], isLoading: false })); grantUserPermissions([AccessControlAction.AlertingRuleExternalRead]); -testWithFeatureToggles(['alertingListViewV2']); +testWithFeatureToggles({ enable: ['alertingListViewV2'] }); setupMswServer(); @@ -253,7 +252,7 @@ describe('RuleListActions', () => { }); describe('Import Alert Rules', () => { - testWithFeatureToggles(['alertingMigrationUI']); + testWithFeatureToggles({ enable: ['alertingMigrationUI'] }); it('should show "Import alert rules" option when user has required permissions and feature toggle is enabled', async () => { grantUserPermissions([ diff --git a/public/app/features/alerting/unified/rule-list/RuleListPageTitle.test.tsx b/public/app/features/alerting/unified/rule-list/RuleListPageTitle.test.tsx index 7a1618e26b6..fb1d1618dc7 100644 --- a/public/app/features/alerting/unified/rule-list/RuleListPageTitle.test.tsx +++ b/public/app/features/alerting/unified/rule-list/RuleListPageTitle.test.tsx @@ -1,11 +1,10 @@ -import { render } from 'test/test-utils'; +import { render, testWithFeatureToggles } from 'test/test-utils'; import { byRole } from 'testing-library-selector'; import { reportInteraction } from '@grafana/runtime'; import { mockLocalStorage } from '../mocks'; import { getPreviewToggle, setPreviewToggle } from '../previewToggles'; -import { testWithFeatureToggles } from '../test/test-utils'; import { RuleListPageTitle } from './RuleListPageTitle'; @@ -56,7 +55,7 @@ describe('RuleListPageTitle', () => { }); describe('with alertingListViewV2PreviewToggle enabled and alertingListViewV2 disabled', () => { - testWithFeatureToggles(['alertingListViewV2PreviewToggle']); + testWithFeatureToggles({ enable: ['alertingListViewV2PreviewToggle'] }); it('should show enable v2 button', () => { renderRuleListPageTitle(); @@ -85,7 +84,7 @@ describe('RuleListPageTitle', () => { }); describe('with alertingListViewV2PreviewToggle enabled and alertingListViewV2 enabled', () => { - testWithFeatureToggles(['alertingListViewV2PreviewToggle', 'alertingListViewV2']); + testWithFeatureToggles({ enable: ['alertingListViewV2PreviewToggle', 'alertingListViewV2'] }); it('should show disable v2 button', () => { renderRuleListPageTitle(); diff --git a/public/app/features/alerting/unified/test/test-utils.ts b/public/app/features/alerting/unified/test/test-utils.ts index 0dea7f9eb58..0a5a6d458fe 100644 --- a/public/app/features/alerting/unified/test/test-utils.ts +++ b/public/app/features/alerting/unified/test/test-utils.ts @@ -1,7 +1,5 @@ import { act } from '@testing-library/react'; -import { FeatureToggles } from '@grafana/data'; -import { config } from '@grafana/runtime'; import { AccessControlAction } from 'app/types/accessControl'; import { grantUserPermissions } from '../mocks'; @@ -17,41 +15,6 @@ export const flushMicrotasks = async () => { }); }; -/** - * Enables feature toggles `beforeEach` test, and sets back to original settings `afterEach` test - */ -export const testWithFeatureToggles = (featureToggles: Array) => { - const originalToggles = { ...config.featureToggles }; - - beforeEach(() => { - featureToggles.forEach((featureToggle) => { - config.featureToggles[featureToggle] = true; - }); - }); - - afterEach(() => { - config.featureToggles = originalToggles; - }); -}; - -/** - * Enables license features `beforeEach` test, and sets back to original settings `afterEach` test - */ -export const testWithLicenseFeatures = (features: string[]) => { - const originalFeatures = { ...config.licenseInfo.enabledFeatures }; - beforeEach(() => { - config.licenseInfo.enabledFeatures = config.licenseInfo.enabledFeatures || {}; - - features.forEach((feature) => { - config.licenseInfo.enabledFeatures[feature] = true; - }); - }); - - afterEach(() => { - config.licenseInfo.enabledFeatures = originalFeatures; - }); -}; - /** * "Grants" permissions via contextSrv mock, and additionally sets folder access control * API response to match diff --git a/public/app/features/dashboard/components/SubMenu/DashboardLinksDashboard.test.tsx b/public/app/features/dashboard/components/SubMenu/DashboardLinksDashboard.test.tsx index 76485079afe..85144529c48 100644 --- a/public/app/features/dashboard/components/SubMenu/DashboardLinksDashboard.test.tsx +++ b/public/app/features/dashboard/components/SubMenu/DashboardLinksDashboard.test.tsx @@ -1,12 +1,11 @@ import { ComponentProps } from 'react'; -import { render, screen } from 'test/test-utils'; +import { render, screen, testWithFeatureToggles } from 'test/test-utils'; import { setBackendSrv } from '@grafana/runtime'; import { DashboardLink } from '@grafana/schema'; import { setupMockServer } from '@grafana/test-utils/server'; import { getFolderFixtures } from '@grafana/test-utils/unstable'; import { backendSrv } from 'app/core/services/backend_srv'; -import { testWithFeatureToggles } from 'app/features/alerting/unified/test/test-utils'; import { LinkSrv } from 'app/features/panel/panellinks/link_srv'; import { resetGrafanaSearcher } from 'app/features/search/service/searcher'; @@ -58,7 +57,7 @@ describe.each([ // Legacy APIs false, ])('with unifiedStorageSearchUI: %s', (featureTogglesEnabled) => { - testWithFeatureToggles(featureTogglesEnabled ? ['unifiedStorageSearchUI'] : []); + testWithFeatureToggles({ enable: featureTogglesEnabled ? ['unifiedStorageSearchUI'] : [] }); describe('DashboardLinksDashboard', () => { it('renders a dropdown', async () => { diff --git a/public/app/plugins/panel/dashlist/DashList.test.tsx b/public/app/plugins/panel/dashlist/DashList.test.tsx index d1febc53817..88f6032d3fc 100644 --- a/public/app/plugins/panel/dashlist/DashList.test.tsx +++ b/public/app/plugins/panel/dashlist/DashList.test.tsx @@ -1,11 +1,10 @@ -import { render, screen } from 'test/test-utils'; +import { render, screen, testWithFeatureToggles } from 'test/test-utils'; import { setBackendSrv } from '@grafana/runtime'; import { setupMockServer } from '@grafana/test-utils/server'; import { getFolderFixtures } from '@grafana/test-utils/unstable'; import { backendSrv } from 'app/core/services/backend_srv'; import impressionSrv from 'app/core/services/impression_srv'; -import { testWithFeatureToggles } from 'app/features/alerting/unified/test/test-utils'; import { getPanelProps } from '../test-utils'; @@ -39,7 +38,7 @@ describe.each([ // Legacy APIs false, ])('DashList - app platform APIs: %s', (featureTogglesEnabled) => { - testWithFeatureToggles(featureTogglesEnabled ? ['unifiedStorageSearchUI'] : []); + testWithFeatureToggles({ enable: featureTogglesEnabled ? ['unifiedStorageSearchUI'] : [] }); it('renders different groups of dashboards', async () => { const props = getPanelProps({ diff --git a/public/app/plugins/panel/gettingstarted/GettingStarted.test.tsx b/public/app/plugins/panel/gettingstarted/GettingStarted.test.tsx index 08c3eb8edce..6e29959fd41 100644 --- a/public/app/plugins/panel/gettingstarted/GettingStarted.test.tsx +++ b/public/app/plugins/panel/gettingstarted/GettingStarted.test.tsx @@ -1,10 +1,9 @@ -import { render, screen } from 'test/test-utils'; +import { render, screen, testWithFeatureToggles } from 'test/test-utils'; import { setBackendSrv } from '@grafana/runtime'; import { setupMockServer } from '@grafana/test-utils/server'; import { backendSrv } from 'app/core/services/backend_srv'; import { mockDataSource } from 'app/features/alerting/unified/mocks'; -import { testWithFeatureToggles } from 'app/features/alerting/unified/test/test-utils'; import { setupDataSources } from 'app/features/alerting/unified/testSetup/datasources'; import { getPanelProps } from '../test-utils'; @@ -23,7 +22,7 @@ describe.each([ // Legacy APIs false, ])('GettingStarted - app platform APIs: %s', (featureTogglesEnabled) => { - testWithFeatureToggles(featureTogglesEnabled ? ['unifiedStorageSearchUI'] : []); + testWithFeatureToggles({ enable: featureTogglesEnabled ? ['unifiedStorageSearchUI'] : [] }); it('renders getting started steps', async () => { const props = getPanelProps({}); diff --git a/public/test/test-utils.tsx b/public/test/test-utils.tsx index 5ed6982bc91..149055ac5df 100644 --- a/public/test/test-utils.tsx +++ b/public/test/test-utils.tsx @@ -10,7 +10,9 @@ import { Router } from 'react-router-dom'; import { CompatRouter } from 'react-router-dom-v5-compat'; import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock'; +import { FeatureToggles } from '@grafana/data'; import { + config, HistoryWrapper, LocationServiceProvider, setChromeHeaderHeightHook, @@ -121,5 +123,49 @@ const customRender = ( }; }; +/** + * Enables and disables feature toggles `beforeEach` test, and sets back to empty object `afterEach` test + */ +export const testWithFeatureToggles = ({ + enable, + disable, +}: { + enable?: Array; + disable?: Array; +}) => { + beforeEach(() => { + for (const featureToggle of enable || []) { + config.featureToggles[featureToggle] = true; + } + for (const featureToggle of disable || []) { + config.featureToggles[featureToggle] = false; + } + }); + + afterEach(() => { + config.featureToggles = {}; + }); +}; + +/** + * Enables license features `beforeEach` test, and sets back to empty object `afterEach` test + */ +export const testWithLicenseFeatures = ({ enable, disable }: { enable?: string[]; disable?: string[] }) => { + beforeEach(() => { + config.licenseInfo.enabledFeatures = config.licenseInfo.enabledFeatures || {}; + + for (const feature of enable || []) { + config.licenseInfo.enabledFeatures[feature] = true; + } + for (const feature of disable || []) { + config.licenseInfo.enabledFeatures[feature] = false; + } + }); + + afterEach(() => { + config.licenseInfo.enabledFeatures = {}; + }); +}; + export * from '@testing-library/react'; export { customRender as render, getWrapper, userEvent };