diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.test.tsx
index 7ef698ae05b36..1e8525f0519ed 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.test.tsx
@@ -6,14 +6,38 @@
*/
import React from 'react';
-import { shallow } from 'enzyme';
+import { mount, shallow } from 'enzyme';
import { QueryBarDefineRule } from './index';
-import { useFormFieldMock } from '../../../../common/mock';
+import {
+ TestProviders,
+ useFormFieldMock,
+ mockOpenTimelineQueryResults,
+} from '../../../../common/mock';
+import { mockHistory, Router } from '../../../../cases/components/__mock__/router';
+import { useGetAllTimeline, getAllTimeline } from '../../../../timelines/containers/all';
jest.mock('../../../../common/lib/kibana');
+jest.mock('../../../../timelines/containers/all', () => {
+ const originalModule = jest.requireActual('../../../../timelines/containers/all');
+ return {
+ ...originalModule,
+ useGetAllTimeline: jest.fn(),
+ };
+});
+
describe('QueryBarDefineRule', () => {
+ beforeEach(() => {
+ ((useGetAllTimeline as unknown) as jest.Mock).mockReturnValue({
+ fetchAllTimeline: jest.fn(),
+ timelines: getAllTimeline('', mockOpenTimelineQueryResults.timeline ?? []),
+ loading: false,
+ totalCount: mockOpenTimelineQueryResults.totalCount,
+ refetch: jest.fn(),
+ });
+ });
+
it('renders correctly', () => {
const Component = () => {
const field = useFormFieldMock();
@@ -32,7 +56,35 @@ describe('QueryBarDefineRule', () => {
);
};
const wrapper = shallow();
-
expect(wrapper.dive().find('[data-test-subj="query-bar-define-rule"]')).toHaveLength(1);
});
+
+ it('renders import query from saved timeline modal actions hidden correctly', () => {
+ const Component = () => {
+ const field = useFormFieldMock();
+
+ return (
+
+ );
+ };
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ expect(wrapper.find('[data-test-subj="open-duplicate"]').exists()).toBeFalsy();
+ expect(wrapper.find('[data-test-subj="create-from-template"]').exists()).toBeFalsy();
+ });
});
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx
index f45ff5f1ea1a1..6bda4a0e0f6b8 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx
@@ -6,7 +6,7 @@
*/
import { EuiFormRow, EuiMutationObserver } from '@elastic/eui';
-import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
import { Subscription } from 'rxjs';
import styled from 'styled-components';
import deepEqual from 'fast-deep-equal';
@@ -50,6 +50,8 @@ interface QueryBarDefineRuleProps {
onValidityChange?: (arg: boolean) => void;
}
+const actionTimelineToHide: ActionTimelineToShow[] = ['duplicate', 'createFrom'];
+
const StyledEuiFormRow = styled(EuiFormRow)`
.kbnTypeahead__items {
max-height: 45vh !important;
@@ -253,8 +255,6 @@ export const QueryBarDefineRule = ({
}
};
- const actionTimelineToHide = useMemo(() => ['duplicate'], []);
-
return (
<>
{
+ const originalModule = jest.requireActual('../../open_timeline/use_timeline_status');
+ return {
+ ...originalModule,
+ useTimelineStatus: jest.fn().mockReturnValue({
+ timelineStatus: 'active',
+ templateTimelineFilter: [],
+ installPrepackagedTimelines: jest.fn(),
+ }),
+ };
+});
-jest.mock('../../../../common/lib/kibana', () => ({
- useKibana: jest.fn(),
- useUiSetting$: jest.fn().mockReturnValue([]),
-}));
+jest.mock('../../../../common/lib/kibana', () => {
+ const originalModule = jest.requireActual('../../../../common/lib/kibana');
+ return {
+ ...originalModule,
+ useKibana: jest.fn(),
+ useUiSetting$: jest.fn().mockReturnValue([]),
+ };
+});
+
+jest.mock('../../../containers/all', () => {
+ const originalModule = jest.requireActual('../../../containers/all');
+ return {
+ ...originalModule,
+ useGetAllTimeline: jest.fn(),
+ };
+});
jest.mock('../../timeline/properties/new_template_timeline', () => ({
NewTemplateTimeline: jest.fn(() => ),
@@ -35,8 +62,7 @@ jest.mock('../../../../common/components/inspect', () => ({
InspectButtonContainer: jest.fn(({ children }) => {children}
),
}));
-// FLAKY: https://github.com/elastic/kibana/issues/96691
-describe.skip('AddTimelineButton', () => {
+describe('AddTimelineButton', () => {
let wrapper: ReactWrapper;
const props = {
timelineId: TimelineId.active,
@@ -67,24 +93,24 @@ describe.skip('AddTimelineButton', () => {
});
test('it renders create timeline btn', async () => {
- await waitFor(() => {
- wrapper.find('[data-test-subj="settings-plus-in-circle"]').last().simulate('click');
- expect(wrapper.find('[data-test-subj="create-default-btn"]').exists()).toBeTruthy();
- });
+ wrapper.find('[data-test-subj="settings-plus-in-circle"]').last().simulate('click');
+ await waitFor(() =>
+ expect(wrapper.find('[data-test-subj="create-default-btn"]').exists()).toBeTruthy()
+ );
});
test('it renders create timeline template btn', async () => {
- await waitFor(() => {
- wrapper.find('[data-test-subj="settings-plus-in-circle"]').last().simulate('click');
- expect(wrapper.find('[data-test-subj="create-template-btn"]').exists()).toBeTruthy();
- });
+ wrapper.find('[data-test-subj="settings-plus-in-circle"]').last().simulate('click');
+ await waitFor(() =>
+ expect(wrapper.find('[data-test-subj="create-template-btn"]').exists()).toBeTruthy()
+ );
});
test('it renders Open timeline btn', async () => {
- await waitFor(() => {
- wrapper.find('[data-test-subj="settings-plus-in-circle"]').last().simulate('click');
- expect(wrapper.find('[data-test-subj="open-timeline-button"]').exists()).toBeTruthy();
- });
+ wrapper.find('[data-test-subj="settings-plus-in-circle"]').last().simulate('click');
+ await waitFor(() =>
+ expect(wrapper.find('[data-test-subj="open-timeline-button"]').exists()).toBeTruthy()
+ );
});
});
@@ -113,24 +139,86 @@ describe.skip('AddTimelineButton', () => {
});
test('it renders create timeline btn', async () => {
- await waitFor(() => {
- wrapper.find('[data-test-subj="settings-plus-in-circle"]').last().simulate('click');
- expect(wrapper.find('[data-test-subj="create-default-btn"]').exists()).toBeTruthy();
- });
+ wrapper.find('[data-test-subj="settings-plus-in-circle"]').last().simulate('click');
+ await waitFor(() =>
+ expect(wrapper.find('[data-test-subj="create-default-btn"]').exists()).toBeTruthy()
+ );
});
test('it renders create timeline template btn', async () => {
- await waitFor(() => {
- wrapper.find('[data-test-subj="settings-plus-in-circle"]').last().simulate('click');
- expect(wrapper.find('[data-test-subj="create-template-btn"]').exists()).toBeTruthy();
- });
+ wrapper.find('[data-test-subj="settings-plus-in-circle"]').last().simulate('click');
+ await waitFor(() =>
+ expect(wrapper.find('[data-test-subj="create-template-btn"]').exists()).toBeTruthy()
+ );
});
test('it renders Open timeline btn', async () => {
+ wrapper.find('[data-test-subj="settings-plus-in-circle"]').last().simulate('click');
+ await waitFor(() =>
+ expect(wrapper.find('[data-test-subj="open-timeline-button"]').exists()).toBeTruthy()
+ );
+ });
+ });
+
+ describe('open modal', () => {
+ beforeEach(() => {
+ (useKibana as jest.Mock).mockReturnValue({
+ services: {
+ application: {
+ getUrlForApp: jest.fn(),
+ capabilities: {
+ siem: {
+ crud: true,
+ },
+ },
+ },
+ },
+ });
+
+ ((useGetAllTimeline as unknown) as jest.Mock).mockReturnValue({
+ fetchAllTimeline: jest.fn(),
+ timelines: getAllTimeline('', mockOpenTimelineQueryResults.timeline ?? []),
+ loading: false,
+ totalCount: mockOpenTimelineQueryResults.totalCount,
+ refetch: jest.fn(),
+ });
+
+ wrapper = mount(
+
+
+
+
+
+ );
+ });
+
+ afterEach(() => {
+ (useKibana as jest.Mock).mockReset();
+ });
+
+ it('should render timelines table', async () => {
+ wrapper.find('[data-test-subj="settings-plus-in-circle"]').last().simulate('click');
await waitFor(() => {
- wrapper.find('[data-test-subj="settings-plus-in-circle"]').last().simulate('click');
expect(wrapper.find('[data-test-subj="open-timeline-button"]').exists()).toBeTruthy();
});
+
+ wrapper.find('[data-test-subj="open-timeline-button"]').first().simulate('click');
+ await waitFor(() => {
+ expect(wrapper.find('[data-test-subj="timelines-table"]').exists()).toBeTruthy();
+ });
+ });
+
+ it('should render correct actions', async () => {
+ wrapper.find('[data-test-subj="settings-plus-in-circle"]').last().simulate('click');
+ await waitFor(() =>
+ expect(wrapper.find('[data-test-subj="open-timeline-button"]').exists()).toBeTruthy()
+ );
+
+ wrapper.find('[data-test-subj="open-timeline-button"]').first().simulate('click');
+ await waitFor(() => {
+ expect(wrapper.find('[data-test-subj="open-duplicate"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="create-from-template"]').exists()).toBeFalsy();
+ });
});
});
});
diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx
index 90b1cf09cb6cd..5ea1b60e4f156 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx
@@ -10,6 +10,7 @@ import React, { useCallback, useMemo, useState } from 'react';
import { OpenTimelineModalButton } from '../../open_timeline/open_timeline_modal/open_timeline_modal_button';
import { OpenTimelineModal } from '../../open_timeline/open_timeline_modal';
+import { ActionTimelineToShow } from '../../open_timeline/types';
import * as i18n from '../../timeline/properties/translations';
import { NewTimeline } from '../../timeline/properties/helpers';
import { NewTemplateTimeline } from '../../timeline/properties/new_template_timeline';
@@ -20,6 +21,8 @@ interface AddTimelineButtonComponentProps {
export const ADD_TIMELINE_BUTTON_CLASS_NAME = 'add-timeline-button';
+const actionTimelineToHide: ActionTimelineToShow[] = ['createFrom'];
+
const AddTimelineButtonComponent: React.FC = ({ timelineId }) => {
const [showActions, setShowActions] = useState(false);
const [showTimelineModal, setShowTimelineModal] = useState(false);
@@ -83,7 +86,9 @@ const AddTimelineButtonComponent: React.FC = ({
- {showTimelineModal ? : null}
+ {showTimelineModal ? (
+
+ ) : null}
>
);
};
diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/index.tsx
index c1b30f3e68cf4..4aa6fd469de26 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/index.tsx
@@ -83,13 +83,15 @@ export const getTimelinesTableColumns = ({
}),
...getExtendedColumns(showExtendedColumns),
...getIconHeaderColumns({ timelineType }),
- ...getActionsColumns({
- actionTimelineToShow,
- deleteTimelines,
- enableExportTimelineDownloader,
- onOpenDeleteTimelineModal,
- onOpenTimeline,
- }),
+ ...(actionTimelineToShow.length
+ ? getActionsColumns({
+ actionTimelineToShow,
+ deleteTimelines,
+ enableExportTimelineDownloader,
+ onOpenDeleteTimelineModal,
+ onOpenTimeline,
+ })
+ : []),
];
};