diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/table_filters.test.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/table_filters.test.tsx
index 615d052347203..21dcc9732440d 100644
--- a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/table_filters.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/table_filters.test.tsx
@@ -19,17 +19,20 @@ jest.mock('../../../../containers/case/use_get_tags');
const onFilterChanged = jest.fn();
const fetchReporters = jest.fn();
+const fetchTags = jest.fn();
+const setFilterRefetch = jest.fn();
const props = {
countClosedCases: 1234,
countOpenCases: 99,
onFilterChanged,
initial: DEFAULT_FILTER_OPTIONS,
+ setFilterRefetch,
};
describe('CasesTableFilters ', () => {
beforeEach(() => {
jest.resetAllMocks();
- (useGetTags as jest.Mock).mockReturnValue({ tags: ['coke', 'pepsi'] });
+ (useGetTags as jest.Mock).mockReturnValue({ tags: ['coke', 'pepsi'], fetchTags });
(useGetReporters as jest.Mock).mockReturnValue({
reporters: ['casetester'],
respReporters: [{ username: 'casetester' }],
@@ -57,7 +60,7 @@ describe('CasesTableFilters ', () => {
.text()
).toEqual('Closed cases (1234)');
});
- it('should call onFilterChange when tags change', () => {
+ it('should call onFilterChange when selected tags change', () => {
const wrapper = mount(
@@ -74,7 +77,7 @@ describe('CasesTableFilters ', () => {
expect(onFilterChanged).toBeCalledWith({ tags: ['coke'] });
});
- it('should call onFilterChange when reporters change', () => {
+ it('should call onFilterChange when selected reporters change', () => {
const wrapper = mount(
@@ -118,4 +121,45 @@ describe('CasesTableFilters ', () => {
expect(onFilterChanged).toBeCalledWith({ status: 'closed' });
});
+ it('should call on load setFilterRefetch', () => {
+ mount(
+
+
+
+ );
+ expect(setFilterRefetch).toHaveBeenCalled();
+ });
+ it('should remove tag from selected tags when tag no longer exists', () => {
+ const ourProps = {
+ ...props,
+ initial: {
+ ...DEFAULT_FILTER_OPTIONS,
+ tags: ['pepsi', 'rc'],
+ },
+ };
+ mount(
+
+
+
+ );
+ expect(onFilterChanged).toHaveBeenCalledWith({ tags: ['pepsi'] });
+ });
+ it('should remove reporter from selected reporters when reporter no longer exists', () => {
+ const ourProps = {
+ ...props,
+ initial: {
+ ...DEFAULT_FILTER_OPTIONS,
+ reporters: [
+ { username: 'casetester', full_name: null, email: null },
+ { username: 'batman', full_name: null, email: null },
+ ],
+ },
+ };
+ mount(
+
+
+
+ );
+ expect(onFilterChanged).toHaveBeenCalledWith({ reporters: [{ username: 'casetester' }] });
+ });
});
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/table_filters.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/table_filters.tsx
index da477a56c0a22..901fb133753e8 100644
--- a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/table_filters.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/table_filters.tsx
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { useCallback, useState } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
import { isEqual } from 'lodash/fp';
import {
EuiFieldSearch,
@@ -25,6 +25,7 @@ interface CasesTableFiltersProps {
countOpenCases: number | null;
onFilterChanged: (filterOptions: Partial) => void;
initial: FilterOptions;
+ setFilterRefetch: (val: () => void) => void;
}
/**
@@ -41,6 +42,7 @@ const CasesTableFiltersComponent = ({
countOpenCases,
onFilterChanged,
initial = defaultInitial,
+ setFilterRefetch,
}: CasesTableFiltersProps) => {
const [selectedReporters, setSelectedReporters] = useState(
initial.reporters.map(r => r.full_name ?? r.username ?? '')
@@ -48,8 +50,29 @@ const CasesTableFiltersComponent = ({
const [search, setSearch] = useState(initial.search);
const [selectedTags, setSelectedTags] = useState(initial.tags);
const [showOpenCases, setShowOpenCases] = useState(initial.status === 'open');
- const { tags } = useGetTags();
- const { reporters, respReporters } = useGetReporters();
+ const { tags, fetchTags } = useGetTags();
+ const { reporters, respReporters, fetchReporters } = useGetReporters();
+ const refetch = useCallback(() => {
+ fetchTags();
+ fetchReporters();
+ }, [fetchReporters, fetchTags]);
+ useEffect(() => {
+ if (setFilterRefetch != null) {
+ setFilterRefetch(refetch);
+ }
+ }, [refetch, setFilterRefetch]);
+ useEffect(() => {
+ if (selectedReporters.length) {
+ const newReporters = selectedReporters.filter(r => reporters.includes(r));
+ handleSelectedReporters(newReporters);
+ }
+ }, [reporters]);
+ useEffect(() => {
+ if (selectedTags.length) {
+ const newTags = selectedTags.filter(t => tags.includes(t));
+ handleSelectedTags(newTags);
+ }
+ }, [tags]);
const handleSelectedReporters = useCallback(
newReporters => {
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/activity_monitor/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/activity_monitor/index.test.tsx
new file mode 100644
index 0000000000000..c5a4057b64ea7
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/activity_monitor/index.test.tsx
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { ActivityMonitor } from './index';
+
+describe('activity_monitor', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('[title="Activity monitor"]')).toBeTruthy();
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/detection_engine_header_page/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/detection_engine_header_page/index.test.tsx
new file mode 100644
index 0000000000000..a2685017f86d6
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/detection_engine_header_page/index.test.tsx
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { DetectionEngineHeaderPage } from './index';
+
+describe('detection_engine_header_page', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('[title="Title"]')).toBeTruthy();
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/no_api_integration_callout/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/no_api_integration_callout/index.test.tsx
new file mode 100644
index 0000000000000..0e2589150e858
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/no_api_integration_callout/index.test.tsx
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { NoApiIntegrationKeyCallOut } from './index';
+
+describe('no_api_integration_callout', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('EuiCallOut')).toBeTruthy();
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/no_write_signals_callout/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/no_write_signals_callout/index.test.tsx
new file mode 100644
index 0000000000000..2e6890e60fc61
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/no_write_signals_callout/index.test.tsx
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { NoWriteSignalsCallOut } from './index';
+
+describe('no_write_signals_callout', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('EuiCallOut')).toBeTruthy();
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.test.tsx
new file mode 100644
index 0000000000000..b66a9fc881045
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.test.tsx
@@ -0,0 +1,43 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { SignalsTableComponent } from './index';
+
+describe('SignalsTableComponent', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find('[title="Signals"]')).toBeTruthy();
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx
index 6cdb2f326901e..ce8ae2054b2c7 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx
@@ -61,7 +61,7 @@ interface OwnProps {
type SignalsTableComponentProps = OwnProps & PropsFromRedux;
-const SignalsTableComponent: React.FC = ({
+export const SignalsTableComponent: React.FC = ({
canUserCRUD,
clearEventsDeleted,
clearEventsLoading,
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_filter_group/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_filter_group/index.test.tsx
new file mode 100644
index 0000000000000..dd30bb1b0a74d
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_filter_group/index.test.tsx
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { SignalsTableFilterGroup } from './index';
+
+describe('SignalsTableFilterGroup', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('EuiFilterButton')).toBeTruthy();
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/batch_actions.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/batch_actions.tsx
deleted file mode 100644
index bb45ff68cb01d..0000000000000
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/batch_actions.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { EuiContextMenuItem } from '@elastic/eui';
-import React from 'react';
-import * as i18n from './translations';
-import { TimelineNonEcsData } from '../../../../../graphql/types';
-import { SendSignalsToTimeline, UpdateSignalsStatus } from '../types';
-import { FILTER_CLOSED, FILTER_OPEN } from '../signals_filter_group';
-
-interface GetBatchItems {
- areEventsLoading: boolean;
- allEventsSelected: boolean;
- selectedEventIds: Readonly>;
- updateSignalsStatus: UpdateSignalsStatus;
- sendSignalsToTimeline: SendSignalsToTimeline;
- closePopover: () => void;
- isFilteredToOpen: boolean;
-}
-/**
- * Returns ViewInTimeline / UpdateSignalStatus actions to be display within an EuiContextMenuPanel
- *
- * @param areEventsLoading are any events loading
- * @param allEventsSelected are all events on all pages selected
- * @param selectedEventIds
- * @param updateSignalsStatus function for updating signal status
- * @param sendSignalsToTimeline function for sending signals to timeline
- * @param closePopover
- * @param isFilteredToOpen currently selected filter options
- */
-export const getBatchItems = ({
- areEventsLoading,
- allEventsSelected,
- selectedEventIds,
- updateSignalsStatus,
- sendSignalsToTimeline,
- closePopover,
- isFilteredToOpen,
-}: GetBatchItems) => {
- const allDisabled = areEventsLoading || Object.keys(selectedEventIds).length === 0;
- const sendToTimelineDisabled = allEventsSelected || uniqueRuleCount(selectedEventIds) > 1;
- const filterString = isFilteredToOpen
- ? i18n.BATCH_ACTION_CLOSE_SELECTED
- : i18n.BATCH_ACTION_OPEN_SELECTED;
-
- return [
- {
- closePopover();
- sendSignalsToTimeline();
- }}
- >
- {i18n.BATCH_ACTION_VIEW_SELECTED_IN_TIMELINE}
- ,
-
- {
- closePopover();
- await updateSignalsStatus({
- signalIds: Object.keys(selectedEventIds),
- status: isFilteredToOpen ? FILTER_CLOSED : FILTER_OPEN,
- });
- }}
- >
- {filterString}
- ,
- ];
-};
-
-/**
- * Returns the number of unique rules for a given list of signals
- *
- * @param signals
- */
-export const uniqueRuleCount = (
- signals: Readonly>
-): number => {
- const ruleIds = Object.values(signals).flatMap(
- data => data.find(d => d.field === 'signal.rule.id')?.value
- );
-
- return Array.from(new Set(ruleIds)).length;
-};
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.test.tsx
new file mode 100644
index 0000000000000..6cab43b5285b5
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.test.tsx
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { SignalsUtilityBar } from './index';
+
+jest.mock('../../../../../lib/kibana');
+
+describe('SignalsUtilityBar', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find('[dataTestSubj="openCloseSignal"]')).toBeTruthy();
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.test.tsx
new file mode 100644
index 0000000000000..6921c49d8a8b4
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.test.tsx
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { SignalsHistogramPanel } from './index';
+
+jest.mock('../../../../lib/kibana');
+jest.mock('../../../../components/navigation/use_get_url_search');
+
+describe('SignalsHistogramPanel', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find('[id="detections-histogram"]')).toBeTruthy();
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram.test.tsx
new file mode 100644
index 0000000000000..5eb9beaaaf76a
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram.test.tsx
@@ -0,0 +1,22 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { SignalsHistogram } from './signals_histogram';
+
+jest.mock('../../../../lib/kibana');
+
+describe('SignalsHistogram', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find('Chart')).toBeTruthy();
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/user_info/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/user_info/index.test.tsx
new file mode 100644
index 0000000000000..b3d710de5e94e
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/user_info/index.test.tsx
@@ -0,0 +1,50 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { renderHook } from '@testing-library/react-hooks';
+import { useUserInfo } from './index';
+
+import { usePrivilegeUser } from '../../../../containers/detection_engine/signals/use_privilege_user';
+import { useSignalIndex } from '../../../../containers/detection_engine/signals/use_signal_index';
+import { useKibana } from '../../../../lib/kibana';
+jest.mock('../../../../containers/detection_engine/signals/use_privilege_user');
+jest.mock('../../../../containers/detection_engine/signals/use_signal_index');
+jest.mock('../../../../lib/kibana');
+
+describe('useUserInfo', () => {
+ beforeAll(() => {
+ (usePrivilegeUser as jest.Mock).mockReturnValue({});
+ (useSignalIndex as jest.Mock).mockReturnValue({});
+ (useKibana as jest.Mock).mockReturnValue({
+ services: {
+ application: {
+ capabilities: {
+ siem: {
+ crud: true,
+ },
+ },
+ },
+ },
+ });
+ });
+ it('returns default state', () => {
+ const { result } = renderHook(() => useUserInfo());
+
+ expect(result).toEqual({
+ current: {
+ canUserCRUD: null,
+ hasEncryptionKey: null,
+ hasIndexManage: null,
+ hasIndexWrite: null,
+ isAuthenticated: null,
+ isSignalIndexExists: null,
+ loading: true,
+ signalIndexName: null,
+ },
+ error: undefined,
+ });
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.test.tsx
new file mode 100644
index 0000000000000..779e9a4557f2a
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.test.tsx
@@ -0,0 +1,43 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+import { useParams } from 'react-router-dom';
+
+import '../../mock/match_media';
+import { setAbsoluteRangeDatePicker } from '../../store/inputs/actions';
+import { DetectionEnginePageComponent } from './detection_engine';
+import { useUserInfo } from './components/user_info';
+
+jest.mock('./components/user_info');
+jest.mock('../../lib/kibana');
+jest.mock('react-router-dom', () => {
+ const originalModule = jest.requireActual('react-router-dom');
+
+ return {
+ ...originalModule,
+ useParams: jest.fn(),
+ };
+});
+
+describe('DetectionEnginePageComponent', () => {
+ beforeAll(() => {
+ (useParams as jest.Mock).mockReturnValue({});
+ (useUserInfo as jest.Mock).mockReturnValue({});
+ });
+ it('renders correctly', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find('WithSource')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx
index 1bd7ab2c4f1ae..a26d7f5672106 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx
@@ -59,7 +59,7 @@ const detectionsTabs: Record = {
},
};
-const DetectionEnginePageComponent: React.FC = ({
+export const DetectionEnginePageComponent: React.FC = ({
filters,
query,
setAbsoluteRangeDatePicker,
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_empty_page.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_empty_page.test.tsx
new file mode 100644
index 0000000000000..f64526fd2f7c4
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_empty_page.test.tsx
@@ -0,0 +1,19 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { DetectionEngineEmptyPage } from './detection_engine_empty_page';
+jest.mock('../../lib/kibana');
+
+describe('DetectionEngineEmptyPage', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('EmptyPage')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_no_signal_index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_no_signal_index.test.tsx
new file mode 100644
index 0000000000000..e9f05f7aafe3c
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_no_signal_index.test.tsx
@@ -0,0 +1,19 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { DetectionEngineNoIndex } from './detection_engine_no_signal_index';
+jest.mock('../../lib/kibana');
+
+describe('DetectionEngineNoIndex', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('EmptyPage')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_user_unauthenticated.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_user_unauthenticated.test.tsx
new file mode 100644
index 0000000000000..e71f4de2b010b
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_user_unauthenticated.test.tsx
@@ -0,0 +1,19 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { DetectionEngineUserUnauthenticated } from './detection_engine_user_unauthenticated';
+jest.mock('../../lib/kibana');
+
+describe('DetectionEngineUserUnauthenticated', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('EmptyPage')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/helpers.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/helpers.ts
deleted file mode 100644
index 1399df0fcf6d1..0000000000000
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/helpers.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export const sampleChartOptions = [
- { text: 'Risk scores', value: 'risk_scores' },
- { text: 'Severities', value: 'severities' },
- { text: 'Top destination IPs', value: 'destination_ips' },
- { text: 'Top event actions', value: 'event_actions' },
- { text: 'Top event categories', value: 'event_categories' },
- { text: 'Top host names', value: 'host_names' },
- { text: 'Top rule types', value: 'rule_types' },
- { text: 'Top rules', value: 'rules' },
- { text: 'Top source IPs', value: 'source_ips' },
- { text: 'Top users', value: 'users' },
-];
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/index.test.tsx
new file mode 100644
index 0000000000000..6c4980f1d1500
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/index.test.tsx
@@ -0,0 +1,19 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import '../../mock/match_media';
+import { DetectionEngineContainer } from './index';
+
+describe('DetectionEngineContainer', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('ManageUserInfo')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.test.tsx
new file mode 100644
index 0000000000000..f4955c2a93b8d
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.test.tsx
@@ -0,0 +1,40 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { AllRules } from './index';
+
+jest.mock('react-router-dom', () => {
+ const originalModule = jest.requireActual('react-router-dom');
+
+ return {
+ ...originalModule,
+ useHistory: jest.fn(),
+ };
+});
+
+describe('AllRules', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find('[title="All rules"]')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.test.tsx
new file mode 100644
index 0000000000000..92f69d79110d2
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.test.tsx
@@ -0,0 +1,20 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { RulesTableFilters } from './rules_table_filters';
+
+describe('RulesTableFilters', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find('[data-test-subj="show-elastic-rules-filter-button"]')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.test.tsx
new file mode 100644
index 0000000000000..e31b8394e07d6
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.test.tsx
@@ -0,0 +1,25 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { TagsFilterPopover } from './tags_filter_popover';
+
+describe('TagsFilterPopover', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find('EuiPopover')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/accordion_title/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/accordion_title/index.test.tsx
new file mode 100644
index 0000000000000..9202da3336565
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/accordion_title/index.test.tsx
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { AccordionTitle } from './index';
+
+describe('AccordionTitle', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('h6').text()).toContain('title');
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/add_item_form/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/add_item_form/index.test.tsx
new file mode 100644
index 0000000000000..eafa89a33f596
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/add_item_form/index.test.tsx
@@ -0,0 +1,32 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { AddItem } from './index';
+import { useFormFieldMock } from '../../../../../../public/mock/test_providers';
+
+describe('AddItem', () => {
+ it('renders correctly', () => {
+ const Component = () => {
+ const field = useFormFieldMock();
+
+ return (
+
+ );
+ };
+ const wrapper = shallow();
+
+ expect(wrapper.dive().find('[iconType="plusInCircle"]')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/all_rules_tables/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/all_rules_tables/index.test.tsx
new file mode 100644
index 0000000000000..3dab83bca2946
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/all_rules_tables/index.test.tsx
@@ -0,0 +1,46 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useRef } from 'react';
+import { shallow } from 'enzyme';
+
+import { AllRulesTables } from './index';
+
+describe('AllRulesTables', () => {
+ it('renders correctly', () => {
+ const Component = () => {
+ const ref = useRef();
+
+ return (
+
+ );
+ };
+ const wrapper = shallow();
+
+ expect(wrapper.dive().find('[data-test-subj="rules-table"]')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/anomaly_threshold_slider/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/anomaly_threshold_slider/index.test.tsx
new file mode 100644
index 0000000000000..c0e957d94261f
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/anomaly_threshold_slider/index.test.tsx
@@ -0,0 +1,24 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { AnomalyThresholdSlider } from './index';
+import { useFormFieldMock } from '../../../../../mock';
+
+describe('AnomalyThresholdSlider', () => {
+ it('renders correctly', () => {
+ const Component = () => {
+ const field = useFormFieldMock();
+
+ return ;
+ };
+ const wrapper = shallow();
+
+ expect(wrapper.dive().find('EuiRange')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/anomaly_threshold_slider/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/anomaly_threshold_slider/index.tsx
index 19d1c698cbd9b..01fddf98b97d8 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/anomaly_threshold_slider/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/anomaly_threshold_slider/index.tsx
@@ -16,10 +16,10 @@ interface AnomalyThresholdSliderProps {
type Event = React.ChangeEvent;
type EventArg = Event | React.MouseEvent;
-export const AnomalyThresholdSlider: React.FC = ({
+export const AnomalyThresholdSlider = ({
describedByIds = [],
field,
-}) => {
+}: AnomalyThresholdSliderProps) => {
const threshold = field.value as number;
const onThresholdChange = useCallback(
(event: EventArg) => {
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/ml_job_description.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/ml_job_description.test.tsx
new file mode 100644
index 0000000000000..59231c31d15bb
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/ml_job_description.test.tsx
@@ -0,0 +1,54 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { MlJobDescription, AuditIcon, JobStatusBadge } from './ml_job_description';
+jest.mock('../../../../../lib/kibana');
+
+const job = {
+ moduleId: 'moduleId',
+ defaultIndexPattern: 'defaultIndexPattern',
+ isCompatible: true,
+ isInstalled: true,
+ isElasticJob: true,
+ datafeedId: 'datafeedId',
+ datafeedIndices: [],
+ datafeedState: 'datafeedState',
+ description: 'description',
+ groups: [],
+ hasDatafeed: true,
+ id: 'id',
+ isSingleMetricViewerJob: false,
+ jobState: 'jobState',
+ memory_status: 'memory_status',
+ processed_record_count: 0,
+};
+
+describe('MlJobDescription', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('[data-test-subj="machineLearningJobId"]')).toHaveLength(1);
+ });
+});
+
+describe('AuditIcon', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('EuiToolTip')).toHaveLength(0);
+ });
+});
+
+describe('JobStatusBadge', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('EuiBadge')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/ml_job_description.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/ml_job_description.tsx
index 5a9593f1a6de2..1664ea320bc1e 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/ml_job_description.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/ml_job_description.tsx
@@ -20,7 +20,7 @@ enum MessageLevels {
error = 'error',
}
-const AuditIcon: React.FC<{
+const AuditIconComponent: React.FC<{
message: SiemJob['auditMessage'];
}> = ({ message }) => {
if (!message) {
@@ -45,7 +45,9 @@ const AuditIcon: React.FC<{
);
};
-export const JobStatusBadge: React.FC<{ job: SiemJob }> = ({ job }) => {
+export const AuditIcon = React.memo(AuditIconComponent);
+
+const JobStatusBadgeComponent: React.FC<{ job: SiemJob }> = ({ job }) => {
const isStarted = isJobStarted(job.jobState, job.datafeedState);
const color = isStarted ? 'secondary' : 'danger';
const text = isStarted ? ML_JOB_STARTED : ML_JOB_STOPPED;
@@ -57,6 +59,8 @@ export const JobStatusBadge: React.FC<{ job: SiemJob }> = ({ job }) => {
);
};
+export const JobStatusBadge = React.memo(JobStatusBadgeComponent);
+
const JobLink = styled(EuiLink)`
margin-right: ${({ theme }) => theme.eui.euiSizeS};
`;
@@ -65,7 +69,7 @@ const Wrapper = styled.div`
overflow: hidden;
`;
-export const MlJobDescription: React.FC<{ job: SiemJob }> = ({ job }) => {
+const MlJobDescriptionComponent: React.FC<{ job: SiemJob }> = ({ job }) => {
const jobUrl = useKibana().services.application.getUrlForApp(
`ml#/jobs?mlManagement=(jobId:${encodeURI(job.id)})`
);
@@ -83,6 +87,8 @@ export const MlJobDescription: React.FC<{ job: SiemJob }> = ({ job }) => {
);
};
+export const MlJobDescription = React.memo(MlJobDescriptionComponent);
+
export const buildMlJobDescription = (
jobId: string,
label: string,
diff --git a/x-pack/legacy/plugins/reporting/server/config/config.js b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/helpers.test.tsx
similarity index 53%
rename from x-pack/legacy/plugins/reporting/server/config/config.js
rename to x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/helpers.test.tsx
index 08e4db464b003..dc201eb21c911 100644
--- a/x-pack/legacy/plugins/reporting/server/config/config.js
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/helpers.test.tsx
@@ -4,18 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { cpus } from 'os';
+import { isMitreAttackInvalid } from './helpers';
-const defaultCPUCount = 2;
-
-function cpuCount() {
- try {
- return cpus().length;
- } catch (e) {
- return defaultCPUCount;
- }
-}
-
-export const config = {
- concurrency: cpuCount(),
-};
+describe('isMitreAttackInvalid', () => {
+ it('returns true if tacticName is empty', () => {
+ expect(isMitreAttackInvalid('', undefined)).toBe(true);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.test.tsx
new file mode 100644
index 0000000000000..3e8d542682456
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.test.tsx
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { AddMitreThreat } from './index';
+import { useFormFieldMock } from '../../../../../mock';
+
+describe('AddMitreThreat', () => {
+ it('renders correctly', () => {
+ const Component = () => {
+ const field = useFormFieldMock();
+
+ return (
+
+ );
+ };
+ const wrapper = shallow();
+
+ expect(wrapper.dive().find('[data-test-subj="addMitre"]')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/ml_job_select/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/ml_job_select/index.test.tsx
new file mode 100644
index 0000000000000..dea27d8d04536
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/ml_job_select/index.test.tsx
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { MlJobSelect } from './index';
+import { useSiemJobs } from '../../../../../components/ml_popover/hooks/use_siem_jobs';
+import { useFormFieldMock } from '../../../../../mock';
+jest.mock('../../../../../components/ml_popover/hooks/use_siem_jobs');
+jest.mock('../../../../../lib/kibana');
+
+describe('MlJobSelect', () => {
+ beforeAll(() => {
+ (useSiemJobs as jest.Mock).mockReturnValue([false, []]);
+ });
+
+ it('renders correctly', () => {
+ const Component = () => {
+ const field = useFormFieldMock();
+
+ return ;
+ };
+ const wrapper = shallow();
+
+ expect(wrapper.dive().find('[data-test-subj="mlJobSelect"]')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/optional_field_label/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/optional_field_label/index.test.tsx
new file mode 100644
index 0000000000000..789f12f290a34
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/optional_field_label/index.test.tsx
@@ -0,0 +1,17 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { shallow } from 'enzyme';
+
+import { OptionalFieldLabel } from './index';
+
+describe('OptionalFieldLabel', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow(OptionalFieldLabel);
+
+ expect(wrapper.find('EuiTextColor')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/pick_timeline/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/pick_timeline/index.test.tsx
new file mode 100644
index 0000000000000..fefc9697176c4
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/pick_timeline/index.test.tsx
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { PickTimeline } from './index';
+import { useFormFieldMock } from '../../../../../mock';
+
+describe('PickTimeline', () => {
+ it('renders correctly', () => {
+ const Component = () => {
+ const field = useFormFieldMock();
+
+ return (
+
+ );
+ };
+ const wrapper = shallow();
+
+ expect(wrapper.dive().find('[data-test-subj="pick-timeline"]')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/pre_packaged_rules/load_empty_prompt.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/pre_packaged_rules/load_empty_prompt.test.tsx
new file mode 100644
index 0000000000000..8ace42fc5c3f9
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/pre_packaged_rules/load_empty_prompt.test.tsx
@@ -0,0 +1,24 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { PrePackagedRulesPrompt } from './load_empty_prompt';
+
+describe('PrePackagedRulesPrompt', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find('EmptyPrompt')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/pre_packaged_rules/load_empty_prompt.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/pre_packaged_rules/load_empty_prompt.tsx
index 1cff4751e8188..5d136265ef1f2 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/pre_packaged_rules/load_empty_prompt.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/pre_packaged_rules/load_empty_prompt.tsx
@@ -15,6 +15,8 @@ const EmptyPrompt = styled(EuiEmptyPrompt)`
align-self: center; /* Corrects horizontal centering in IE11 */
`;
+EmptyPrompt.displayName = 'EmptyPrompt';
+
interface PrePackagedRulesPromptProps {
createPrePackagedRules: () => void;
loading: boolean;
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/pre_packaged_rules/update_callout.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/pre_packaged_rules/update_callout.test.tsx
new file mode 100644
index 0000000000000..807da79fb7a1a
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/pre_packaged_rules/update_callout.test.tsx
@@ -0,0 +1,36 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { UpdatePrePackagedRulesCallOut } from './update_callout';
+import { useKibana } from '../../../../../lib/kibana';
+jest.mock('../../../../../lib/kibana');
+
+describe('UpdatePrePackagedRulesCallOut', () => {
+ beforeAll(() => {
+ (useKibana as jest.Mock).mockReturnValue({
+ services: {
+ docLinks: {
+ ELASTIC_WEBSITE_URL: '',
+ DOC_LINK_VERSION: '',
+ },
+ },
+ });
+ });
+ it('renders correctly', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find('EuiCallOut')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/query_bar/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/query_bar/index.test.tsx
new file mode 100644
index 0000000000000..cdd06ad58bb4b
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/query_bar/index.test.tsx
@@ -0,0 +1,37 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { QueryBarDefineRule } from './index';
+import { useFormFieldMock } from '../../../../../mock';
+
+jest.mock('../../../../../lib/kibana');
+
+describe('QueryBarDefineRule', () => {
+ it('renders correctly', () => {
+ const Component = () => {
+ const field = useFormFieldMock();
+
+ return (
+
+ );
+ };
+ const wrapper = shallow();
+
+ expect(wrapper.dive().find('[data-test-subj="query-bar-define-rule"]')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/read_only_callout/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/read_only_callout/index.test.tsx
new file mode 100644
index 0000000000000..e761cb3323b2c
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/read_only_callout/index.test.tsx
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { ReadOnlyCallOut } from './index';
+
+describe('ReadOnlyCallOut', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('EuiCallOut')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_actions_field/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_actions_field/index.test.tsx
new file mode 100644
index 0000000000000..4cfad36b2933f
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_actions_field/index.test.tsx
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { RuleActionsField } from './index';
+import { useKibana } from '../../../../../lib/kibana';
+import { useFormFieldMock } from '../../../../../mock';
+jest.mock('../../../../../lib/kibana');
+
+describe('RuleActionsField', () => {
+ it('should not render ActionForm is no actions are supported', () => {
+ (useKibana as jest.Mock).mockReturnValue({
+ services: {
+ triggers_actions_ui: {
+ actionTypeRegistry: {},
+ },
+ },
+ });
+ const Component = () => {
+ const field = useFormFieldMock();
+
+ return ;
+ };
+ const wrapper = shallow();
+
+ expect(wrapper.dive().find('ActionForm')).toHaveLength(0);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_status/helpers.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_status/helpers.test.tsx
new file mode 100644
index 0000000000000..aba30e4b7f3ca
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_status/helpers.test.tsx
@@ -0,0 +1,13 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { getStatusColor } from './helpers';
+
+describe('rule_status helpers', () => {
+ it('getStatusColor returns subdued if null was provided', () => {
+ expect(getStatusColor(null)).toBe('subdued');
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_status/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_status/index.test.tsx
new file mode 100644
index 0000000000000..6e230de11c4f3
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_status/index.test.tsx
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { RuleStatus } from './index';
+
+describe('RuleStatus', () => {
+ it('renders loader correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.dive().find('[data-test-subj="rule-status-loader"]')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/schedule_item_form/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/schedule_item_form/index.test.tsx
new file mode 100644
index 0000000000000..3829af02ca4f1
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/schedule_item_form/index.test.tsx
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { ScheduleItem } from './index';
+import { useFormFieldMock } from '../../../../../mock';
+
+describe('ScheduleItem', () => {
+ it('renders correctly', () => {
+ const Component = () => {
+ const field = useFormFieldMock();
+
+ return (
+
+ );
+ };
+ const wrapper = shallow();
+
+ expect(wrapper.dive().find('[data-test-subj="schedule-item"]')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.test.tsx
new file mode 100644
index 0000000000000..3d832d61abb28
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/select_rule_type/index.test.tsx
@@ -0,0 +1,25 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { SelectRuleType } from './index';
+import { useFormFieldMock } from '../../../../../mock';
+jest.mock('../../../../../lib/kibana');
+
+describe('SelectRuleType', () => {
+ it('renders correctly', () => {
+ const Component = () => {
+ const field = useFormFieldMock();
+
+ return ;
+ };
+ const wrapper = shallow();
+
+ expect(wrapper.dive().find('[data-test-subj="selectRuleType"]')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/severity_badge/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/severity_badge/index.test.tsx
new file mode 100644
index 0000000000000..a9dddfedc2bab
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/severity_badge/index.test.tsx
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { SeverityBadge } from './index';
+
+describe('SeverityBadge', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('EuiHealth')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/status_icon/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/status_icon/index.test.tsx
new file mode 100644
index 0000000000000..89b8a56e79054
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/status_icon/index.test.tsx
@@ -0,0 +1,22 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { TestProviders } from '../../../../../mock';
+import { RuleStatusIcon } from './index';
+jest.mock('../../../../../lib/kibana');
+
+describe('RuleStatusIcon', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow(, {
+ wrappingComponent: TestProviders,
+ });
+
+ expect(wrapper.find('EuiAvatar')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_content_wrapper/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_content_wrapper/index.test.tsx
new file mode 100644
index 0000000000000..af0547ea03261
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_content_wrapper/index.test.tsx
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { StepContentWrapper } from './index';
+
+describe('StepContentWrapper', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('div')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_content_wrapper/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_content_wrapper/index.tsx
index b04a321dab05b..a7343a87a7ef8 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_content_wrapper/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_content_wrapper/index.tsx
@@ -16,3 +16,5 @@ StyledDiv.defaultProps = {
};
export const StepContentWrapper = React.memo(StyledDiv);
+
+StepContentWrapper.displayName = 'StepContentWrapper';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.test.tsx
new file mode 100644
index 0000000000000..ebef6348d477e
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.test.tsx
@@ -0,0 +1,20 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { StepDefineRule } from './index';
+
+jest.mock('../../../../../lib/kibana');
+
+describe('StepDefineRule', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('Form[data-test-subj="stepDefineRule"]')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_panel/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_panel/index.test.tsx
new file mode 100644
index 0000000000000..ce01d6995a2d8
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_panel/index.test.tsx
@@ -0,0 +1,22 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { StepPanel } from './index';
+
+describe('StepPanel', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow(
+
+
+
+ );
+
+ expect(wrapper.find('MyPanel')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_panel/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_panel/index.tsx
index 88cecadb8b137..1923ed09252dd 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_panel/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_panel/index.tsx
@@ -20,6 +20,8 @@ const MyPanel = styled(EuiPanel)`
position: relative;
`;
+MyPanel.displayName = 'MyPanel';
+
const StepPanelComponent: React.FC = ({ children, loading, title }) => (
{loading && }
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_rule_actions/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_rule_actions/index.test.tsx
new file mode 100644
index 0000000000000..69d118ba9f28e
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_rule_actions/index.test.tsx
@@ -0,0 +1,22 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { StepRuleActions } from './index';
+
+jest.mock('../../../../../lib/kibana');
+
+describe('StepRuleActions', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper.find('[data-test-subj="stepRuleActions"]')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_rule_actions/schema.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_rule_actions/schema.tsx
index bc3b0dfe720bc..1b27d0e0fcc0e 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_rule_actions/schema.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_rule_actions/schema.tsx
@@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
+/* istanbul ignore file */
+
import { i18n } from '@kbn/i18n';
import { FormSchema } from '../../../../../shared_imports';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/index.test.tsx
new file mode 100644
index 0000000000000..98de933590d60
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/index.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow, mount } from 'enzyme';
+
+import { TestProviders } from '../../../../../mock';
+import { StepScheduleRule } from './index';
+
+describe('StepScheduleRule', () => {
+ it('renders correctly', () => {
+ const wrapper = mount(, {
+ wrappingComponent: TestProviders,
+ });
+
+ expect(wrapper.find('Form[data-test-subj="stepScheduleRule"]')).toHaveLength(1);
+ });
+
+ it('renders correctly if isReadOnlyView', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('StepContentWrapper')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/schema.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/schema.tsx
index 8fbfdf5f25a51..e79aec2be6e15 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/schema.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/schema.tsx
@@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
+/* istanbul ignore file */
+
import { i18n } from '@kbn/i18n';
import { OptionalFieldLabel } from '../optional_field_label';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/throttle_select_field/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/throttle_select_field/index.test.tsx
new file mode 100644
index 0000000000000..0ab19b671494e
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/throttle_select_field/index.test.tsx
@@ -0,0 +1,24 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { ThrottleSelectField } from './index';
+import { useFormFieldMock } from '../../../../../mock';
+
+describe('ThrottleSelectField', () => {
+ it('renders correctly', () => {
+ const Component = () => {
+ const field = useFormFieldMock();
+
+ return ;
+ };
+ const wrapper = shallow();
+
+ expect(wrapper.dive().find('SelectField')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/index.test.tsx
new file mode 100644
index 0000000000000..db32be652d0f7
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/index.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { TestProviders } from '../../../../mock';
+import { CreateRulePage } from './index';
+import { useUserInfo } from '../../components/user_info';
+
+jest.mock('../../components/user_info');
+
+describe('CreateRulePage', () => {
+ it('renders correctly', () => {
+ (useUserInfo as jest.Mock).mockReturnValue({});
+ const wrapper = shallow(, { wrappingComponent: TestProviders });
+
+ expect(wrapper.find('[title="Create new rule"]')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/failure_history.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/failure_history.test.tsx
new file mode 100644
index 0000000000000..a83ff4c54b076
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/failure_history.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { TestProviders } from '../../../../mock';
+import { FailureHistory } from './failure_history';
+import { useRuleStatus } from '../../../../containers/detection_engine/rules';
+jest.mock('../../../../containers/detection_engine/rules');
+
+describe('FailureHistory', () => {
+ beforeAll(() => {
+ (useRuleStatus as jest.Mock).mockReturnValue([false, null]);
+ });
+
+ it('renders correctly', () => {
+ const wrapper = shallow(, {
+ wrappingComponent: TestProviders,
+ });
+
+ expect(wrapper.find('EuiBasicTable')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.test.tsx
new file mode 100644
index 0000000000000..19c6f39a9bc7e
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.test.tsx
@@ -0,0 +1,47 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import '../../../../mock/match_media';
+import { TestProviders } from '../../../../mock';
+import { RuleDetailsPageComponent } from './index';
+import { setAbsoluteRangeDatePicker } from '../../../../store/inputs/actions';
+import { useUserInfo } from '../../components/user_info';
+import { useParams } from 'react-router-dom';
+
+jest.mock('../../components/user_info');
+jest.mock('react-router-dom', () => {
+ const originalModule = jest.requireActual('react-router-dom');
+
+ return {
+ ...originalModule,
+ useParams: jest.fn(),
+ };
+});
+
+describe('RuleDetailsPageComponent', () => {
+ beforeAll(() => {
+ (useUserInfo as jest.Mock).mockReturnValue({});
+ (useParams as jest.Mock).mockReturnValue({});
+ });
+
+ it('renders correctly', () => {
+ const wrapper = shallow(
+ ,
+ {
+ wrappingComponent: TestProviders,
+ }
+ );
+
+ expect(wrapper.find('WithSource')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx
index 2b648a3b3f825..14e5f2b90882e 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx
@@ -88,7 +88,7 @@ const ruleDetailTabs = [
},
];
-const RuleDetailsPageComponent: FC = ({
+export const RuleDetailsPageComponent: FC = ({
filters,
query,
setAbsoluteRangeDatePicker,
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/status_failed_callout.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/status_failed_callout.test.tsx
new file mode 100644
index 0000000000000..3394b0fc8c5c0
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/status_failed_callout.test.tsx
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { RuleStatusFailedCallOut } from './status_failed_callout';
+
+describe('RuleStatusFailedCallOut', () => {
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('EuiCallOut')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/edit/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/edit/index.test.tsx
new file mode 100644
index 0000000000000..d22bc12abf9fa
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/edit/index.test.tsx
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { TestProviders } from '../../../../mock';
+import { EditRulePage } from './index';
+import { useUserInfo } from '../../components/user_info';
+import { useParams } from 'react-router-dom';
+
+jest.mock('../../components/user_info');
+jest.mock('react-router-dom', () => {
+ const originalModule = jest.requireActual('react-router-dom');
+
+ return {
+ ...originalModule,
+ useParams: jest.fn(),
+ };
+});
+
+describe('EditRulePage', () => {
+ it('renders correctly', () => {
+ (useUserInfo as jest.Mock).mockReturnValue({});
+ (useParams as jest.Mock).mockReturnValue({});
+ const wrapper = shallow(, { wrappingComponent: TestProviders });
+
+ expect(wrapper.find('[title="Edit rule settings"]')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.test.tsx
index 443dbd2c93a35..1c01a19573cd6 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.test.tsx
@@ -302,11 +302,25 @@ describe('rule helpers', () => {
test('returns expected ActionsStepRule rule object', () => {
const mockedRule = {
...mockRule('test-id'),
- actions: [],
+ actions: [
+ {
+ id: 'id',
+ group: 'group',
+ params: {},
+ action_type_id: 'action_type_id',
+ },
+ ],
};
const result: ActionsStepRule = getActionsStepsData(mockedRule);
const expected = {
- actions: [],
+ actions: [
+ {
+ id: 'id',
+ group: 'group',
+ params: {},
+ actionTypeId: 'action_type_id',
+ },
+ ],
enabled: mockedRule.enabled,
isNew: false,
throttle: 'no_actions',
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.test.tsx
new file mode 100644
index 0000000000000..3fa81ca3ced08
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import { RulesPage } from './index';
+import { useUserInfo } from '../components/user_info';
+import { usePrePackagedRules } from '../../../containers/detection_engine/rules';
+
+jest.mock('../components/user_info');
+jest.mock('../../../containers/detection_engine/rules');
+
+describe('RulesPage', () => {
+ beforeAll(() => {
+ (useUserInfo as jest.Mock).mockReturnValue({});
+ (usePrePackagedRules as jest.Mock).mockReturnValue({});
+ });
+ it('renders correctly', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.find('AllRules')).toHaveLength(1);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/utils.test.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/utils.test.ts
new file mode 100644
index 0000000000000..34a521ed32b12
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/utils.test.ts
@@ -0,0 +1,24 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { getBreadcrumbs } from './utils';
+
+describe('getBreadcrumbs', () => {
+ it('returns default value for incorrect params', () => {
+ expect(
+ getBreadcrumbs(
+ {
+ pageName: 'pageName',
+ detailName: 'detailName',
+ tabName: undefined,
+ search: '',
+ pathName: 'pathName',
+ },
+ []
+ )
+ ).toEqual([{ href: '#/link-to/detections', text: 'Detections' }]);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/rules_notification_alert_type.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/rules_notification_alert_type.test.ts
index 50ac10347e062..f537b22bac1eb 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/rules_notification_alert_type.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/rules_notification_alert_type.test.ts
@@ -4,42 +4,27 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { savedObjectsClientMock } from 'src/core/server/mocks';
import { loggerMock } from 'src/core/server/logging/logger.mock';
import { getResult } from '../routes/__mocks__/request_responses';
import { rulesNotificationAlertType } from './rules_notification_alert_type';
import { buildSignalsSearchQuery } from './build_signals_query';
-import { AlertInstance } from '../../../../../../../plugins/alerting/server';
+import { alertsMock, AlertServicesMock } from '../../../../../../../plugins/alerting/server/mocks';
import { NotificationExecutorOptions } from './types';
jest.mock('./build_signals_query');
describe('rules_notification_alert_type', () => {
let payload: NotificationExecutorOptions;
let alert: ReturnType;
- let alertInstanceMock: Record;
- let alertInstanceFactoryMock: () => AlertInstance;
- let savedObjectsClient: ReturnType;
let logger: ReturnType;
- let callClusterMock: jest.Mock;
+ let alertServices: AlertServicesMock;
beforeEach(() => {
- alertInstanceMock = {
- scheduleActions: jest.fn(),
- replaceState: jest.fn(),
- };
- alertInstanceMock.replaceState.mockReturnValue(alertInstanceMock);
- alertInstanceFactoryMock = jest.fn().mockReturnValue(alertInstanceMock);
- callClusterMock = jest.fn();
- savedObjectsClient = savedObjectsClientMock.create();
+ alertServices = alertsMock.createAlertServices();
logger = loggerMock.create();
payload = {
alertId: '1111',
- services: {
- savedObjectsClient,
- alertInstanceFactory: alertInstanceFactoryMock,
- callCluster: callClusterMock,
- },
+ services: alertServices,
params: { ruleAlertId: '2222' },
state: {},
spaceId: '',
@@ -58,7 +43,7 @@ describe('rules_notification_alert_type', () => {
describe('executor', () => {
it('throws an error if rule alert was not found', async () => {
- savedObjectsClient.get.mockResolvedValue({
+ alertServices.savedObjectsClient.get.mockResolvedValue({
id: 'id',
attributes: {},
type: 'type',
@@ -72,13 +57,13 @@ describe('rules_notification_alert_type', () => {
it('should call buildSignalsSearchQuery with proper params', async () => {
const ruleAlert = getResult();
- savedObjectsClient.get.mockResolvedValue({
+ alertServices.savedObjectsClient.get.mockResolvedValue({
id: 'id',
type: 'type',
references: [],
attributes: ruleAlert,
});
- callClusterMock.mockResolvedValue({
+ alertServices.callCluster.mockResolvedValue({
count: 0,
});
@@ -96,36 +81,38 @@ describe('rules_notification_alert_type', () => {
it('should not call alertInstanceFactory if signalsCount was 0', async () => {
const ruleAlert = getResult();
- savedObjectsClient.get.mockResolvedValue({
+ alertServices.savedObjectsClient.get.mockResolvedValue({
id: 'id',
type: 'type',
references: [],
attributes: ruleAlert,
});
- callClusterMock.mockResolvedValue({
+ alertServices.callCluster.mockResolvedValue({
count: 0,
});
await alert.executor(payload);
- expect(alertInstanceFactoryMock).not.toHaveBeenCalled();
+ expect(alertServices.alertInstanceFactory).not.toHaveBeenCalled();
});
it('should call scheduleActions if signalsCount was greater than 0', async () => {
const ruleAlert = getResult();
- savedObjectsClient.get.mockResolvedValue({
+ alertServices.savedObjectsClient.get.mockResolvedValue({
id: 'id',
type: 'type',
references: [],
attributes: ruleAlert,
});
- callClusterMock.mockResolvedValue({
+ alertServices.callCluster.mockResolvedValue({
count: 10,
});
await alert.executor(payload);
- expect(alertInstanceFactoryMock).toHaveBeenCalled();
+ expect(alertServices.alertInstanceFactory).toHaveBeenCalled();
+
+ const [{ value: alertInstanceMock }] = alertServices.alertInstanceFactory.mock.results;
expect(alertInstanceMock.replaceState).toHaveBeenCalledWith(
expect.objectContaining({ signals_count: 10 })
);
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts
index 29ae5056a3ae8..57ccc7a7806ac 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts
@@ -26,13 +26,13 @@ import {
buildSiemResponse,
validateLicenseForRuleType,
} from '../utils';
-import { createRulesStreamFromNdJson } from '../../rules/create_rules_stream_from_ndjson';
import { ImportRuleAlertRest } from '../../types';
import { patchRules } from '../../rules/patch_rules';
import { importRulesQuerySchema, importRulesPayloadSchema } from '../schemas/import_rules_schema';
import { ImportRulesSchema, importRulesSchema } from '../schemas/response/import_rules_schema';
import { getTupleDuplicateErrorsAndUniqueRules } from './utils';
import { validate } from './validate';
+import { createRulesStreamFromNdJson } from '../../rules/create_rules_stream_from_ndjson';
type PromiseFromStreams = ImportRuleAlertRest | Error;
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts
index 31a0f37fe81c9..8b1b0cab3b2f2 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts
@@ -12,7 +12,6 @@ import {
transformTags,
getIdBulkError,
transformOrBulkError,
- transformDataToNdjson,
transformAlertsToRules,
transformOrImportError,
getDuplicates,
@@ -22,14 +21,13 @@ import { getResult } from '../__mocks__/request_responses';
import { INTERNAL_IDENTIFIER } from '../../../../../common/constants';
import { ImportRuleAlertRest, RuleAlertParamsRest, RuleTypeParams } from '../../types';
import { BulkError, ImportSuccessError } from '../utils';
-import { sampleRule } from '../../signals/__mocks__/es_results';
import { getSimpleRule, getOutputRuleAlertForRest } from '../__mocks__/utils';
-import { createRulesStreamFromNdJson } from '../../rules/create_rules_stream_from_ndjson';
import { createPromiseFromStreams } from '../../../../../../../../../src/legacy/utils/streams';
import { PartialAlert } from '../../../../../../../../plugins/alerting/server';
import { SanitizedAlert } from '../../../../../../../../plugins/alerting/server/types';
import { RuleAlertType } from '../../rules/types';
import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../feature_flags';
+import { createRulesStreamFromNdJson } from '../../rules/create_rules_stream_from_ndjson';
type PromiseFromStreams = ImportRuleAlertRest | Error;
@@ -396,47 +394,6 @@ describe('utils', () => {
});
});
- describe('transformDataToNdjson', () => {
- test('if rules are empty it returns an empty string', () => {
- const ruleNdjson = transformDataToNdjson([]);
- expect(ruleNdjson).toEqual('');
- });
-
- test('single rule will transform with new line ending character for ndjson', () => {
- const rule = sampleRule();
- const ruleNdjson = transformDataToNdjson([rule]);
- expect(ruleNdjson.endsWith('\n')).toBe(true);
- });
-
- test('multiple rules will transform with two new line ending characters for ndjson', () => {
- const result1 = sampleRule();
- const result2 = sampleRule();
- result2.id = 'some other id';
- result2.rule_id = 'some other id';
- result2.name = 'Some other rule';
-
- const ruleNdjson = transformDataToNdjson([result1, result2]);
- // this is how we count characters in JavaScript :-)
- const count = ruleNdjson.split('\n').length - 1;
- expect(count).toBe(2);
- });
-
- test('you can parse two rules back out without errors', () => {
- const result1 = sampleRule();
- const result2 = sampleRule();
- result2.id = 'some other id';
- result2.rule_id = 'some other id';
- result2.name = 'Some other rule';
-
- const ruleNdjson = transformDataToNdjson([result1, result2]);
- const ruleStrings = ruleNdjson.split('\n');
- const reParsed1 = JSON.parse(ruleStrings[0]);
- const reParsed2 = JSON.parse(ruleStrings[1]);
- expect(reParsed1).toEqual(result1);
- expect(reParsed2).toEqual(result2);
- });
- });
-
describe('transformAlertsToRules', () => {
test('given an empty array returns an empty array', () => {
expect(transformAlertsToRules([])).toEqual([]);
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts
index 4d13fa1b6ae50..790603fa8cfc1 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts
@@ -152,15 +152,6 @@ export const transformAlertToRule = (
});
};
-export const transformDataToNdjson = (data: unknown[]): string => {
- if (data.length !== 0) {
- const dataString = data.map(rule => JSON.stringify(rule)).join('\n');
- return `${dataString}\n`;
- } else {
- return '';
- }
-};
-
export const transformAlertsToRules = (
alerts: RuleAlertType[]
): Array> => {
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.ts
index 3e22999528101..27008d17d2192 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.ts
@@ -4,39 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Transform } from 'stream';
-import { has, isString } from 'lodash/fp';
import { ImportRuleAlertRest } from '../types';
import {
createSplitStream,
createMapStream,
- createFilterStream,
createConcatStream,
} from '../../../../../../../../src/legacy/utils/streams';
import { importRulesSchema } from '../routes/schemas/import_rules_schema';
import { BadRequestError } from '../errors/bad_request_error';
-
-export interface RulesObjectsExportResultDetails {
- /** number of successfully exported objects */
- exportedCount: number;
-}
-
-export const parseNdjsonStrings = (): Transform => {
- return createMapStream((ndJsonStr: string) => {
- if (isString(ndJsonStr) && ndJsonStr.trim() !== '') {
- try {
- return JSON.parse(ndJsonStr);
- } catch (err) {
- return err;
- }
- }
- });
-};
-
-export const filterExportedCounts = (): Transform => {
- return createFilterStream(
- obj => obj != null && !has('exported_count', obj)
- );
-};
+import {
+ parseNdjsonStrings,
+ filterExportedCounts,
+ createLimitStream,
+} from '../../../utils/read_stream/create_stream_from_ndjson';
export const validateRules = (): Transform => {
return createMapStream((obj: ImportRuleAlertRest) => {
@@ -53,21 +33,6 @@ export const validateRules = (): Transform => {
});
};
-// Adaptation from: saved_objects/import/create_limit_stream.ts
-export const createLimitStream = (limit: number): Transform => {
- let counter = 0;
- return new Transform({
- objectMode: true,
- async transform(obj, _, done) {
- if (counter >= limit) {
- return done(new Error(`Can't import more than ${limit} rules`));
- }
- counter++;
- done(undefined, obj);
- },
- });
-};
-
// TODO: Capture both the line number and the rule_id if you have that information for the error message
// eventually and then pass it down so we can give error messages on the line number
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.ts
index 6a27abb66ce85..40c07f28ea848 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.ts
@@ -7,7 +7,8 @@
import { AlertsClient } from '../../../../../../../plugins/alerting/server';
import { getNonPackagedRules } from './get_existing_prepackaged_rules';
import { getExportDetailsNdjson } from './get_export_details_ndjson';
-import { transformAlertsToRules, transformDataToNdjson } from '../routes/rules/utils';
+import { transformAlertsToRules } from '../routes/rules/utils';
+import { transformDataToNdjson } from '../../../utils/read_stream/create_stream_from_ndjson';
export const getExportAll = async (
alertsClient: AlertsClient
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.ts
index 6f642231ebbaf..048f09e95b062 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.ts
@@ -8,8 +8,9 @@ import { AlertsClient } from '../../../../../../../plugins/alerting/server';
import { getExportDetailsNdjson } from './get_export_details_ndjson';
import { isAlertType } from '../rules/types';
import { readRules } from './read_rules';
-import { transformDataToNdjson, transformAlertToRule } from '../routes/rules/utils';
+import { transformAlertToRule } from '../routes/rules/utils';
import { OutputRuleAlertRest } from '../types';
+import { transformDataToNdjson } from '../../../utils/read_stream/create_stream_from_ndjson';
interface ExportSuccesRule {
statusCode: 200;
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts
index 86d1278031695..510667b211d25 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts
@@ -5,42 +5,28 @@
*/
import { getQueryFilter, getFilter } from './get_filter';
-import { savedObjectsClientMock } from 'src/core/server/mocks';
import { PartialFilter } from '../types';
-import { AlertServices } from '../../../../../../../plugins/alerting/server';
+import { alertsMock, AlertServicesMock } from '../../../../../../../plugins/alerting/server/mocks';
describe('get_filter', () => {
- let savedObjectsClient = savedObjectsClientMock.create();
- savedObjectsClient.get = jest.fn().mockImplementation(() => ({
- attributes: {
- query: { query: 'host.name: linux', language: 'kuery' },
- filters: [],
- },
- }));
- let servicesMock: AlertServices = {
- savedObjectsClient,
- callCluster: jest.fn(),
- alertInstanceFactory: jest.fn(),
- };
+ let servicesMock: AlertServicesMock;
beforeAll(() => {
jest.resetAllMocks();
});
beforeEach(() => {
- savedObjectsClient = savedObjectsClientMock.create();
- savedObjectsClient.get = jest.fn().mockImplementation(() => ({
+ servicesMock = alertsMock.createAlertServices();
+ servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({
+ id,
+ type,
+ references: [],
attributes: {
query: { query: 'host.name: linux', language: 'kuery' },
language: 'kuery',
filters: [],
},
}));
- servicesMock = {
- savedObjectsClient,
- callCluster: jest.fn(),
- alertInstanceFactory: jest.fn(),
- };
});
afterEach(() => {
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts
index 18286dc7754e0..ccd882228d4de 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts
@@ -4,22 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { savedObjectsClientMock } from 'src/core/server/mocks';
import { DEFAULT_INDEX_KEY } from '../../../../common/constants';
import { getInputIndex } from './get_input_output_index';
import { defaultIndexPattern } from '../../../../default_index_pattern';
-import { AlertServices } from '../../../../../../../plugins/alerting/server';
+import { alertsMock, AlertServicesMock } from '../../../../../../../plugins/alerting/server/mocks';
describe('get_input_output_index', () => {
- let savedObjectsClient = savedObjectsClientMock.create();
- savedObjectsClient.get = jest.fn().mockImplementation(() => ({
- attributes: {},
- }));
- let servicesMock: AlertServices = {
- savedObjectsClient,
- callCluster: jest.fn(),
- alertInstanceFactory: jest.fn(),
- };
+ let servicesMock: AlertServicesMock;
beforeAll(() => {
jest.resetAllMocks();
@@ -30,20 +21,21 @@ describe('get_input_output_index', () => {
});
beforeEach(() => {
- savedObjectsClient = savedObjectsClientMock.create();
- savedObjectsClient.get = jest.fn().mockImplementation(() => ({
+ servicesMock = alertsMock.createAlertServices();
+ servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({
+ id,
+ type,
+ references: [],
attributes: {},
}));
- servicesMock = {
- savedObjectsClient,
- callCluster: jest.fn(),
- alertInstanceFactory: jest.fn(),
- };
});
describe('getInputOutputIndex', () => {
test('Returns inputIndex if inputIndex is passed in', async () => {
- savedObjectsClient.get = jest.fn().mockImplementation(() => ({
+ servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({
+ id,
+ type,
+ references: [],
attributes: {},
}));
const inputIndex = await getInputIndex(servicesMock, '8.0.0', ['test-input-index-1']);
@@ -51,7 +43,10 @@ describe('get_input_output_index', () => {
});
test('Returns a saved object inputIndex if passed in inputIndex is undefined', async () => {
- savedObjectsClient.get = jest.fn().mockImplementation(() => ({
+ servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({
+ id,
+ type,
+ references: [],
attributes: {
[DEFAULT_INDEX_KEY]: ['configured-index-1', 'configured-index-2'],
},
@@ -61,7 +56,10 @@ describe('get_input_output_index', () => {
});
test('Returns a saved object inputIndex if passed in inputIndex is null', async () => {
- savedObjectsClient.get = jest.fn().mockImplementation(() => ({
+ servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({
+ id,
+ type,
+ references: [],
attributes: {
[DEFAULT_INDEX_KEY]: ['configured-index-1', 'configured-index-2'],
},
@@ -71,7 +69,10 @@ describe('get_input_output_index', () => {
});
test('Returns a saved object inputIndex default from constants if inputIndex passed in is null and the key is also null', async () => {
- savedObjectsClient.get = jest.fn().mockImplementation(() => ({
+ servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({
+ id,
+ type,
+ references: [],
attributes: {
[DEFAULT_INDEX_KEY]: null,
},
@@ -81,7 +82,10 @@ describe('get_input_output_index', () => {
});
test('Returns a saved object inputIndex default from constants if inputIndex passed in is undefined and the key is also null', async () => {
- savedObjectsClient.get = jest.fn().mockImplementation(() => ({
+ servicesMock.savedObjectsClient.get.mockImplementation(async (type: string, id: string) => ({
+ id,
+ type,
+ references: [],
attributes: {
[DEFAULT_INDEX_KEY]: null,
},
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts
index 81600b0b8dd9b..9e2f36fe2653a 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts
@@ -16,20 +16,16 @@ import {
} from './__mocks__/es_results';
import { searchAfterAndBulkCreate } from './search_after_bulk_create';
import { DEFAULT_SIGNALS_INDEX } from '../../../../common/constants';
-import { savedObjectsClientMock } from 'src/core/server/mocks';
+import { alertsMock, AlertServicesMock } from '../../../../../../../plugins/alerting/server/mocks';
import uuid from 'uuid';
-export const mockService = {
- callCluster: jest.fn(),
- alertInstanceFactory: jest.fn(),
- savedObjectsClient: savedObjectsClientMock.create(),
-};
-
describe('searchAfterAndBulkCreate', () => {
+ let mockService: AlertServicesMock;
let inputIndexPattern: string[] = [];
beforeEach(() => {
jest.clearAllMocks();
inputIndexPattern = ['auditbeat-*'];
+ mockService = alertsMock.createAlertServices();
});
test('if successful with empty search results', async () => {
@@ -65,7 +61,7 @@ describe('searchAfterAndBulkCreate', () => {
const sampleParams = sampleRuleAlertParams(30);
const someGuids = Array.from({ length: 13 }).map(x => uuid.v4());
mockService.callCluster
- .mockReturnValueOnce({
+ .mockResolvedValueOnce({
took: 100,
errors: false,
items: [
@@ -79,8 +75,8 @@ describe('searchAfterAndBulkCreate', () => {
},
],
})
- .mockReturnValueOnce(repeatedSearchResultsWithSortId(3, 1, someGuids.slice(0, 3)))
- .mockReturnValueOnce({
+ .mockResolvedValueOnce(repeatedSearchResultsWithSortId(3, 1, someGuids.slice(0, 3)))
+ .mockResolvedValueOnce({
took: 100,
errors: false,
items: [
@@ -94,8 +90,8 @@ describe('searchAfterAndBulkCreate', () => {
},
],
})
- .mockReturnValueOnce(repeatedSearchResultsWithSortId(3, 1, someGuids.slice(3, 6)))
- .mockReturnValueOnce({
+ .mockResolvedValueOnce(repeatedSearchResultsWithSortId(3, 1, someGuids.slice(3, 6)))
+ .mockResolvedValueOnce({
took: 100,
errors: false,
items: [
@@ -139,7 +135,7 @@ describe('searchAfterAndBulkCreate', () => {
test('if unsuccessful first bulk create', async () => {
const someGuids = Array.from({ length: 4 }).map(x => uuid.v4());
const sampleParams = sampleRuleAlertParams(10);
- mockService.callCluster.mockReturnValue(sampleBulkCreateDuplicateResult);
+ mockService.callCluster.mockResolvedValue(sampleBulkCreateDuplicateResult);
const { success, createdSignalsCount } = await searchAfterAndBulkCreate({
someResult: repeatedSearchResultsWithSortId(4, 1, someGuids),
ruleParams: sampleParams,
@@ -169,7 +165,7 @@ describe('searchAfterAndBulkCreate', () => {
test('if unsuccessful iteration of searchAfterAndBulkCreate due to empty sort ids', async () => {
const sampleParams = sampleRuleAlertParams();
- mockService.callCluster.mockReturnValueOnce({
+ mockService.callCluster.mockResolvedValueOnce({
took: 100,
errors: false,
items: [
@@ -212,7 +208,7 @@ describe('searchAfterAndBulkCreate', () => {
test('if unsuccessful iteration of searchAfterAndBulkCreate due to empty sort ids and 0 total hits', async () => {
const sampleParams = sampleRuleAlertParams();
- mockService.callCluster.mockReturnValueOnce({
+ mockService.callCluster.mockResolvedValueOnce({
took: 100,
errors: false,
items: [
@@ -256,7 +252,7 @@ describe('searchAfterAndBulkCreate', () => {
const sampleParams = sampleRuleAlertParams(10);
const someGuids = Array.from({ length: 4 }).map(x => uuid.v4());
mockService.callCluster
- .mockReturnValueOnce({
+ .mockResolvedValueOnce({
took: 100,
errors: false,
items: [
@@ -270,7 +266,7 @@ describe('searchAfterAndBulkCreate', () => {
},
],
})
- .mockReturnValueOnce(sampleDocSearchResultsNoSortId());
+ .mockResolvedValueOnce(sampleDocSearchResultsNoSortId());
const { success, createdSignalsCount } = await searchAfterAndBulkCreate({
someResult: repeatedSearchResultsWithSortId(4, 1, someGuids),
ruleParams: sampleParams,
@@ -301,7 +297,7 @@ describe('searchAfterAndBulkCreate', () => {
const sampleParams = sampleRuleAlertParams(10);
const someGuids = Array.from({ length: 4 }).map(x => uuid.v4());
mockService.callCluster
- .mockReturnValueOnce({
+ .mockResolvedValueOnce({
took: 100,
errors: false,
items: [
@@ -315,7 +311,7 @@ describe('searchAfterAndBulkCreate', () => {
},
],
})
- .mockReturnValueOnce(sampleEmptyDocSearchResults());
+ .mockResolvedValueOnce(sampleEmptyDocSearchResults());
const { success, createdSignalsCount } = await searchAfterAndBulkCreate({
someResult: repeatedSearchResultsWithSortId(4, 1, someGuids),
ruleParams: sampleParams,
@@ -346,7 +342,7 @@ describe('searchAfterAndBulkCreate', () => {
const sampleParams = sampleRuleAlertParams(10);
const someGuids = Array.from({ length: 4 }).map(x => uuid.v4());
mockService.callCluster
- .mockReturnValueOnce({
+ .mockResolvedValueOnce({
took: 100,
errors: false,
items: [
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts
index 03fb5832fdf42..31b407da111ea 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts
@@ -5,11 +5,10 @@
*/
import moment from 'moment';
-import { savedObjectsClientMock } from 'src/core/server/mocks';
import { loggerMock } from 'src/core/server/logging/logger.mock';
import { getResult, getMlResult } from '../routes/__mocks__/request_responses';
import { signalRulesAlertType } from './signal_rule_alert_type';
-import { AlertInstance } from '../../../../../../../plugins/alerting/server';
+import { alertsMock, AlertServicesMock } from '../../../../../../../plugins/alerting/server/mocks';
import { ruleStatusServiceFactory } from './rule_status_service';
import { getGapBetweenRuns } from './utils';
import { RuleExecutorOptions } from './types';
@@ -28,18 +27,9 @@ jest.mock('../notifications/schedule_notification_actions');
jest.mock('./find_ml_signals');
jest.mock('./bulk_create_ml_signals');
-const getPayload = (
- ruleAlert: RuleAlertType,
- alertInstanceFactoryMock: () => AlertInstance,
- savedObjectsClient: ReturnType,
- callClusterMock: jest.Mock
-) => ({
+const getPayload = (ruleAlert: RuleAlertType, services: AlertServicesMock) => ({
alertId: ruleAlert.id,
- services: {
- savedObjectsClient,
- alertInstanceFactory: alertInstanceFactoryMock,
- callCluster: callClusterMock,
- },
+ services,
params: {
...ruleAlert.params,
actions: [],
@@ -78,24 +68,14 @@ describe('rules_notification_alert_type', () => {
modulesProvider: jest.fn(),
resultsServiceProvider: jest.fn(),
};
- let payload: RuleExecutorOptions;
+ let payload: jest.Mocked;
let alert: ReturnType;
- let alertInstanceMock: Record;
- let alertInstanceFactoryMock: () => AlertInstance;
- let savedObjectsClient: ReturnType;
let logger: ReturnType;
- let callClusterMock: jest.Mock;
+ let alertServices: AlertServicesMock;
let ruleStatusService: Record;
beforeEach(() => {
- alertInstanceMock = {
- scheduleActions: jest.fn(),
- replaceState: jest.fn(),
- };
- alertInstanceMock.replaceState.mockReturnValue(alertInstanceMock);
- alertInstanceFactoryMock = jest.fn().mockReturnValue(alertInstanceMock);
- callClusterMock = jest.fn();
- savedObjectsClient = savedObjectsClientMock.create();
+ alertServices = alertsMock.createAlertServices();
logger = loggerMock.create();
ruleStatusService = {
success: jest.fn(),
@@ -111,20 +91,20 @@ describe('rules_notification_alert_type', () => {
searchAfterTimes: [],
createdSignalsCount: 10,
});
- callClusterMock.mockResolvedValue({
+ alertServices.callCluster.mockResolvedValue({
hits: {
total: { value: 10 },
},
});
const ruleAlert = getResult();
- savedObjectsClient.get.mockResolvedValue({
+ alertServices.savedObjectsClient.get.mockResolvedValue({
id: 'id',
type: 'type',
references: [],
attributes: ruleAlert,
});
- payload = getPayload(ruleAlert, alertInstanceFactoryMock, savedObjectsClient, callClusterMock);
+ payload = getPayload(ruleAlert, alertServices);
alert = signalRulesAlertType({
logger,
@@ -164,7 +144,7 @@ describe('rules_notification_alert_type', () => {
},
];
- savedObjectsClient.get.mockResolvedValue({
+ alertServices.savedObjectsClient.get.mockResolvedValue({
id: 'id',
type: 'type',
references: [],
@@ -195,7 +175,7 @@ describe('rules_notification_alert_type', () => {
},
];
- savedObjectsClient.get.mockResolvedValue({
+ alertServices.savedObjectsClient.get.mockResolvedValue({
id: 'id',
type: 'type',
references: [],
@@ -214,12 +194,7 @@ describe('rules_notification_alert_type', () => {
describe('ML rule', () => {
it('should throw an error if ML plugin was not available', async () => {
const ruleAlert = getMlResult();
- payload = getPayload(
- ruleAlert,
- alertInstanceFactoryMock,
- savedObjectsClient,
- callClusterMock
- );
+ payload = getPayload(ruleAlert, alertServices);
alert = signalRulesAlertType({
logger,
version,
@@ -235,12 +210,7 @@ describe('rules_notification_alert_type', () => {
it('should throw an error if machineLearningJobId or anomalyThreshold was not null', async () => {
const ruleAlert = getMlResult();
ruleAlert.params.anomalyThreshold = undefined;
- payload = getPayload(
- ruleAlert,
- alertInstanceFactoryMock,
- savedObjectsClient,
- callClusterMock
- );
+ payload = getPayload(ruleAlert, alertServices);
await alert.executor(payload);
expect(logger.error).toHaveBeenCalled();
expect(logger.error.mock.calls[0][0]).toContain(
@@ -250,12 +220,7 @@ describe('rules_notification_alert_type', () => {
it('should throw an error if Machine learning job summary was null', async () => {
const ruleAlert = getMlResult();
- payload = getPayload(
- ruleAlert,
- alertInstanceFactoryMock,
- savedObjectsClient,
- callClusterMock
- );
+ payload = getPayload(ruleAlert, alertServices);
jobsSummaryMock.mockResolvedValue([]);
await alert.executor(payload);
expect(logger.warn).toHaveBeenCalled();
@@ -268,12 +233,7 @@ describe('rules_notification_alert_type', () => {
it('should log an error if Machine learning job was not started', async () => {
const ruleAlert = getMlResult();
- payload = getPayload(
- ruleAlert,
- alertInstanceFactoryMock,
- savedObjectsClient,
- callClusterMock
- );
+ payload = getPayload(ruleAlert, alertServices);
jobsSummaryMock.mockResolvedValue([
{
id: 'some_job_id',
@@ -297,12 +257,7 @@ describe('rules_notification_alert_type', () => {
it('should not call ruleStatusService.success if no anomalies were found', async () => {
const ruleAlert = getMlResult();
- payload = getPayload(
- ruleAlert,
- alertInstanceFactoryMock,
- savedObjectsClient,
- callClusterMock
- );
+ payload = getPayload(ruleAlert, alertServices);
jobsSummaryMock.mockResolvedValue([]);
(findMlSignals as jest.Mock).mockResolvedValue({
hits: {
@@ -320,12 +275,7 @@ describe('rules_notification_alert_type', () => {
it('should call ruleStatusService.success if signals were created', async () => {
const ruleAlert = getMlResult();
- payload = getPayload(
- ruleAlert,
- alertInstanceFactoryMock,
- savedObjectsClient,
- callClusterMock
- );
+ payload = getPayload(ruleAlert, alertServices);
jobsSummaryMock.mockResolvedValue([
{
id: 'some_job_id',
@@ -360,13 +310,8 @@ describe('rules_notification_alert_type', () => {
id: '99403909-ca9b-49ba-9d7a-7e5320e68d05',
},
];
- payload = getPayload(
- ruleAlert,
- alertInstanceFactoryMock,
- savedObjectsClient,
- callClusterMock
- );
- savedObjectsClient.get.mockResolvedValue({
+ payload = getPayload(ruleAlert, alertServices);
+ alertServices.savedObjectsClient.get.mockResolvedValue({
id: 'id',
type: 'type',
references: [],
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts
index 45365b446cbf0..3401d7417ec62 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts
@@ -16,17 +16,13 @@ import {
sampleBulkCreateErrorResult,
sampleDocWithAncestors,
} from './__mocks__/es_results';
-import { savedObjectsClientMock } from 'src/core/server/mocks';
import { DEFAULT_SIGNALS_INDEX } from '../../../../common/constants';
import { singleBulkCreate, filterDuplicateRules } from './single_bulk_create';
-
-export const mockService = {
- callCluster: jest.fn(),
- alertInstanceFactory: jest.fn(),
- savedObjectsClient: savedObjectsClientMock.create(),
-};
+import { alertsMock, AlertServicesMock } from '../../../../../../../plugins/alerting/server/mocks';
describe('singleBulkCreate', () => {
+ const mockService: AlertServicesMock = alertsMock.createAlertServices();
+
beforeEach(() => {
jest.clearAllMocks();
});
@@ -135,7 +131,7 @@ describe('singleBulkCreate', () => {
test('create successful bulk create', async () => {
const sampleParams = sampleRuleAlertParams();
- mockService.callCluster.mockReturnValueOnce({
+ mockService.callCluster.mockResolvedValueOnce({
took: 100,
errors: false,
items: [
@@ -169,7 +165,7 @@ describe('singleBulkCreate', () => {
test('create successful bulk create with docs with no versioning', async () => {
const sampleParams = sampleRuleAlertParams();
- mockService.callCluster.mockReturnValueOnce({
+ mockService.callCluster.mockResolvedValueOnce({
took: 100,
errors: false,
items: [
@@ -203,7 +199,7 @@ describe('singleBulkCreate', () => {
test('create unsuccessful bulk create due to empty search results', async () => {
const sampleParams = sampleRuleAlertParams();
- mockService.callCluster.mockReturnValue(false);
+ mockService.callCluster.mockResolvedValue(false);
const { success, createdItemsCount } = await singleBulkCreate({
someResult: sampleEmptyDocSearchResults(),
ruleParams: sampleParams,
@@ -230,7 +226,7 @@ describe('singleBulkCreate', () => {
test('create successful bulk create when bulk create has duplicate errors', async () => {
const sampleParams = sampleRuleAlertParams();
const sampleSearchResult = sampleDocSearchResultsNoSortId;
- mockService.callCluster.mockReturnValue(sampleBulkCreateDuplicateResult);
+ mockService.callCluster.mockResolvedValue(sampleBulkCreateDuplicateResult);
const { success, createdItemsCount } = await singleBulkCreate({
someResult: sampleSearchResult(),
ruleParams: sampleParams,
@@ -259,7 +255,7 @@ describe('singleBulkCreate', () => {
test('create successful bulk create when bulk create has multiple error statuses', async () => {
const sampleParams = sampleRuleAlertParams();
const sampleSearchResult = sampleDocSearchResultsNoSortId;
- mockService.callCluster.mockReturnValue(sampleBulkCreateErrorResult);
+ mockService.callCluster.mockResolvedValue(sampleBulkCreateErrorResult);
const { success, createdItemsCount } = await singleBulkCreate({
someResult: sampleSearchResult(),
ruleParams: sampleParams,
@@ -354,7 +350,7 @@ describe('singleBulkCreate', () => {
test('create successful and returns proper createdItemsCount', async () => {
const sampleParams = sampleRuleAlertParams();
- mockService.callCluster.mockReturnValue(sampleBulkCreateDuplicateResult);
+ mockService.callCluster.mockResolvedValue(sampleBulkCreateDuplicateResult);
const { success, createdItemsCount } = await singleBulkCreate({
someResult: sampleDocSearchResultsNoSortId(),
ruleParams: sampleParams,
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_search_after.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_search_after.test.ts
index 9b726c38d3d96..dbeab70595e4f 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_search_after.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_search_after.test.ts
@@ -4,28 +4,24 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { savedObjectsClientMock } from 'src/core/server/mocks';
import {
sampleDocSearchResultsNoSortId,
mockLogger,
sampleDocSearchResultsWithSortId,
} from './__mocks__/es_results';
import { singleSearchAfter } from './single_search_after';
-
-export const mockService = {
- callCluster: jest.fn(),
- alertInstanceFactory: jest.fn(),
- savedObjectsClient: savedObjectsClientMock.create(),
-};
+import { alertsMock, AlertServicesMock } from '../../../../../../../plugins/alerting/server/mocks';
describe('singleSearchAfter', () => {
+ const mockService: AlertServicesMock = alertsMock.createAlertServices();
+
beforeEach(() => {
jest.clearAllMocks();
});
test('if singleSearchAfter works without a given sort id', async () => {
let searchAfterSortId;
- mockService.callCluster.mockReturnValue(sampleDocSearchResultsNoSortId);
+ mockService.callCluster.mockResolvedValue(sampleDocSearchResultsNoSortId);
await expect(
singleSearchAfter({
searchAfterSortId,
@@ -41,7 +37,7 @@ describe('singleSearchAfter', () => {
});
test('if singleSearchAfter works with a given sort id', async () => {
const searchAfterSortId = '1234567891111';
- mockService.callCluster.mockReturnValue(sampleDocSearchResultsWithSortId);
+ mockService.callCluster.mockResolvedValue(sampleDocSearchResultsWithSortId);
const { searchResult } = await singleSearchAfter({
searchAfterSortId,
index: [],
diff --git a/x-pack/legacy/plugins/siem/server/lib/timeline/create_timelines_stream_from_ndjson.ts b/x-pack/legacy/plugins/siem/server/lib/timeline/create_timelines_stream_from_ndjson.ts
index 5373570a4f8cc..16654b2863ee5 100644
--- a/x-pack/legacy/plugins/siem/server/lib/timeline/create_timelines_stream_from_ndjson.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/timeline/create_timelines_stream_from_ndjson.ts
@@ -3,8 +3,12 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-
+import * as rt from 'io-ts';
import { Transform } from 'stream';
+import { pipe } from 'fp-ts/lib/pipeable';
+import { fold } from 'fp-ts/lib/Either';
+import { failure } from 'io-ts/lib/PathReporter';
+import { identity } from 'fp-ts/lib/function';
import {
createConcatStream,
createSplitStream,
@@ -14,26 +18,28 @@ import {
parseNdjsonStrings,
filterExportedCounts,
createLimitStream,
-} from '../detection_engine/rules/create_rules_stream_from_ndjson';
-import { importTimelinesSchema } from './routes/schemas/import_timelines_schema';
-import { BadRequestError } from '../detection_engine/errors/bad_request_error';
+} from '../../utils/read_stream/create_stream_from_ndjson';
+
import { ImportTimelineResponse } from './routes/utils/import_timelines';
+import { ImportTimelinesSchemaRt } from './routes/schemas/import_timelines_schema';
+
+type ErrorFactory = (message: string) => Error;
-export const validateTimelines = (): Transform => {
- return createMapStream((obj: ImportTimelineResponse) => {
- if (!(obj instanceof Error)) {
- const validated = importTimelinesSchema.validate(obj);
- if (validated.error != null) {
- return new BadRequestError(validated.error.message);
- } else {
- return validated.value;
- }
- } else {
- return obj;
- }
- });
+export const createPlainError = (message: string) => new Error(message);
+
+export const throwErrors = (createError: ErrorFactory) => (errors: rt.Errors) => {
+ throw createError(failure(errors).join('\n'));
};
+export const decodeOrThrow = (
+ runtimeType: rt.Type,
+ createError: ErrorFactory = createPlainError
+) => (inputValue: I) =>
+ pipe(runtimeType.decode(inputValue), fold(throwErrors(createError), identity));
+
+export const validateTimelines = (): Transform =>
+ createMapStream((obj: ImportTimelineResponse) => decodeOrThrow(ImportTimelinesSchemaRt)(obj));
+
export const createTimelinesStreamFromNdJson = (ruleLimit: number) => {
return [
createSplitStream('\n'),
diff --git a/x-pack/legacy/plugins/siem/server/lib/timeline/routes/__mocks__/request_responses.ts b/x-pack/legacy/plugins/siem/server/lib/timeline/routes/__mocks__/request_responses.ts
index 0e73e4bdd6c97..a83c443773302 100644
--- a/x-pack/legacy/plugins/siem/server/lib/timeline/routes/__mocks__/request_responses.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/timeline/routes/__mocks__/request_responses.ts
@@ -6,11 +6,16 @@
import { TIMELINE_EXPORT_URL, TIMELINE_IMPORT_URL } from '../../../../../common/constants';
import { requestMock } from '../../../detection_engine/routes/__mocks__';
-
+import stream from 'stream';
+const readable = new stream.Readable();
export const getExportTimelinesRequest = () =>
requestMock.create({
method: 'get',
path: TIMELINE_EXPORT_URL,
+ query: {
+ file_name: 'mock_export_timeline.ndjson',
+ exclude_export_details: 'false',
+ },
body: {
ids: ['f0e58720-57b6-11ea-b88d-3f1a31716be8', '890b8ae0-57df-11ea-a7c9-3976b7f1cb37'],
},
@@ -22,7 +27,7 @@ export const getImportTimelinesRequest = (filename?: string) =>
path: TIMELINE_IMPORT_URL,
query: { overwrite: false },
body: {
- file: { hapi: { filename: filename ?? 'filename.ndjson' } },
+ file: { ...readable, hapi: { filename: filename ?? 'filename.ndjson' } },
},
});
diff --git a/x-pack/legacy/plugins/siem/server/lib/timeline/routes/export_timelines_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/timeline/routes/export_timelines_route.test.ts
index fe434b5399212..4eadede40f5d9 100644
--- a/x-pack/legacy/plugins/siem/server/lib/timeline/routes/export_timelines_route.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/timeline/routes/export_timelines_route.test.ts
@@ -83,7 +83,7 @@ describe('export timelines', () => {
});
describe('request validation', () => {
- test('disallows singular id query param', async () => {
+ test('return validation error for request body', async () => {
const request = requestMock.create({
method: 'get',
path: TIMELINE_EXPORT_URL,
@@ -91,7 +91,26 @@ describe('export timelines', () => {
});
const result = server.validate(request);
- expect(result.badRequest).toHaveBeenCalledWith('"id" is not allowed');
+ expect(result.badRequest.mock.calls[0][0]).toEqual(
+ 'Invalid value undefined supplied to : { ids: Array