[Attack Discovery][Scheduling] Cases support followup 1 (#225452)

## Summary

Summarize your PR. If it involves visual changes include a screenshot or
gif.

These changes addresses the review comment from my previous PR
36ed6b38c1 (r2150081638)

Initially I broke up rendered component into multiple memoized
sub-sections. Reverting that back and adding tests coverage for the new
functionality - Case actions UI for the Attack Discovery rule type:
* Hidden `group by` component
* Hidden `time window` component
* Hidden `reopen case` component
* Disabled `template selector` component
* Tooltip explaining why we disabled the `template selector` component
This commit is contained in:
Ievgen Sorokopud 2025-06-26 21:31:35 +02:00 committed by GitHub
parent 4de6f7ca2c
commit d38801034a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 133 additions and 100 deletions

View file

@ -19,6 +19,7 @@ import { useGetAllCaseConfigurations } from '../../../containers/configure/use_g
import { useGetAllCaseConfigurationsResponse } from '../../configure_cases/__mock__'; import { useGetAllCaseConfigurationsResponse } from '../../configure_cases/__mock__';
import { templatesConfigurationMock } from '../../../containers/mock'; import { templatesConfigurationMock } from '../../../containers/mock';
import * as utils from '../../../containers/configure/utils'; import * as utils from '../../../containers/configure/utils';
import { ATTACK_DISCOVERY_SCHEDULES_ALERT_TYPE_ID } from '@kbn/elastic-assistant-common';
jest.mock('@kbn/alerts-ui-shared/src/common/hooks/use_alerts_data_view'); jest.mock('@kbn/alerts-ui-shared/src/common/hooks/use_alerts_data_view');
jest.mock('../../../common/lib/kibana/use_application'); jest.mock('../../../common/lib/kibana/use_application');
@ -72,6 +73,7 @@ describe('CasesParamsFields renders', () => {
// Workaround for timeout via https://github.com/testing-library/user-event/issues/833#issuecomment-1171452841 // Workaround for timeout via https://github.com/testing-library/user-event/issues/833#issuecomment-1171452841
user = userEvent.setup({ user = userEvent.setup({
advanceTimers: jest.advanceTimersByTime, advanceTimers: jest.advanceTimersByTime,
pointerEventsCheck: 0,
}); });
useApplicationMock.mockReturnValueOnce({ appId: 'management' }); useApplicationMock.mockReturnValueOnce({ appId: 'management' });
useAlertsDataViewMock.mockReturnValue({ useAlertsDataViewMock.mockReturnValue({
@ -397,4 +399,67 @@ describe('CasesParamsFields renders', () => {
expect(editAction.mock.calls[0][1].reopenClosedCases).toEqual(true); expect(editAction.mock.calls[0][1].reopenClosedCases).toEqual(true);
}); });
}); });
describe('Attack Discovery', () => {
it('does not render `group by` component', async () => {
const newProps = {
...defaultProps,
ruleTypeId: ATTACK_DISCOVERY_SCHEDULES_ALERT_TYPE_ID,
};
render(<CasesParamsFields {...newProps} />);
expect(screen.queryByTestId('group-by-alert-field-combobox')).not.toBeInTheDocument();
});
it('does not render `time window` component', async () => {
const newProps = {
...defaultProps,
ruleTypeId: ATTACK_DISCOVERY_SCHEDULES_ALERT_TYPE_ID,
};
render(<CasesParamsFields {...newProps} />);
expect(screen.queryByTestId('time-window-size-input')).not.toBeInTheDocument();
expect(screen.queryByTestId('time-window-unit-select')).not.toBeInTheDocument();
});
it('does not render `reopen case` component', async () => {
const newProps = {
...defaultProps,
ruleTypeId: ATTACK_DISCOVERY_SCHEDULES_ALERT_TYPE_ID,
};
render(<CasesParamsFields {...newProps} />);
expect(screen.queryByTestId('reopen-case')).not.toBeInTheDocument();
});
it('renders disabled `template selector` component', async () => {
const newProps = {
...defaultProps,
ruleTypeId: ATTACK_DISCOVERY_SCHEDULES_ALERT_TYPE_ID,
};
render(<CasesParamsFields {...newProps} />);
const templateSelectorComponent = await screen.findByTestId('create-case-template-select');
expect(templateSelectorComponent).toBeInTheDocument();
expect(templateSelectorComponent).toBeDisabled();
});
it('shows attack discovery explanation tooltip', async () => {
const newProps = {
...defaultProps,
ruleTypeId: ATTACK_DISCOVERY_SCHEDULES_ALERT_TYPE_ID,
};
render(<CasesParamsFields {...newProps} />);
await user.hover(await screen.findByTestId('create-case-template-select'));
expect(await screen.findByTestId('case-action-attack-discovery-tooltip')).toBeTruthy();
expect(
await screen.findByText(
'Attack Discovery Schedules fully manage Case actions, automatically filling in all fields for new Cases.'
)
).toBeInTheDocument();
});
});
}); });

View file

@ -188,8 +188,26 @@ export const CasesParamsFieldsComponent: React.FunctionComponent<
[editSubActionProperty] [editSubActionProperty]
); );
const groupByComponent = useMemo(() => { if (isAttackDiscoveryRuleType) {
return ( return (
<EuiToolTip
data-test-subj="case-action-attack-discovery-tooltip"
content={i18n.ATTACK_DISCOVERY_TEMPLATE_TOOLTIP}
>
<TemplateSelector
key={currentConfiguration.id}
isLoading={isLoadingCaseConfiguration}
templates={[defaultTemplate, ...currentConfiguration.templates]}
onTemplateChange={onTemplateChange}
initialTemplate={selectedTemplate}
isDisabled={true}
/>
</EuiToolTip>
);
}
return (
<>
<EuiFlexGroup> <EuiFlexGroup>
<EuiFlexItem grow={true}> <EuiFlexItem grow={true}>
<EuiFormRow fullWidth label={i18n.GROUP_BY_ALERT} labelAppend={OptionalFieldLabel}> <EuiFormRow fullWidth label={i18n.GROUP_BY_ALERT} labelAppend={OptionalFieldLabel}>
@ -207,12 +225,7 @@ export const CasesParamsFieldsComponent: React.FunctionComponent<
</EuiFormRow> </EuiFormRow>
</EuiFlexItem> </EuiFlexItem>
</EuiFlexGroup> </EuiFlexGroup>
); <EuiSpacer size="m" />
}, [loadingAlertDataViews, onChangeComboBox, options, selectedOptions]);
const timeWindowComponent = useMemo(() => {
return (
<>
<EuiFormRow <EuiFormRow
fullWidth fullWidth
id="timeWindow" id="timeWindow"
@ -259,19 +272,7 @@ export const CasesParamsFieldsComponent: React.FunctionComponent<
size="s" size="s"
/> />
)} )}
</> <EuiSpacer size="m" />
);
}, [
errors.timeWindow,
handleTimeWindowChange,
showTimeWindowWarning,
timeWindow,
timeWindowSize,
timeWindowUnit,
]);
const templateSelectorComponent = useMemo(() => {
return (
<EuiFlexGroup> <EuiFlexGroup>
<EuiFlexItem grow={true}> <EuiFlexItem grow={true}>
<TemplateSelector <TemplateSelector
@ -280,23 +281,10 @@ export const CasesParamsFieldsComponent: React.FunctionComponent<
templates={[defaultTemplate, ...currentConfiguration.templates]} templates={[defaultTemplate, ...currentConfiguration.templates]}
onTemplateChange={onTemplateChange} onTemplateChange={onTemplateChange}
initialTemplate={selectedTemplate} initialTemplate={selectedTemplate}
isDisabled={isAttackDiscoveryRuleType}
/> />
</EuiFlexItem> </EuiFlexItem>
</EuiFlexGroup> </EuiFlexGroup>
); <EuiSpacer size="m" />
}, [
currentConfiguration.id,
currentConfiguration.templates,
defaultTemplate,
isAttackDiscoveryRuleType,
isLoadingCaseConfiguration,
onTemplateChange,
selectedTemplate,
]);
const reopenClosedCasesComponent = useMemo(() => {
return (
<EuiFlexGroup> <EuiFlexGroup>
<EuiFlexItem> <EuiFlexItem>
<EuiCheckbox <EuiCheckbox
@ -310,26 +298,6 @@ export const CasesParamsFieldsComponent: React.FunctionComponent<
/> />
</EuiFlexItem> </EuiFlexItem>
</EuiFlexGroup> </EuiFlexGroup>
);
}, [editSubActionProperty, index, reopenClosedCases]);
if (isAttackDiscoveryRuleType) {
return (
<EuiToolTip content={i18n.ATTACK_DISCOVERY_TEMPLATE_TOOLTIP}>
{templateSelectorComponent}
</EuiToolTip>
);
}
return (
<>
{groupByComponent}
<EuiSpacer size="m" />
{timeWindowComponent}
<EuiSpacer size="m" />
{templateSelectorComponent}
<EuiSpacer size="m" />
{reopenClosedCasesComponent}
</> </>
); );
}; };