grafana/public/app/features/alerting/unified/hooks/useIsRuleEditable.test.tsx

202 lines
6.7 KiB
TypeScript
Raw Normal View History

Chore: Upgrade to react 18 (#64428) * update react 18 related deps * fix some types * make sure we're on react-router-dom >= 5.3.3 * Use new root API * Remove StrictMode for now - react 18 double rendering causes issues * fix + ignore some @grafana/ui types * fix some more types * use renderHook from @testing-library/react in almost all cases * fix storybook types * rewrite useDashboardSave to not use useEffect * make props optional * only render if props are provided * add correct type for useCallback * make resourcepicker tests more robust * fix ModalManager rendering * fix some more unit tests * store the click coordinates in a ref as setState is NOT synchronous * fix remaining e2e tests * rewrite dashboardpage tests to avoid act warnings * undo lint ignores * fix ExpanderCell types * set SymbolCell type correctly * fix QueryAndExpressionsStep * looks like the types were actually wrong instead :D * undo this for now... * remove spinner waits * more robust tests * rewrite errorboundary test to not explicitly count the number of renders * make urlParam expect async * increase timeout in waitFor * revert ExplorePage test changes * Update public/app/features/dashboard/containers/DashboardPage.test.tsx Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com> * Update public/app/features/dashboard/containers/PublicDashboardPage.test.tsx Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com> * Update public/app/features/dashboard/containers/PublicDashboardPage.test.tsx Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com> * Update public/app/features/dashboard/containers/PublicDashboardPage.test.tsx Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com> * skip fakeTimer test, ignore table types for now + other review comments * update package peerDeps * small tweak to resourcepicker test * update lockfile... * increase timeout in sharepublicdashboard tests * ensure ExplorePaneContainer passes correct queries to initializeExplore * fix LokiContextUI test * fix unit tests * make importDashboard flow more consistent * wait for dashboard name before continuing * more test fixes * readd dashboard name to variable e2e tests * wait for switches to be enabled before clicking * fix modal rendering * don't use @testing-library/dom directly * quick fix for rendering of panels in firefox * make PromQueryField test more robust * don't wait for chartData - in react 18 this can happen before the wait code even gets executed --------- Co-authored-by: kay delaney <kay@grafana.com> Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
2023-04-11 17:51:54 +08:00
import { renderHook } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { contextSrv } from 'app/core/services/context_srv';
import { AccessControlAction, FolderDTO, StoreState } from 'app/types';
import {
disableRBAC,
enableRBAC,
mockFolder,
mockRulerAlertingRule,
mockRulerGrafanaRule,
mockUnifiedAlertingStore,
} from '../mocks';
import { useFolder } from './useFolder';
import { useIsRuleEditable } from './useIsRuleEditable';
import { useUnifiedAlertingSelector } from './useUnifiedAlertingSelector';
jest.mock('./useFolder');
const mocks = {
useFolder: jest.mocked(useFolder),
useUnifiedAlertingSelector: jest.mocked(useUnifiedAlertingSelector),
};
describe('useIsRuleEditable', () => {
describe('RBAC enabled', () => {
beforeEach(enableRBAC);
describe('Grafana rules', () => {
// When RBAC is enabled we require appropriate alerting permissions in the folder scope
it('Should allow editing when the user has the alert rule update permission in the folder', () => {
mockUseFolder({
accessControl: {
[AccessControlAction.AlertingRuleUpdate]: true,
},
});
const wrapper = getProviderWrapper();
const { result } = renderHook(() => useIsRuleEditable('grafana', mockRulerGrafanaRule()), { wrapper });
expect(result.current.loading).toBe(false);
expect(result.current.isEditable).toBe(true);
});
it('Should allow deleting when the user has the alert rule delete permission', () => {
mockUseFolder({
accessControl: {
[AccessControlAction.AlertingRuleDelete]: true,
},
});
const wrapper = getProviderWrapper();
const { result } = renderHook(() => useIsRuleEditable('grafana', mockRulerGrafanaRule()), { wrapper });
expect(result.current.loading).toBe(false);
expect(result.current.isRemovable).toBe(true);
});
it('Should forbid editing when the user has no alert rule update permission', () => {
mockUseFolder({ accessControl: {} });
const wrapper = getProviderWrapper();
const { result } = renderHook(() => useIsRuleEditable('grafana', mockRulerGrafanaRule()), { wrapper });
expect(result.current.loading).toBe(false);
expect(result.current.isEditable).toBe(false);
});
it('Should forbid deleting when the user has no alert rule delete permission', () => {
mockUseFolder({ accessControl: {} });
const wrapper = getProviderWrapper();
const { result } = renderHook(() => useIsRuleEditable('grafana', mockRulerGrafanaRule()), { wrapper });
expect(result.current.loading).toBe(false);
expect(result.current.isRemovable).toBe(false);
});
it('Should allow editing and deleting when the user has alert rule permissions but does not have folder canSave permission', () => {
mockUseFolder({
canSave: false,
accessControl: {
[AccessControlAction.AlertingRuleUpdate]: true,
[AccessControlAction.AlertingRuleDelete]: true,
},
});
const wrapper = getProviderWrapper();
const { result } = renderHook(() => useIsRuleEditable('grafana', mockRulerGrafanaRule()), { wrapper });
expect(result.current.loading).toBe(false);
expect(result.current.isEditable).toBe(true);
expect(result.current.isRemovable).toBe(true);
});
});
describe('Cloud rules', () => {
beforeEach(() => {
contextSrv.isEditor = true;
});
it('Should allow editing and deleting when the user has alert rule external write permission', () => {
mockPermissions([AccessControlAction.AlertingRuleExternalWrite]);
const wrapper = getProviderWrapper();
const { result } = renderHook(() => useIsRuleEditable('cortex', mockRulerAlertingRule()), { wrapper });
expect(result.current.loading).toBe(false);
expect(result.current.isEditable).toBe(true);
expect(result.current.isRemovable).toBe(true);
});
it('Should forbid editing and deleting when the user has no alert rule external write permission', () => {
mockPermissions([]);
const wrapper = getProviderWrapper();
const { result } = renderHook(() => useIsRuleEditable('cortex', mockRulerAlertingRule()), { wrapper });
expect(result.current.loading).toBe(false);
expect(result.current.isEditable).toBe(false);
expect(result.current.isRemovable).toBe(false);
});
});
});
describe('RBAC disabled', () => {
beforeEach(disableRBAC);
describe('Grafana rules', () => {
it('Should allow editing and deleting when the user has folder canSave permission', () => {
mockUseFolder({ canSave: true });
const wrapper = getProviderWrapper();
const { result } = renderHook(() => useIsRuleEditable('grafana', mockRulerGrafanaRule()), { wrapper });
expect(result.current.loading).toBe(false);
expect(result.current.isEditable).toBe(true);
expect(result.current.isRemovable).toBe(true);
});
it('Should forbid editing and deleting when the user has no folder canSave permission', () => {
mockUseFolder({ canSave: false });
const wrapper = getProviderWrapper();
const { result } = renderHook(() => useIsRuleEditable('grafana', mockRulerGrafanaRule()), { wrapper });
expect(result.current.loading).toBe(false);
expect(result.current.isEditable).toBe(false);
expect(result.current.isRemovable).toBe(false);
});
});
});
});
function mockUseFolder(partial?: Partial<FolderDTO>) {
mocks.useFolder.mockReturnValue({ loading: false, folder: mockFolder(partial) });
}
function mockPermissions(grantedPermissions: AccessControlAction[]) {
jest
.spyOn(contextSrv, 'hasPermission')
.mockImplementation((action) => grantedPermissions.includes(action as AccessControlAction));
}
function getProviderWrapper() {
const dataSources = getMockedDataSources();
const store = mockUnifiedAlertingStore({ dataSources });
const wrapper = ({ children }: React.PropsWithChildren<{}>) => <Provider store={store}>{children}</Provider>;
return wrapper;
}
function getMockedDataSources(): StoreState['unifiedAlerting']['dataSources'] {
return {
grafana: {
loading: false,
dispatched: false,
result: {
id: 'grafana',
name: 'grafana',
rulerConfig: { dataSourceName: 'grafana', apiVersion: 'legacy' },
},
},
cortex: {
loading: false,
dispatched: false,
result: {
id: 'cortex',
name: 'Cortex',
rulerConfig: { dataSourceName: 'cortex', apiVersion: 'legacy' },
},
},
};
}