mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Hide edit/view rule buttons according to deleting/creating state (#90375)
This commit is contained in:
parent
dbc755925d
commit
7829fced94
@ -1,13 +1,8 @@
|
|||||||
import { render } from '@testing-library/react';
|
import { render, userEvent, screen } from 'test/test-utils';
|
||||||
import userEvent from '@testing-library/user-event';
|
|
||||||
import { Provider } from 'react-redux';
|
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
|
||||||
import { byRole } from 'testing-library-selector';
|
import { byRole } from 'testing-library-selector';
|
||||||
|
|
||||||
import { setPluginExtensionsHook } from '@grafana/runtime';
|
import { setPluginExtensionsHook } from '@grafana/runtime';
|
||||||
import { mockApi, setupMswServer } from 'app/features/alerting/unified/mockApi';
|
import { mockApi, setupMswServer } from 'app/features/alerting/unified/mockApi';
|
||||||
import { configureStore } from 'app/store/configureStore';
|
|
||||||
import { CombinedRule } from 'app/types/unified-alerting';
|
|
||||||
|
|
||||||
import { AlertRuleAction, useAlertRuleAbility } from '../../hooks/useAbilities';
|
import { AlertRuleAction, useAlertRuleAbility } from '../../hooks/useAbilities';
|
||||||
import { getCloudRule, getGrafanaRule, getMockPluginMeta } from '../../mocks';
|
import { getCloudRule, getGrafanaRule, getMockPluginMeta } from '../../mocks';
|
||||||
@ -36,18 +31,6 @@ const ui = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function renderRulesTable(rule: CombinedRule) {
|
|
||||||
const store = configureStore();
|
|
||||||
|
|
||||||
render(
|
|
||||||
<Provider store={store}>
|
|
||||||
<MemoryRouter>
|
|
||||||
<RulesTable rules={[rule]} />
|
|
||||||
</MemoryRouter>
|
|
||||||
</Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
const server = setupMswServer();
|
const server = setupMswServer();
|
||||||
|
|
||||||
@ -57,6 +40,7 @@ describe('RulesTable RBAC', () => {
|
|||||||
...getMockPluginMeta('grafana-incident-app', 'Grafana Incident'),
|
...getMockPluginMeta('grafana-incident-app', 'Grafana Incident'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Grafana rules action buttons', () => {
|
describe('Grafana rules action buttons', () => {
|
||||||
const grafanaRule = getGrafanaRule({ name: 'Grafana' });
|
const grafanaRule = getGrafanaRule({ name: 'Grafana' });
|
||||||
|
|
||||||
@ -64,7 +48,8 @@ describe('RulesTable RBAC', () => {
|
|||||||
mocks.useAlertRuleAbility.mockImplementation((_rule, action) => {
|
mocks.useAlertRuleAbility.mockImplementation((_rule, action) => {
|
||||||
return action === AlertRuleAction.Update ? [true, false] : [true, true];
|
return action === AlertRuleAction.Update ? [true, false] : [true, true];
|
||||||
});
|
});
|
||||||
renderRulesTable(grafanaRule);
|
|
||||||
|
render(<RulesTable rules={[grafanaRule]} />);
|
||||||
|
|
||||||
expect(ui.actionButtons.edit.query()).not.toBeInTheDocument();
|
expect(ui.actionButtons.edit.query()).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
@ -74,7 +59,8 @@ describe('RulesTable RBAC', () => {
|
|||||||
return action === AlertRuleAction.Delete ? [true, false] : [true, true];
|
return action === AlertRuleAction.Delete ? [true, false] : [true, true];
|
||||||
});
|
});
|
||||||
|
|
||||||
renderRulesTable(grafanaRule);
|
render(<RulesTable rules={[grafanaRule]} />);
|
||||||
|
|
||||||
await user.click(ui.actionButtons.more.get());
|
await user.click(ui.actionButtons.more.get());
|
||||||
|
|
||||||
expect(ui.moreActionItems.delete.query()).not.toBeInTheDocument();
|
expect(ui.moreActionItems.delete.query()).not.toBeInTheDocument();
|
||||||
@ -84,7 +70,8 @@ describe('RulesTable RBAC', () => {
|
|||||||
mocks.useAlertRuleAbility.mockImplementation((_rule, action) => {
|
mocks.useAlertRuleAbility.mockImplementation((_rule, action) => {
|
||||||
return action === AlertRuleAction.Update ? [true, true] : [false, false];
|
return action === AlertRuleAction.Update ? [true, true] : [false, false];
|
||||||
});
|
});
|
||||||
renderRulesTable(grafanaRule);
|
render(<RulesTable rules={[grafanaRule]} />);
|
||||||
|
|
||||||
expect(ui.actionButtons.edit.get()).toBeInTheDocument();
|
expect(ui.actionButtons.edit.get()).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -93,12 +80,56 @@ describe('RulesTable RBAC', () => {
|
|||||||
return action === AlertRuleAction.Delete ? [true, true] : [false, false];
|
return action === AlertRuleAction.Delete ? [true, true] : [false, false];
|
||||||
});
|
});
|
||||||
|
|
||||||
renderRulesTable(grafanaRule);
|
render(<RulesTable rules={[grafanaRule]} />);
|
||||||
|
|
||||||
expect(ui.actionButtons.more.get()).toBeInTheDocument();
|
expect(ui.actionButtons.more.get()).toBeInTheDocument();
|
||||||
await user.click(ui.actionButtons.more.get());
|
await user.click(ui.actionButtons.more.get());
|
||||||
expect(ui.moreActionItems.delete.get()).toBeInTheDocument();
|
expect(ui.moreActionItems.delete.get()).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('rules in creating/deleting states', () => {
|
||||||
|
const { promRule, ...creatingRule } = grafanaRule;
|
||||||
|
const { rulerRule, ...deletingRule } = grafanaRule;
|
||||||
|
const rulesSource = 'grafana';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preloaded state that implies the rulerRules have finished loading
|
||||||
|
*
|
||||||
|
* @todo Remove this state and test at a higher level to avoid mocking the store.
|
||||||
|
* We need to manually populate this, as the component hierarchy expects that we will
|
||||||
|
* have already called the necessary APIs to get the rulerRules data
|
||||||
|
*/
|
||||||
|
const preloadedState = {
|
||||||
|
unifiedAlerting: { rulerRules: { [rulesSource]: { result: {}, loading: false, dispatched: true } } },
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mocks.useAlertRuleAbility.mockImplementation(() => {
|
||||||
|
return [true, true];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not render View button when rule is creating', async () => {
|
||||||
|
render(<RulesTable rules={[creatingRule]} />, {
|
||||||
|
// @ts-ignore
|
||||||
|
preloadedState,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await screen.findByText('Creating')).toBeInTheDocument();
|
||||||
|
expect(ui.actionButtons.view.query()).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not render View or Edit button when rule is deleting', async () => {
|
||||||
|
render(<RulesTable rules={[deletingRule]} />, {
|
||||||
|
// @ts-ignore
|
||||||
|
preloadedState,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await screen.findByText('Deleting')).toBeInTheDocument();
|
||||||
|
expect(ui.actionButtons.view.query()).not.toBeInTheDocument();
|
||||||
|
expect(ui.actionButtons.edit.query()).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Cloud rules action buttons', () => {
|
describe('Cloud rules action buttons', () => {
|
||||||
@ -109,7 +140,8 @@ describe('RulesTable RBAC', () => {
|
|||||||
return action === AlertRuleAction.Update ? [true, false] : [true, true];
|
return action === AlertRuleAction.Update ? [true, false] : [true, true];
|
||||||
});
|
});
|
||||||
|
|
||||||
renderRulesTable(cloudRule);
|
render(<RulesTable rules={[cloudRule]} />);
|
||||||
|
|
||||||
expect(ui.actionButtons.edit.query()).not.toBeInTheDocument();
|
expect(ui.actionButtons.edit.query()).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -118,7 +150,8 @@ describe('RulesTable RBAC', () => {
|
|||||||
return action === AlertRuleAction.Delete ? [true, false] : [true, true];
|
return action === AlertRuleAction.Delete ? [true, false] : [true, true];
|
||||||
});
|
});
|
||||||
|
|
||||||
renderRulesTable(cloudRule);
|
render(<RulesTable rules={[cloudRule]} />);
|
||||||
|
|
||||||
await user.click(ui.actionButtons.more.get());
|
await user.click(ui.actionButtons.more.get());
|
||||||
expect(ui.moreActionItems.delete.query()).not.toBeInTheDocument();
|
expect(ui.moreActionItems.delete.query()).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
@ -128,7 +161,8 @@ describe('RulesTable RBAC', () => {
|
|||||||
return action === AlertRuleAction.Update ? [true, true] : [false, false];
|
return action === AlertRuleAction.Update ? [true, true] : [false, false];
|
||||||
});
|
});
|
||||||
|
|
||||||
renderRulesTable(cloudRule);
|
render(<RulesTable rules={[cloudRule]} />);
|
||||||
|
|
||||||
expect(ui.actionButtons.edit.get()).toBeInTheDocument();
|
expect(ui.actionButtons.edit.get()).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -137,7 +171,8 @@ describe('RulesTable RBAC', () => {
|
|||||||
return action === AlertRuleAction.Delete ? [true, true] : [false, false];
|
return action === AlertRuleAction.Delete ? [true, true] : [false, false];
|
||||||
});
|
});
|
||||||
|
|
||||||
renderRulesTable(cloudRule);
|
render(<RulesTable rules={[cloudRule]} />);
|
||||||
|
|
||||||
await user.click(ui.actionButtons.more.get());
|
await user.click(ui.actionButtons.more.get());
|
||||||
expect(ui.moreActionItems.delete.get()).toBeInTheDocument();
|
expect(ui.moreActionItems.delete.get()).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
@ -109,18 +109,25 @@ function useColumns(showSummaryColumn: boolean, showGroupColumn: boolean, showNe
|
|||||||
const { hasRuler, rulerRulesLoaded } = useHasRuler();
|
const { hasRuler, rulerRulesLoaded } = useHasRuler();
|
||||||
|
|
||||||
return useMemo((): RuleTableColumnProps[] => {
|
return useMemo((): RuleTableColumnProps[] => {
|
||||||
|
const ruleIsDeleting = (rule: CombinedRule) => {
|
||||||
|
const { namespace, promRule, rulerRule } = rule;
|
||||||
|
const { rulesSource } = namespace;
|
||||||
|
return Boolean(hasRuler(rulesSource) && rulerRulesLoaded(rulesSource) && promRule && !rulerRule);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ruleIsCreating = (rule: CombinedRule) => {
|
||||||
|
const { namespace, promRule, rulerRule } = rule;
|
||||||
|
const { rulesSource } = namespace;
|
||||||
|
return Boolean(hasRuler(rulesSource) && rulerRulesLoaded(rulesSource) && rulerRule && !promRule);
|
||||||
|
};
|
||||||
|
|
||||||
const columns: RuleTableColumnProps[] = [
|
const columns: RuleTableColumnProps[] = [
|
||||||
{
|
{
|
||||||
id: 'state',
|
id: 'state',
|
||||||
label: 'State',
|
label: 'State',
|
||||||
// eslint-disable-next-line react/display-name
|
|
||||||
renderCell: ({ data: rule }) => {
|
renderCell: ({ data: rule }) => {
|
||||||
const { namespace } = rule;
|
const isDeleting = ruleIsDeleting(rule);
|
||||||
const { rulesSource } = namespace;
|
const isCreating = ruleIsCreating(rule);
|
||||||
const { promRule, rulerRule } = rule;
|
|
||||||
|
|
||||||
const isDeleting = !!(hasRuler(rulesSource) && rulerRulesLoaded(rulesSource) && promRule && !rulerRule);
|
|
||||||
const isCreating = !!(hasRuler(rulesSource) && rulerRulesLoaded(rulesSource) && rulerRule && !promRule);
|
|
||||||
const isPaused = isGrafanaRulerRule(rule.rulerRule) && isGrafanaRulerRulePaused(rule.rulerRule);
|
const isPaused = isGrafanaRulerRule(rule.rulerRule) && isGrafanaRulerRulePaused(rule.rulerRule);
|
||||||
|
|
||||||
return <RuleState rule={rule} isDeleting={isDeleting} isCreating={isCreating} isPaused={isPaused} />;
|
return <RuleState rule={rule} isDeleting={isDeleting} isCreating={isCreating} isPaused={isPaused} />;
|
||||||
@ -226,7 +233,16 @@ function useColumns(showSummaryColumn: boolean, showGroupColumn: boolean, showNe
|
|||||||
label: 'Actions',
|
label: 'Actions',
|
||||||
// eslint-disable-next-line react/display-name
|
// eslint-disable-next-line react/display-name
|
||||||
renderCell: ({ data: rule }) => {
|
renderCell: ({ data: rule }) => {
|
||||||
return <RuleActionsButtons compact showViewButton rule={rule} rulesSource={rule.namespace.rulesSource} />;
|
const isDeleting = ruleIsDeleting(rule);
|
||||||
|
const isCreating = ruleIsCreating(rule);
|
||||||
|
return (
|
||||||
|
<RuleActionsButtons
|
||||||
|
compact
|
||||||
|
showViewButton={!isDeleting && !isCreating}
|
||||||
|
rule={rule}
|
||||||
|
rulesSource={rule.namespace.rulesSource}
|
||||||
|
/>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
size: '200px',
|
size: '200px',
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user