mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security Solution] Update button text according to status (#80389)
* update button text according to status * remove unused translations * fix functional test * fixup * fix unit test * update unit tests * update unit test
This commit is contained in:
parent
41db7d175c
commit
6b8e8a5b46
11 changed files with 493 additions and 125 deletions
|
@ -5,10 +5,12 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import { shallow, mount, ReactWrapper } from 'enzyme';
|
||||
|
||||
import '../../../../common/mock/match_media';
|
||||
import { PrePackagedRulesPrompt } from './load_empty_prompt';
|
||||
import { getPrePackagedRulesStatus } from '../../../containers/detection_engine/rules/api';
|
||||
|
||||
jest.mock('react-router-dom', () => {
|
||||
const original = jest.requireActual('react-router-dom');
|
||||
|
@ -23,16 +25,94 @@ jest.mock('react-router-dom', () => {
|
|||
|
||||
jest.mock('../../../../common/components/link_to');
|
||||
|
||||
jest.mock('../../../containers/detection_engine/rules/api', () => ({
|
||||
getPrePackagedRulesStatus: jest.fn().mockResolvedValue({
|
||||
rules_not_installed: 0,
|
||||
rules_installed: 0,
|
||||
rules_not_updated: 0,
|
||||
timelines_not_installed: 0,
|
||||
timelines_installed: 0,
|
||||
timelines_not_updated: 0,
|
||||
}),
|
||||
createPrepackagedRules: jest.fn(),
|
||||
}));
|
||||
|
||||
const props = {
|
||||
createPrePackagedRules: jest.fn(),
|
||||
loading: false,
|
||||
userHasNoPermissions: false,
|
||||
'data-test-subj': 'load-prebuilt-rules',
|
||||
};
|
||||
|
||||
describe('PrePackagedRulesPrompt', () => {
|
||||
it('renders correctly', () => {
|
||||
const wrapper = shallow(
|
||||
<PrePackagedRulesPrompt
|
||||
createPrePackagedRules={jest.fn()}
|
||||
loading={false}
|
||||
userHasNoPermissions={false}
|
||||
/>
|
||||
);
|
||||
const wrapper = shallow(<PrePackagedRulesPrompt {...props} />);
|
||||
|
||||
expect(wrapper.find('EmptyPrompt')).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('LoadPrebuiltRulesAndTemplatesButton', () => {
|
||||
it('renders correct button with correct text - Load Elastic prebuilt rules and timeline templates', async () => {
|
||||
(getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({
|
||||
rules_not_installed: 3,
|
||||
rules_installed: 0,
|
||||
rules_not_updated: 0,
|
||||
timelines_not_installed: 3,
|
||||
timelines_installed: 0,
|
||||
timelines_not_updated: 0,
|
||||
});
|
||||
|
||||
const wrapper: ReactWrapper = mount(<PrePackagedRulesPrompt {...props} />);
|
||||
await waitFor(() => {
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('[data-test-subj="load-prebuilt-rules"]').exists()).toEqual(true);
|
||||
expect(wrapper.find('[data-test-subj="load-prebuilt-rules"]').last().text()).toEqual(
|
||||
'Load Elastic prebuilt rules and timeline templates'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders correct button with correct text - Load Elastic prebuilt rules', async () => {
|
||||
(getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({
|
||||
rules_not_installed: 3,
|
||||
rules_installed: 0,
|
||||
rules_not_updated: 0,
|
||||
timelines_not_installed: 0,
|
||||
timelines_installed: 0,
|
||||
timelines_not_updated: 0,
|
||||
});
|
||||
|
||||
const wrapper: ReactWrapper = mount(<PrePackagedRulesPrompt {...props} />);
|
||||
await waitFor(() => {
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('[data-test-subj="load-prebuilt-rules"]').exists()).toEqual(true);
|
||||
expect(wrapper.find('[data-test-subj="load-prebuilt-rules"]').last().text()).toEqual(
|
||||
'Load Elastic prebuilt rules'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders correct button with correct text - Load Elastic prebuilt timeline templates', async () => {
|
||||
(getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({
|
||||
rules_not_installed: 0,
|
||||
rules_installed: 0,
|
||||
rules_not_updated: 0,
|
||||
timelines_not_installed: 3,
|
||||
timelines_installed: 0,
|
||||
timelines_not_updated: 0,
|
||||
});
|
||||
|
||||
const wrapper: ReactWrapper = mount(<PrePackagedRulesPrompt {...props} />);
|
||||
await waitFor(() => {
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('[data-test-subj="load-prebuilt-rules"]').exists()).toEqual(true);
|
||||
expect(wrapper.find('[data-test-subj="load-prebuilt-rules"]').last().text()).toEqual(
|
||||
'Load Elastic prebuilt timeline templates'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/eui';
|
||||
import React, { memo, useCallback } from 'react';
|
||||
import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import React, { memo, useCallback, useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
@ -14,6 +14,8 @@ import * as i18n from './translations';
|
|||
import { LinkButton } from '../../../../common/components/links';
|
||||
import { SecurityPageName } from '../../../../app/types';
|
||||
import { useFormatUrl } from '../../../../common/components/link_to';
|
||||
import { usePrePackagedRules } from '../../../containers/detection_engine/rules';
|
||||
import { useUserData } from '../../user_info';
|
||||
|
||||
const EmptyPrompt = styled(EuiEmptyPrompt)`
|
||||
align-self: center; /* Corrects horizontal centering in IE11 */
|
||||
|
@ -46,24 +48,36 @@ const PrePackagedRulesPromptComponent: React.FC<PrePackagedRulesPromptProps> = (
|
|||
[history]
|
||||
);
|
||||
|
||||
const [
|
||||
{ isSignalIndexExists, isAuthenticated, hasEncryptionKey, canUserCRUD, hasIndexWrite },
|
||||
] = useUserData();
|
||||
|
||||
const { getLoadPrebuiltRulesAndTemplatesButton } = usePrePackagedRules({
|
||||
canUserCRUD,
|
||||
hasIndexWrite,
|
||||
isSignalIndexExists,
|
||||
isAuthenticated,
|
||||
hasEncryptionKey,
|
||||
});
|
||||
|
||||
const loadPrebuiltRulesAndTemplatesButton = useMemo(
|
||||
() =>
|
||||
getLoadPrebuiltRulesAndTemplatesButton({
|
||||
isDisabled: userHasNoPermissions,
|
||||
onClick: handlePreBuiltCreation,
|
||||
fill: true,
|
||||
'data-test-subj': 'load-prebuilt-rules',
|
||||
}),
|
||||
[getLoadPrebuiltRulesAndTemplatesButton, handlePreBuiltCreation, userHasNoPermissions]
|
||||
);
|
||||
|
||||
return (
|
||||
<EmptyPrompt
|
||||
title={<h2>{i18n.PRE_BUILT_TITLE}</h2>}
|
||||
body={<p>{i18n.PRE_BUILT_MSG}</p>}
|
||||
actions={
|
||||
<EuiFlexGroup justifyContent="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
fill
|
||||
iconType="indexOpen"
|
||||
isDisabled={userHasNoPermissions}
|
||||
isLoading={loading}
|
||||
onClick={handlePreBuiltCreation}
|
||||
data-test-subj="load-prebuilt-rules"
|
||||
>
|
||||
{i18n.PRE_BUILT_ACTION}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>{loadPrebuiltRulesAndTemplatesButton}</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<LinkButton
|
||||
isDisabled={userHasNoPermissions}
|
||||
|
|
|
@ -21,13 +21,6 @@ export const PRE_BUILT_MSG = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const PRE_BUILT_ACTION = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.prePackagedRules.loadPreBuiltButton',
|
||||
{
|
||||
defaultMessage: 'Load prebuilt detection rules and timeline templates',
|
||||
}
|
||||
);
|
||||
|
||||
export const CREATE_RULE_ACTION = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.prePackagedRules.createOwnRuletButton',
|
||||
{
|
||||
|
|
|
@ -40,3 +40,57 @@ export const TAG_FETCH_FAILURE = i18n.translate(
|
|||
defaultMessage: 'Failed to fetch Tags',
|
||||
}
|
||||
);
|
||||
|
||||
export const LOAD_PREPACKAGED_RULES = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.loadPrePackagedRulesButton',
|
||||
{
|
||||
defaultMessage: 'Load Elastic prebuilt rules',
|
||||
}
|
||||
);
|
||||
|
||||
export const LOAD_PREPACKAGED_TIMELINE_TEMPLATES = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.loadPrePackagedTimelineTemplatesButton',
|
||||
{
|
||||
defaultMessage: 'Load Elastic prebuilt timeline templates',
|
||||
}
|
||||
);
|
||||
|
||||
export const LOAD_PREPACKAGED_RULES_AND_TEMPLATES = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.loadPrePackagedRulesAndTemplatesButton',
|
||||
{
|
||||
defaultMessage: 'Load Elastic prebuilt rules and timeline templates',
|
||||
}
|
||||
);
|
||||
|
||||
export const RELOAD_MISSING_PREPACKAGED_RULES = (missingRules: number) =>
|
||||
i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesButton',
|
||||
{
|
||||
values: { missingRules },
|
||||
defaultMessage:
|
||||
'Install {missingRules} Elastic prebuilt {missingRules, plural, =1 {rule} other {rules}} ',
|
||||
}
|
||||
);
|
||||
|
||||
export const RELOAD_MISSING_PREPACKAGED_TIMELINES = (missingTimelines: number) =>
|
||||
i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedTimelinesButton',
|
||||
{
|
||||
values: { missingTimelines },
|
||||
defaultMessage:
|
||||
'Install {missingTimelines} Elastic prebuilt {missingTimelines, plural, =1 {timeline} other {timelines}} ',
|
||||
}
|
||||
);
|
||||
|
||||
export const RELOAD_MISSING_PREPACKAGED_RULES_AND_TIMELINES = (
|
||||
missingRules: number,
|
||||
missingTimelines: number
|
||||
) =>
|
||||
i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesAndTimelinesButton',
|
||||
{
|
||||
values: { missingRules, missingTimelines },
|
||||
defaultMessage:
|
||||
'Install {missingRules} Elastic prebuilt {missingRules, plural, =1 {rule} other {rules}} and {missingTimelines} Elastic prebuilt {missingTimelines, plural, =1 {timeline} other {timelines}} ',
|
||||
}
|
||||
);
|
||||
|
|
|
@ -32,6 +32,10 @@ describe('usePrePackagedRules', () => {
|
|||
await waitForNextUpdate();
|
||||
|
||||
expect(result.current).toEqual({
|
||||
getLoadPrebuiltRulesAndTemplatesButton:
|
||||
result.current.getLoadPrebuiltRulesAndTemplatesButton,
|
||||
getReloadPrebuiltRulesAndTemplatesButton:
|
||||
result.current.getReloadPrebuiltRulesAndTemplatesButton,
|
||||
createPrePackagedRules: null,
|
||||
loading: true,
|
||||
loadingCreatePrePackagedRules: false,
|
||||
|
@ -63,6 +67,10 @@ describe('usePrePackagedRules', () => {
|
|||
await waitForNextUpdate();
|
||||
|
||||
expect(result.current).toEqual({
|
||||
getLoadPrebuiltRulesAndTemplatesButton:
|
||||
result.current.getLoadPrebuiltRulesAndTemplatesButton,
|
||||
getReloadPrebuiltRulesAndTemplatesButton:
|
||||
result.current.getReloadPrebuiltRulesAndTemplatesButton,
|
||||
createPrePackagedRules: result.current.createPrePackagedRules,
|
||||
loading: false,
|
||||
loadingCreatePrePackagedRules: false,
|
||||
|
@ -100,6 +108,10 @@ describe('usePrePackagedRules', () => {
|
|||
expect(resp).toEqual(true);
|
||||
expect(spyOnCreatePrepackagedRules).toHaveBeenCalled();
|
||||
expect(result.current).toEqual({
|
||||
getLoadPrebuiltRulesAndTemplatesButton:
|
||||
result.current.getLoadPrebuiltRulesAndTemplatesButton,
|
||||
getReloadPrebuiltRulesAndTemplatesButton:
|
||||
result.current.getReloadPrebuiltRulesAndTemplatesButton,
|
||||
createPrePackagedRules: result.current.createPrePackagedRules,
|
||||
loading: false,
|
||||
loadingCreatePrePackagedRules: false,
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useState, useEffect } from 'react';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
|
||||
import {
|
||||
errorToToaster,
|
||||
|
@ -14,6 +15,11 @@ import {
|
|||
import { getPrePackagedRulesStatus, createPrepackagedRules } from './api';
|
||||
import * as i18n from './translations';
|
||||
|
||||
import {
|
||||
getPrePackagedRuleStatus,
|
||||
getPrePackagedTimelineStatus,
|
||||
} from '../../../pages/detection_engine/rules/helpers';
|
||||
|
||||
type Func = () => void;
|
||||
export type CreatePreBuiltRules = () => Promise<boolean>;
|
||||
|
||||
|
@ -23,6 +29,23 @@ interface ReturnPrePackagedTimelines {
|
|||
timelinesNotUpdated: number | null;
|
||||
}
|
||||
|
||||
type GetLoadPrebuiltRulesAndTemplatesButton = (args: {
|
||||
isDisabled: boolean;
|
||||
onClick: () => void;
|
||||
fill?: boolean;
|
||||
'data-test-subj'?: string;
|
||||
}) => React.ReactNode | null;
|
||||
|
||||
type GetReloadPrebuiltRulesAndTemplatesButton = ({
|
||||
isDisabled,
|
||||
onClick,
|
||||
fill,
|
||||
}: {
|
||||
isDisabled: boolean;
|
||||
onClick: () => void;
|
||||
fill?: boolean;
|
||||
}) => React.ReactNode | null;
|
||||
|
||||
interface ReturnPrePackagedRules {
|
||||
createPrePackagedRules: null | CreatePreBuiltRules;
|
||||
loading: boolean;
|
||||
|
@ -32,6 +55,8 @@ interface ReturnPrePackagedRules {
|
|||
rulesInstalled: number | null;
|
||||
rulesNotInstalled: number | null;
|
||||
rulesNotUpdated: number | null;
|
||||
getLoadPrebuiltRulesAndTemplatesButton: GetLoadPrebuiltRulesAndTemplatesButton;
|
||||
getReloadPrebuiltRulesAndTemplatesButton: GetReloadPrebuiltRulesAndTemplatesButton;
|
||||
}
|
||||
|
||||
export type ReturnPrePackagedRulesAndTimelines = ReturnPrePackagedRules &
|
||||
|
@ -89,7 +114,6 @@ export const usePrePackagedRules = ({
|
|||
const [loadingCreatePrePackagedRules, setLoadingCreatePrePackagedRules] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [, dispatchToaster] = useStateToaster();
|
||||
|
||||
useEffect(() => {
|
||||
let isSubscribed = true;
|
||||
const abortCtrl = new AbortController();
|
||||
|
@ -100,7 +124,6 @@ export const usePrePackagedRules = ({
|
|||
const prePackagedRuleStatusResponse = await getPrePackagedRulesStatus({
|
||||
signal: abortCtrl.signal,
|
||||
});
|
||||
|
||||
if (isSubscribed) {
|
||||
setPrepackagedDataStatus({
|
||||
createPrePackagedRules: createElasticRules,
|
||||
|
@ -225,9 +248,108 @@ export const usePrePackagedRules = ({
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [canUserCRUD, hasIndexWrite, isAuthenticated, hasEncryptionKey, isSignalIndexExists]);
|
||||
|
||||
const prePackagedRuleStatus = useMemo(
|
||||
() =>
|
||||
getPrePackagedRuleStatus(
|
||||
prepackagedDataStatus.rulesInstalled,
|
||||
prepackagedDataStatus.rulesNotInstalled,
|
||||
prepackagedDataStatus.rulesNotUpdated
|
||||
),
|
||||
[
|
||||
prepackagedDataStatus.rulesInstalled,
|
||||
prepackagedDataStatus.rulesNotInstalled,
|
||||
prepackagedDataStatus.rulesNotUpdated,
|
||||
]
|
||||
);
|
||||
|
||||
const prePackagedTimelineStatus = useMemo(
|
||||
() =>
|
||||
getPrePackagedTimelineStatus(
|
||||
prepackagedDataStatus.timelinesInstalled,
|
||||
prepackagedDataStatus.timelinesNotInstalled,
|
||||
prepackagedDataStatus.timelinesNotUpdated
|
||||
),
|
||||
[
|
||||
prepackagedDataStatus.timelinesInstalled,
|
||||
prepackagedDataStatus.timelinesNotInstalled,
|
||||
prepackagedDataStatus.timelinesNotUpdated,
|
||||
]
|
||||
);
|
||||
const getLoadPrebuiltRulesAndTemplatesButton = useCallback(
|
||||
({ isDisabled, onClick, fill, 'data-test-subj': dataTestSubj = 'loadPrebuiltRulesBtn' }) => {
|
||||
return prePackagedRuleStatus === 'ruleNotInstalled' ||
|
||||
prePackagedTimelineStatus === 'timelinesNotInstalled' ? (
|
||||
<EuiButton
|
||||
fill={fill}
|
||||
iconType="indexOpen"
|
||||
isLoading={loadingCreatePrePackagedRules}
|
||||
isDisabled={isDisabled}
|
||||
onClick={onClick}
|
||||
data-test-subj={dataTestSubj}
|
||||
>
|
||||
{prePackagedRuleStatus === 'ruleNotInstalled' &&
|
||||
prePackagedTimelineStatus === 'timelinesNotInstalled' &&
|
||||
i18n.LOAD_PREPACKAGED_RULES_AND_TEMPLATES}
|
||||
|
||||
{prePackagedRuleStatus === 'ruleNotInstalled' &&
|
||||
prePackagedTimelineStatus !== 'timelinesNotInstalled' &&
|
||||
i18n.LOAD_PREPACKAGED_RULES}
|
||||
|
||||
{prePackagedRuleStatus !== 'ruleNotInstalled' &&
|
||||
prePackagedTimelineStatus === 'timelinesNotInstalled' &&
|
||||
i18n.LOAD_PREPACKAGED_TIMELINE_TEMPLATES}
|
||||
</EuiButton>
|
||||
) : null;
|
||||
},
|
||||
[loadingCreatePrePackagedRules, prePackagedRuleStatus, prePackagedTimelineStatus]
|
||||
);
|
||||
|
||||
const getMissingRulesOrTimelinesButtonTitle = useCallback(
|
||||
(missingRules: number, missingTimelines: number) => {
|
||||
if (missingRules > 0 && missingTimelines === 0)
|
||||
return i18n.RELOAD_MISSING_PREPACKAGED_RULES(missingRules);
|
||||
else if (missingRules === 0 && missingTimelines > 0)
|
||||
return i18n.RELOAD_MISSING_PREPACKAGED_TIMELINES(missingTimelines);
|
||||
else if (missingRules > 0 && missingTimelines > 0)
|
||||
return i18n.RELOAD_MISSING_PREPACKAGED_RULES_AND_TIMELINES(missingRules, missingTimelines);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const getReloadPrebuiltRulesAndTemplatesButton = useCallback(
|
||||
({ isDisabled, onClick, fill = false }) => {
|
||||
return prePackagedRuleStatus === 'someRuleUninstall' ||
|
||||
prePackagedTimelineStatus === 'someTimelineUninstall' ? (
|
||||
<EuiButton
|
||||
fill={fill}
|
||||
iconType="plusInCircle"
|
||||
isLoading={loadingCreatePrePackagedRules}
|
||||
isDisabled={isDisabled}
|
||||
onClick={onClick}
|
||||
data-test-subj="reloadPrebuiltRulesBtn"
|
||||
>
|
||||
{getMissingRulesOrTimelinesButtonTitle(
|
||||
prepackagedDataStatus.rulesNotInstalled ?? 0,
|
||||
prepackagedDataStatus.timelinesNotInstalled ?? 0
|
||||
)}
|
||||
</EuiButton>
|
||||
) : null;
|
||||
},
|
||||
[
|
||||
getMissingRulesOrTimelinesButtonTitle,
|
||||
loadingCreatePrePackagedRules,
|
||||
prePackagedRuleStatus,
|
||||
prePackagedTimelineStatus,
|
||||
prepackagedDataStatus.rulesNotInstalled,
|
||||
prepackagedDataStatus.timelinesNotInstalled,
|
||||
]
|
||||
);
|
||||
|
||||
return {
|
||||
loading,
|
||||
loadingCreatePrePackagedRules,
|
||||
...prepackagedDataStatus,
|
||||
getLoadPrebuiltRulesAndTemplatesButton,
|
||||
getReloadPrebuiltRulesAndTemplatesButton,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,13 +5,14 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { shallow, mount, ReactWrapper } from 'enzyme';
|
||||
|
||||
import '../../../../common/mock/match_media';
|
||||
import { RulesPage } from './index';
|
||||
import { useUserData } from '../../../components/user_info';
|
||||
import { usePrePackagedRules } from '../../../containers/detection_engine/rules';
|
||||
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import { TestProviders } from '../../../../common/mock';
|
||||
import { getPrePackagedRulesStatus } from '../../../containers/detection_engine/rules/api';
|
||||
jest.mock('react-router-dom', () => {
|
||||
const original = jest.requireActual('react-router-dom');
|
||||
|
||||
|
@ -26,16 +27,164 @@ jest.mock('react-router-dom', () => {
|
|||
jest.mock('../../../containers/detection_engine/lists/use_lists_config');
|
||||
jest.mock('../../../../common/components/link_to');
|
||||
jest.mock('../../../components/user_info');
|
||||
jest.mock('../../../containers/detection_engine/rules');
|
||||
jest.mock('../../../../common/components/toasters', () => {
|
||||
const actual = jest.requireActual('../../../../common/components/toasters');
|
||||
return {
|
||||
...actual,
|
||||
errorToToaster: jest.fn(),
|
||||
useStateToaster: jest.fn().mockReturnValue([jest.fn(), jest.fn()]),
|
||||
displaySuccessToast: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../containers/detection_engine/rules/api', () => ({
|
||||
getPrePackagedRulesStatus: jest.fn().mockResolvedValue({
|
||||
rules_not_installed: 0,
|
||||
rules_installed: 0,
|
||||
rules_not_updated: 0,
|
||||
timelines_not_installed: 0,
|
||||
timelines_installed: 0,
|
||||
timelines_not_updated: 0,
|
||||
}),
|
||||
createPrepackagedRules: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../common/lib/kibana', () => {
|
||||
return {
|
||||
useToast: jest.fn(),
|
||||
useHttp: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../components/value_lists_management_modal', () => {
|
||||
return {
|
||||
ValueListsModal: jest.fn().mockReturnValue(<div />),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('./all', () => {
|
||||
return {
|
||||
AllRules: jest.fn().mockReturnValue(<div />),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../../common/utils/route/spy_routes', () => {
|
||||
return {
|
||||
SpyRoute: jest.fn().mockReturnValue(<div />),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../components/rules/pre_packaged_rules/update_callout', () => {
|
||||
return {
|
||||
UpdatePrePackagedRulesCallOut: jest.fn().mockReturnValue(<div />),
|
||||
};
|
||||
});
|
||||
|
||||
describe('RulesPage', () => {
|
||||
beforeAll(() => {
|
||||
(useUserData as jest.Mock).mockReturnValue([{}]);
|
||||
(usePrePackagedRules as jest.Mock).mockReturnValue({});
|
||||
});
|
||||
it('renders correctly', () => {
|
||||
const wrapper = shallow(<RulesPage />);
|
||||
|
||||
expect(wrapper.find('AllRules')).toHaveLength(1);
|
||||
it('renders AllRules', () => {
|
||||
const wrapper = shallow(<RulesPage />);
|
||||
expect(wrapper.find('[data-test-subj="all-rules"]').exists()).toEqual(true);
|
||||
});
|
||||
|
||||
it('renders correct button with correct text - Load Elastic prebuilt rules and timeline templates', async () => {
|
||||
(getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({
|
||||
rules_not_installed: 3,
|
||||
rules_installed: 0,
|
||||
rules_not_updated: 0,
|
||||
timelines_not_installed: 3,
|
||||
timelines_installed: 0,
|
||||
timelines_not_updated: 0,
|
||||
});
|
||||
|
||||
const wrapper: ReactWrapper = mount(
|
||||
<TestProviders>
|
||||
<RulesPage />
|
||||
</TestProviders>
|
||||
);
|
||||
await waitFor(() => {
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('[data-test-subj="loadPrebuiltRulesBtn"]').exists()).toEqual(true);
|
||||
expect(wrapper.find('[data-test-subj="loadPrebuiltRulesBtn"]').last().text()).toEqual(
|
||||
'Load Elastic prebuilt rules and timeline templates'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders correct button with correct text - Load Elastic prebuilt rules', async () => {
|
||||
(getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({
|
||||
rules_not_installed: 3,
|
||||
rules_installed: 0,
|
||||
rules_not_updated: 0,
|
||||
timelines_not_installed: 0,
|
||||
timelines_installed: 0,
|
||||
timelines_not_updated: 0,
|
||||
});
|
||||
|
||||
const wrapper: ReactWrapper = mount(
|
||||
<TestProviders>
|
||||
<RulesPage />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('[data-test-subj="loadPrebuiltRulesBtn"]').exists()).toEqual(true);
|
||||
expect(wrapper.find('[data-test-subj="loadPrebuiltRulesBtn"]').last().text()).toEqual(
|
||||
'Load Elastic prebuilt rules'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders correct button with correct text - Load Elastic prebuilt timeline templates', async () => {
|
||||
(getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({
|
||||
rules_not_installed: 0,
|
||||
rules_installed: 0,
|
||||
rules_not_updated: 0,
|
||||
timelines_not_installed: 3,
|
||||
timelines_installed: 0,
|
||||
timelines_not_updated: 0,
|
||||
});
|
||||
|
||||
const wrapper: ReactWrapper = mount(
|
||||
<TestProviders>
|
||||
<RulesPage />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
wrapper.update();
|
||||
expect(wrapper.find('[data-test-subj="loadPrebuiltRulesBtn"]').exists()).toEqual(true);
|
||||
expect(wrapper.find('[data-test-subj="loadPrebuiltRulesBtn"]').last().text()).toEqual(
|
||||
'Load Elastic prebuilt timeline templates'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders a callout - Update Elastic prebuilt rules', async () => {
|
||||
(getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({
|
||||
rules_not_installed: 2,
|
||||
rules_installed: 1,
|
||||
rules_not_updated: 1,
|
||||
timelines_not_installed: 0,
|
||||
timelines_installed: 0,
|
||||
timelines_not_updated: 0,
|
||||
});
|
||||
|
||||
const wrapper: ReactWrapper = mount(
|
||||
<TestProviders>
|
||||
<RulesPage />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
wrapper.update();
|
||||
expect(wrapper.find('[data-test-subj="update-callout-button"]').exists()).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { usePrePackagedRules, importRules } from '../../../containers/detection_engine/rules';
|
||||
|
@ -70,6 +70,8 @@ const RulesPageComponent: React.FC = () => {
|
|||
timelinesInstalled,
|
||||
timelinesNotInstalled,
|
||||
timelinesNotUpdated,
|
||||
getLoadPrebuiltRulesAndTemplatesButton,
|
||||
getReloadPrebuiltRulesAndTemplatesButton,
|
||||
} = usePrePackagedRules({
|
||||
canUserCRUD,
|
||||
hasIndexWrite,
|
||||
|
@ -113,18 +115,6 @@ const RulesPageComponent: React.FC = () => {
|
|||
refreshRulesData.current = refreshRule;
|
||||
}, []);
|
||||
|
||||
const getMissingRulesOrTimelinesButtonTitle = useCallback(
|
||||
(missingRules: number, missingTimelines: number) => {
|
||||
if (missingRules > 0 && missingTimelines === 0)
|
||||
return i18n.RELOAD_MISSING_PREPACKAGED_RULES(missingRules);
|
||||
else if (missingRules === 0 && missingTimelines > 0)
|
||||
return i18n.RELOAD_MISSING_PREPACKAGED_TIMELINES(missingTimelines);
|
||||
else if (missingRules > 0 && missingTimelines > 0)
|
||||
return i18n.RELOAD_MISSING_PREPACKAGED_RULES_AND_TIMELINES(missingRules, missingTimelines);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const goToNewRule = useCallback(
|
||||
(ev) => {
|
||||
ev.preventDefault();
|
||||
|
@ -133,6 +123,24 @@ const RulesPageComponent: React.FC = () => {
|
|||
[history]
|
||||
);
|
||||
|
||||
const loadPrebuiltRulesAndTemplatesButton = useMemo(
|
||||
() =>
|
||||
getLoadPrebuiltRulesAndTemplatesButton({
|
||||
isDisabled: userHasNoPermissions(canUserCRUD) || loading,
|
||||
onClick: handleCreatePrePackagedRules,
|
||||
}),
|
||||
[canUserCRUD, getLoadPrebuiltRulesAndTemplatesButton, handleCreatePrePackagedRules, loading]
|
||||
);
|
||||
|
||||
const reloadPrebuiltRulesAndTemplatesButton = useMemo(
|
||||
() =>
|
||||
getReloadPrebuiltRulesAndTemplatesButton({
|
||||
isDisabled: userHasNoPermissions(canUserCRUD) || loading,
|
||||
onClick: handleCreatePrePackagedRules,
|
||||
}),
|
||||
[canUserCRUD, getReloadPrebuiltRulesAndTemplatesButton, handleCreatePrePackagedRules, loading]
|
||||
);
|
||||
|
||||
if (
|
||||
redirectToDetections(
|
||||
isSignalIndexExists,
|
||||
|
@ -177,35 +185,11 @@ const RulesPageComponent: React.FC = () => {
|
|||
title={i18n.PAGE_TITLE}
|
||||
>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false} wrap={true}>
|
||||
{(prePackagedRuleStatus === 'ruleNotInstalled' ||
|
||||
prePackagedTimelineStatus === 'timelinesNotInstalled') && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
iconType="indexOpen"
|
||||
isLoading={loadingCreatePrePackagedRules}
|
||||
isDisabled={userHasNoPermissions(canUserCRUD) || loading}
|
||||
onClick={handleCreatePrePackagedRules}
|
||||
>
|
||||
{i18n.LOAD_PREPACKAGED_RULES}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
{loadPrebuiltRulesAndTemplatesButton && (
|
||||
<EuiFlexItem grow={false}>{loadPrebuiltRulesAndTemplatesButton}</EuiFlexItem>
|
||||
)}
|
||||
{(prePackagedRuleStatus === 'someRuleUninstall' ||
|
||||
prePackagedTimelineStatus === 'someTimelineUninstall') && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
data-test-subj="reloadPrebuiltRulesBtn"
|
||||
iconType="plusInCircle"
|
||||
isLoading={loadingCreatePrePackagedRules}
|
||||
isDisabled={userHasNoPermissions(canUserCRUD) || loading}
|
||||
onClick={handleCreatePrePackagedRules}
|
||||
>
|
||||
{getMissingRulesOrTimelinesButtonTitle(
|
||||
rulesNotInstalled ?? 0,
|
||||
timelinesNotInstalled ?? 0
|
||||
)}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
{reloadPrebuiltRulesAndTemplatesButton && (
|
||||
<EuiFlexItem grow={false}>{reloadPrebuiltRulesAndTemplatesButton}</EuiFlexItem>
|
||||
)}
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiToolTip position="top" content={i18n.UPLOAD_VALUE_LISTS_TOOLTIP}>
|
||||
|
@ -247,6 +231,7 @@ const RulesPageComponent: React.FC = () => {
|
|||
{(prePackagedRuleStatus === 'ruleNeedUpdate' ||
|
||||
prePackagedTimelineStatus === 'timelineNeedUpdate') && (
|
||||
<UpdatePrePackagedRulesCallOut
|
||||
data-test-subj="update-callout-button"
|
||||
loading={loadingCreatePrePackagedRules}
|
||||
numberOfUpdatedRules={rulesNotUpdated ?? 0}
|
||||
numberOfUpdatedTimelines={timelinesNotUpdated ?? 0}
|
||||
|
@ -255,6 +240,7 @@ const RulesPageComponent: React.FC = () => {
|
|||
)}
|
||||
<AllRules
|
||||
createPrePackagedRules={createPrePackagedRules}
|
||||
data-test-subj="all-rules"
|
||||
loading={loading || prePackagedRuleLoading}
|
||||
loadingCreatePrePackagedRules={loadingCreatePrePackagedRules}
|
||||
hasNoPermissions={userHasNoPermissions(canUserCRUD)}
|
||||
|
|
|
@ -494,46 +494,6 @@ export const DELETE = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const LOAD_PREPACKAGED_RULES = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.loadPrePackagedRulesButton',
|
||||
{
|
||||
defaultMessage: 'Load Elastic prebuilt rules and timeline templates',
|
||||
}
|
||||
);
|
||||
|
||||
export const RELOAD_MISSING_PREPACKAGED_RULES = (missingRules: number) =>
|
||||
i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesButton',
|
||||
{
|
||||
values: { missingRules },
|
||||
defaultMessage:
|
||||
'Install {missingRules} Elastic prebuilt {missingRules, plural, =1 {rule} other {rules}} ',
|
||||
}
|
||||
);
|
||||
|
||||
export const RELOAD_MISSING_PREPACKAGED_TIMELINES = (missingTimelines: number) =>
|
||||
i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedTimelinesButton',
|
||||
{
|
||||
values: { missingTimelines },
|
||||
defaultMessage:
|
||||
'Install {missingTimelines} Elastic prebuilt {missingTimelines, plural, =1 {timeline} other {timelines}} ',
|
||||
}
|
||||
);
|
||||
|
||||
export const RELOAD_MISSING_PREPACKAGED_RULES_AND_TIMELINES = (
|
||||
missingRules: number,
|
||||
missingTimelines: number
|
||||
) =>
|
||||
i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesAndTimelinesButton',
|
||||
{
|
||||
values: { missingRules, missingTimelines },
|
||||
defaultMessage:
|
||||
'Install {missingRules} Elastic prebuilt {missingRules, plural, =1 {rule} other {rules}} and {missingTimelines} Elastic prebuilt {missingTimelines, plural, =1 {timeline} other {timelines}} ',
|
||||
}
|
||||
);
|
||||
|
||||
export const IMPORT_RULE_BTN_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.components.importRuleModal.importRuleTitle',
|
||||
{
|
||||
|
|
|
@ -15574,13 +15574,12 @@
|
|||
"xpack.securitySolution.detectionEngine.rules.deleteDescription": "削除",
|
||||
"xpack.securitySolution.detectionEngine.rules.editPageTitle": "編集",
|
||||
"xpack.securitySolution.detectionEngine.rules.importRuleTitle": "ルールのインポート...",
|
||||
"xpack.securitySolution.detectionEngine.rules.loadPrePackagedRulesButton": "Elastic事前構築済みルールおよびタイムラインテンプレートを読み込む",
|
||||
"xpack.securitySolution.detectionEngine.rules.loadPrePackagedRulesAndTemplatesButton": "Elastic事前構築済みルールおよびタイムラインテンプレートを読み込む",
|
||||
"xpack.securitySolution.detectionEngine.rules.optionalFieldDescription": "オプション",
|
||||
"xpack.securitySolution.detectionEngine.rules.pageTitle": "検出ルール",
|
||||
"xpack.securitySolution.detectionEngine.rules.prePackagedRules.createOwnRuletButton": "独自のルールの作成",
|
||||
"xpack.securitySolution.detectionEngine.rules.prePackagedRules.emptyPromptMessage": "Elasticセキュリティには、バックグラウンドで実行され、条件が合うとアラートを作成する事前構築済み検出ルールがあります。デフォルトでは、Elastic Endpoint Securityルールを除くすべての事前構築済みルールが無効になっています。有効にする追加のルールを選択することができます。",
|
||||
"xpack.securitySolution.detectionEngine.rules.prePackagedRules.emptyPromptTitle": "Elastic事前構築済み検出ルールを読み込む",
|
||||
"xpack.securitySolution.detectionEngine.rules.prePackagedRules.loadPreBuiltButton": "事前構築済み検出ルールおよびタイムラインテンプレートを読み込む",
|
||||
"xpack.securitySolution.detectionEngine.rules.releaseNotesHelp": "リリースノート",
|
||||
"xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesAndTimelinesButton": "{missingRules} Elastic事前構築済み{missingRules, plural, =1 {ルール} other {ルール}}と{missingTimelines} Elastic事前構築済み{missingTimelines, plural, =1 {タイムライン} other {タイムライン}}をインストール ",
|
||||
"xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesButton": "{missingRules} Elasticの事前構築済みの{missingRules, plural, =1 {個のルール} other {個のルール}}をインストール ",
|
||||
|
|
|
@ -15583,13 +15583,12 @@
|
|||
"xpack.securitySolution.detectionEngine.rules.deleteDescription": "删除",
|
||||
"xpack.securitySolution.detectionEngine.rules.editPageTitle": "编辑",
|
||||
"xpack.securitySolution.detectionEngine.rules.importRuleTitle": "导入规则……",
|
||||
"xpack.securitySolution.detectionEngine.rules.loadPrePackagedRulesButton": "加载 Elastic 预构建规则和时间线模板",
|
||||
"xpack.securitySolution.detectionEngine.rules.loadPrePackagedRulesAndTemplatesButton": "加载 Elastic 预构建规则和时间线模板",
|
||||
"xpack.securitySolution.detectionEngine.rules.optionalFieldDescription": "可选",
|
||||
"xpack.securitySolution.detectionEngine.rules.pageTitle": "检测规则",
|
||||
"xpack.securitySolution.detectionEngine.rules.prePackagedRules.createOwnRuletButton": "创建自己的规则",
|
||||
"xpack.securitySolution.detectionEngine.rules.prePackagedRules.emptyPromptMessage": "Elastic Security 附带预置检测规则,这些规则在后台运行,并在条件满足时创建告警。默认情况下,除 Elastic Endpoint Security 规则外,所有预置规则都处于禁用状态。您可以选择其他要激活的规则。",
|
||||
"xpack.securitySolution.detectionEngine.rules.prePackagedRules.emptyPromptTitle": "加载 Elastic 预构建检测规则",
|
||||
"xpack.securitySolution.detectionEngine.rules.prePackagedRules.loadPreBuiltButton": "加载预置检测规则和时间线模板",
|
||||
"xpack.securitySolution.detectionEngine.rules.releaseNotesHelp": "发行说明",
|
||||
"xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesAndTimelinesButton": "安装 {missingRules} 个 Elastic 预构建{missingRules, plural, =1 {规则} other {规则}}以及 {missingTimelines} 个 Elastic 预构建{missingTimelines, plural, =1 {时间线} other {时间线}} ",
|
||||
"xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesButton": "安装 {missingRules} 个 Elastic 预构建{missingRules, plural, =1 {规则} other {规则}} ",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue