mirror of https://github.com/grafana/grafana
Alerting: Fix alert list V2 preview toggle (#105062)
* Use custom storage for previewing alerting features configuration * Fix tests, add localStorage mock * Use store singletonpull/105126/head
parent
53b4112abe
commit
620260fabc
@ -1,90 +1,81 @@ |
||||
import { setLocalStorageFeatureToggle } from './featureToggles'; |
||||
import { config } from '@grafana/runtime'; |
||||
|
||||
const featureTogglesKey = 'grafana.featureToggles'; |
||||
const storage = new Map<string, string>(); |
||||
import { shouldUseAlertingListViewV2 } from './featureToggles'; |
||||
import { mockLocalStorage } from './mocks'; |
||||
import { setPreviewToggle } from './previewToggles'; |
||||
|
||||
const mockLocalStorage = { |
||||
getItem: (key: string) => storage.get(key) ?? null, |
||||
setItem: (key: string, value: string) => storage.set(key, value), |
||||
clear: () => storage.clear(), |
||||
}; |
||||
const localStorageMock = mockLocalStorage(); |
||||
|
||||
Object.defineProperty(window, 'localStorage', { |
||||
value: mockLocalStorage, |
||||
value: localStorageMock, |
||||
writable: true, |
||||
}); |
||||
|
||||
describe('setLocalStorageFeatureToggle', () => { |
||||
describe('featureToggles', () => { |
||||
beforeEach(() => { |
||||
storage.clear(); |
||||
jest.clearAllMocks(); |
||||
localStorageMock.clear(); |
||||
config.featureToggles = {}; |
||||
}); |
||||
|
||||
it('should set feature toggle to true', () => { |
||||
setLocalStorageFeatureToggle('alertingListViewV2', true); |
||||
expect(storage.get(featureTogglesKey)).toBe('alertingListViewV2=true'); |
||||
}); |
||||
describe('shouldUseAlertingListViewV2', () => { |
||||
describe('when alertingListViewV2 toggle is disabled', () => { |
||||
beforeEach(() => { |
||||
config.featureToggles.alertingListViewV2 = false; |
||||
}); |
||||
|
||||
it('should set feature toggle to false', () => { |
||||
setLocalStorageFeatureToggle('alertingListViewV2', false); |
||||
expect(storage.get(featureTogglesKey)).toBe('alertingListViewV2=false'); |
||||
}); |
||||
it('should return false when preview toggle is disabled', () => { |
||||
config.featureToggles.alertingListViewV2PreviewToggle = false; |
||||
|
||||
it('should remove feature toggle when set to undefined', () => { |
||||
storage.set( |
||||
featureTogglesKey, |
||||
'alertingListViewV2=true,alertingPrometheusRulesPrimary=true,alertingCentralAlertHistory=true' |
||||
); |
||||
expect(shouldUseAlertingListViewV2()).toBe(false); |
||||
}); |
||||
|
||||
setLocalStorageFeatureToggle('alertingPrometheusRulesPrimary', undefined); |
||||
expect(storage.get(featureTogglesKey)).toBe('alertingListViewV2=true,alertingCentralAlertHistory=true'); |
||||
}); |
||||
it('should return true when preview toggle is enabled and preview value is true', () => { |
||||
config.featureToggles.alertingListViewV2PreviewToggle = true; |
||||
setPreviewToggle('alertingListViewV2', true); |
||||
|
||||
it('should not set undefined when no feature toggles are set', () => { |
||||
storage.set(featureTogglesKey, ''); |
||||
expect(shouldUseAlertingListViewV2()).toBe(true); |
||||
}); |
||||
|
||||
setLocalStorageFeatureToggle('alertingPrometheusRulesPrimary', undefined); |
||||
expect(storage.get(featureTogglesKey)).toBe(''); |
||||
}); |
||||
it('should return false when preview toggle is enabled but preview value is false', () => { |
||||
config.featureToggles.alertingListViewV2PreviewToggle = true; |
||||
setPreviewToggle('alertingListViewV2', false); |
||||
|
||||
it('should update only one feature toggle when multiple feature toggles are set', () => { |
||||
storage.set( |
||||
featureTogglesKey, |
||||
'alertingListViewV2=true,alertingPrometheusRulesPrimary=true,alertingCentralAlertHistory=true' |
||||
); |
||||
expect(shouldUseAlertingListViewV2()).toBe(false); |
||||
}); |
||||
}); |
||||
|
||||
setLocalStorageFeatureToggle('alertingPrometheusRulesPrimary', false); |
||||
expect(storage.get(featureTogglesKey)).toBe( |
||||
'alertingListViewV2=true,alertingPrometheusRulesPrimary=false,alertingCentralAlertHistory=true' |
||||
); |
||||
}); |
||||
describe('when alertingListViewV2 toggle is enabled', () => { |
||||
beforeEach(() => { |
||||
config.featureToggles.alertingListViewV2 = true; |
||||
}); |
||||
|
||||
it('should not rewrite other feature toggles when updating one', () => { |
||||
storage.set( |
||||
featureTogglesKey, |
||||
'alertingListViewV2=true,alertingPrometheusRulesPrimary=1,alertingCentralAlertHistory=false' |
||||
); |
||||
it('should return true when preview toggle is disabled', () => { |
||||
config.featureToggles.alertingListViewV2PreviewToggle = false; |
||||
|
||||
setLocalStorageFeatureToggle('alertingListViewV2', false); |
||||
expect(storage.get(featureTogglesKey)).toBe( |
||||
'alertingListViewV2=false,alertingPrometheusRulesPrimary=1,alertingCentralAlertHistory=false' |
||||
); |
||||
}); |
||||
expect(shouldUseAlertingListViewV2()).toBe(true); |
||||
}); |
||||
|
||||
it('should add a new toggle when others exist', () => { |
||||
storage.set(featureTogglesKey, 'alertingListViewV2=true'); |
||||
setLocalStorageFeatureToggle('alertingCentralAlertHistory', true); |
||||
expect(storage.get(featureTogglesKey)).toBe('alertingListViewV2=true,alertingCentralAlertHistory=true'); |
||||
}); |
||||
it('should return true when preview toggle is disabled even if preview value is true', () => { |
||||
config.featureToggles.alertingListViewV2PreviewToggle = false; |
||||
setPreviewToggle('alertingListViewV2', true); |
||||
|
||||
it('should remove the only existing toggle', () => { |
||||
storage.set(featureTogglesKey, 'alertingListViewV2=true'); |
||||
setLocalStorageFeatureToggle('alertingListViewV2', undefined); |
||||
expect(storage.get(featureTogglesKey)).toBe(''); |
||||
}); |
||||
expect(shouldUseAlertingListViewV2()).toBe(true); |
||||
}); |
||||
|
||||
it('should return false when preview toggle is enabled and preview value is false', () => { |
||||
config.featureToggles.alertingListViewV2PreviewToggle = true; |
||||
setPreviewToggle('alertingListViewV2', false); |
||||
|
||||
expect(shouldUseAlertingListViewV2()).toBe(false); |
||||
}); |
||||
|
||||
it('should return true when preview toggle is enabled and preview value is true', () => { |
||||
config.featureToggles.alertingListViewV2PreviewToggle = true; |
||||
setPreviewToggle('alertingListViewV2', true); |
||||
|
||||
it('should not change localStorage when attempting to remove a non-existent toggle', () => { |
||||
storage.set(featureTogglesKey, 'alertingListViewV2=true'); |
||||
setLocalStorageFeatureToggle('alertingCentralAlertHistory', undefined); |
||||
expect(storage.get(featureTogglesKey)).toBe('alertingListViewV2=true'); |
||||
expect(shouldUseAlertingListViewV2()).toBe(true); |
||||
}); |
||||
}); |
||||
}); |
||||
}); |
||||
|
@ -0,0 +1,50 @@ |
||||
import { mockLocalStorage } from './mocks'; |
||||
import { getPreviewToggle, setPreviewToggle } from './previewToggles'; |
||||
|
||||
const localStorageMock = mockLocalStorage(); |
||||
Object.defineProperty(window, 'localStorage', { |
||||
value: localStorageMock, |
||||
writable: true, |
||||
}); |
||||
|
||||
describe('previewToggles', () => { |
||||
beforeEach(() => { |
||||
localStorageMock.clear(); |
||||
}); |
||||
|
||||
describe('getPreviewToggle', () => { |
||||
it('should return undefined by default for a toggle', () => { |
||||
const result = getPreviewToggle('alertingListViewV2'); |
||||
expect(result).toBe(undefined); |
||||
}); |
||||
|
||||
it.each([true, false])('should return the stored value for a toggle: %s', (value) => { |
||||
// Set the toggle value first
|
||||
setPreviewToggle('alertingListViewV2', value); |
||||
|
||||
// Then verify it returns the correct value
|
||||
const result = getPreviewToggle('alertingListViewV2'); |
||||
expect(result).toBe(value); |
||||
}); |
||||
}); |
||||
|
||||
describe('setPreviewToggle', () => { |
||||
it('should set a toggle value', () => { |
||||
setPreviewToggle('alertingListViewV2', true); |
||||
|
||||
const result = getPreviewToggle('alertingListViewV2'); |
||||
expect(result).toBe(true); |
||||
}); |
||||
|
||||
it('should override previous toggle value', () => { |
||||
// Set initial value
|
||||
setPreviewToggle('alertingListViewV2', true); |
||||
|
||||
// Override with new value
|
||||
setPreviewToggle('alertingListViewV2', false); |
||||
|
||||
const result = getPreviewToggle('alertingListViewV2'); |
||||
expect(result).toBe(false); |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,22 @@ |
||||
import { FeatureToggles, store } from '@grafana/data'; |
||||
|
||||
type AlertingPreviewToggles = Pick<FeatureToggles, 'alertingListViewV2'>; |
||||
|
||||
const previewToggleStoreKey = 'grafana.alerting.previewToggles'; |
||||
|
||||
/** |
||||
* Get the preview toggle value for the given toggle name. |
||||
* @returns The value of the preview toggle or undefined if it is not set. |
||||
*/ |
||||
export function getPreviewToggle(previewToggleName: keyof AlertingPreviewToggles): boolean | undefined { |
||||
const previewToggles = store.getObject<AlertingPreviewToggles>(previewToggleStoreKey, {}); |
||||
|
||||
return previewToggles[previewToggleName]; |
||||
} |
||||
|
||||
export function setPreviewToggle(previewToggleName: keyof AlertingPreviewToggles, value: boolean | undefined) { |
||||
const previewToggles = store.getObject<AlertingPreviewToggles>(previewToggleStoreKey, {}); |
||||
|
||||
previewToggles[previewToggleName] = value; |
||||
store.setObject(previewToggleStoreKey, previewToggles); |
||||
} |
Loading…
Reference in new issue