From 1e2617c30feedf1321bcb30306a22dcff28b1963 Mon Sep 17 00:00:00 2001
From: Angela Chuang <6295984+angorayc@users.noreply.github.com>
Date: Mon, 3 Aug 2020 19:07:03 +0100
Subject: [PATCH] fix default timeline's tab (#74051)
---
.../components/open_timeline/index.test.tsx | 172 ++++++++++++++++--
.../open_timeline/use_timeline_types.tsx | 7 +-
2 files changed, 161 insertions(+), 18 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.test.tsx
index 75b6413bf08f9..43fd57fcfc5bf 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.test.tsx
@@ -4,29 +4,48 @@
* you may not use this file except in compliance with the Elastic License.
*/
+/* eslint-disable react/display-name */
+
+import React from 'react';
+import { renderHook, act } from '@testing-library/react-hooks';
import { mount } from 'enzyme';
import { MockedProvider } from 'react-apollo/test-utils';
-import React from 'react';
-
// we don't have the types for waitFor just yet, so using "as waitFor" until when we do
import { wait as waitFor } from '@testing-library/react';
+import { useHistory, useParams } from 'react-router-dom';
+
import '../../../common/mock/match_media';
+import { SecurityPageName } from '../../../app/types';
+import { TimelineType } from '../../../../common/types/timeline';
+
import { TestProviders, apolloClient } from '../../../common/mock/test_providers';
import { mockOpenTimelineQueryResults } from '../../../common/mock/timeline_results';
+import { getTimelineTabsUrl } from '../../../common/components/link_to';
+
import { DEFAULT_SEARCH_RESULTS_PER_PAGE } from '../../pages/timelines_page';
+import { useGetAllTimeline, getAllTimeline } from '../../containers/all';
import { NotePreviews } from './note_previews';
import { OPEN_TIMELINE_CLASS_NAME } from './helpers';
import { StatefulOpenTimeline } from '.';
-import { useGetAllTimeline, getAllTimeline } from '../../containers/all';
+import { TimelineTabsStyle } from './types';
+import {
+ useTimelineTypes,
+ UseTimelineTypesArgs,
+ UseTimelineTypesResult,
+} from './use_timeline_types';
-import { useParams } from 'react-router-dom';
-import { TimelineType } from '../../../../common/types/timeline';
+jest.mock('react-router-dom', () => {
+ const originalModule = jest.requireActual('react-router-dom');
-jest.mock('../../../common/lib/kibana');
-jest.mock('../../../common/components/link_to');
+ return {
+ ...originalModule,
+ useParams: jest.fn(),
+ useHistory: jest.fn(),
+ };
+});
jest.mock('./helpers', () => {
const originalModule = jest.requireActual('./helpers');
@@ -41,24 +60,31 @@ jest.mock('../../containers/all', () => {
return {
...originalModule,
useGetAllTimeline: jest.fn(),
- getAllTimeline: originalModule.getAllTimeline,
};
});
-jest.mock('react-router-dom', () => {
- const originalModule = jest.requireActual('react-router-dom');
+jest.mock('../../../common/lib/kibana');
+jest.mock('../../../common/components/link_to');
+jest.mock('../../../common/components/link_to', () => {
+ const originalModule = jest.requireActual('../../../common/components/link_to');
return {
...originalModule,
- useParams: jest.fn(),
- useHistory: jest.fn().mockReturnValue([]),
+ getTimelineTabsUrl: jest.fn(),
+ useFormatUrl: jest.fn().mockReturnValue({ formatUrl: jest.fn(), search: 'urlSearch' }),
};
});
describe('StatefulOpenTimeline', () => {
const title = 'All Timelines / Open Timelines';
+ let mockHistory: History[];
beforeEach(() => {
- (useParams as jest.Mock).mockReturnValue({ tabName: TimelineType.default });
+ (useParams as jest.Mock).mockReturnValue({
+ tabName: TimelineType.default,
+ pageName: SecurityPageName.timelines,
+ });
+ mockHistory = [];
+ (useHistory as jest.Mock).mockReturnValue(mockHistory);
((useGetAllTimeline as unknown) as jest.Mock).mockReturnValue({
fetchAllTimeline: jest.fn(),
timelines: getAllTimeline(
@@ -71,6 +97,13 @@ describe('StatefulOpenTimeline', () => {
});
});
+ afterEach(() => {
+ (getTimelineTabsUrl as jest.Mock).mockClear();
+ (useParams as jest.Mock).mockClear();
+ (useHistory as jest.Mock).mockClear();
+ mockHistory = [];
+ });
+
test('it has the expected initial state', () => {
const wrapper = mount(
@@ -101,6 +134,109 @@ describe('StatefulOpenTimeline', () => {
});
});
+ describe("Template timelines' tab", () => {
+ test("should land on correct timelines' tab with url timelines/default", () => {
+ const { result } = renderHook(
+ () => useTimelineTypes({ defaultTimelineCount: 0, templateTimelineCount: 0 }),
+ {
+ wrapper: ({ children }) => {children},
+ }
+ );
+
+ expect(result.current.timelineType).toBe(TimelineType.default);
+ });
+
+ test("should land on correct timelines' tab with url timelines/template", () => {
+ (useParams as jest.Mock).mockReturnValue({
+ tabName: TimelineType.template,
+ pageName: SecurityPageName.timelines,
+ });
+
+ const { result } = renderHook(
+ () => useTimelineTypes({ defaultTimelineCount: 0, templateTimelineCount: 0 }),
+ {
+ wrapper: ({ children }) => {children},
+ }
+ );
+
+ expect(result.current.timelineType).toBe(TimelineType.template);
+ });
+
+ test("should land on correct templates' tab after switching tab", () => {
+ (useParams as jest.Mock).mockReturnValue({
+ tabName: TimelineType.template,
+ pageName: SecurityPageName.timelines,
+ });
+
+ const wrapper = mount(
+
+
+
+
+
+ );
+ wrapper
+ .find(`[data-test-subj="timeline-${TimelineTabsStyle.tab}-${TimelineType.template}"]`)
+ .first()
+ .simulate('click');
+ act(() => {
+ expect(history.length).toBeGreaterThan(0);
+ });
+ });
+
+ test("should selecting correct timelines' filter", () => {
+ (useParams as jest.Mock).mockReturnValue({
+ tabName: 'mockTabName',
+ pageName: SecurityPageName.case,
+ });
+
+ const { result } = renderHook(
+ () => useTimelineTypes({ defaultTimelineCount: 0, templateTimelineCount: 0 }),
+ {
+ wrapper: ({ children }) => {children},
+ }
+ );
+
+ expect(result.current.timelineType).toBe(TimelineType.default);
+ });
+
+ test('should not change url after switching filter', () => {
+ (useParams as jest.Mock).mockReturnValue({
+ tabName: 'mockTabName',
+ pageName: SecurityPageName.case,
+ });
+
+ const wrapper = mount(
+
+
+
+
+
+ );
+ wrapper
+ .find(
+ `[data-test-subj="open-timeline-modal-body-${TimelineTabsStyle.filter}-${TimelineType.template}"]`
+ )
+ .first()
+ .simulate('click');
+ act(() => {
+ expect(mockHistory.length).toEqual(0);
+ });
+ });
+ });
+
describe('#onQueryChange', () => {
test('it updates the query state with the expected trimmed value when the user enters a query', () => {
const wrapper = mount(
@@ -482,9 +618,13 @@ describe('StatefulOpenTimeline', () => {
);
- expect(wrapper.find('[data-test-subj="open-timeline-modal-body-filters"]').exists()).toEqual(
- true
- );
+ expect(
+ wrapper
+ .find(
+ `[data-test-subj="open-timeline-modal-body-${TimelineTabsStyle.filter}-${TimelineType.default}"]`
+ )
+ .exists()
+ ).toEqual(true);
});
});
diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_timeline_types.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_timeline_types.tsx
index 55afe845cdfb3..1ffa626b01311 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_timeline_types.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_timeline_types.tsx
@@ -33,7 +33,9 @@ export const useTimelineTypes = ({
const { formatUrl, search: urlSearch } = useFormatUrl(SecurityPageName.timelines);
const { tabName } = useParams<{ pageName: SecurityPageName; tabName: string }>();
const [timelineType, setTimelineTypes] = useState(
- tabName === TimelineType.default || tabName === TimelineType.template ? tabName : null
+ tabName === TimelineType.default || tabName === TimelineType.template
+ ? tabName
+ : TimelineType.default
);
const goToTimeline = useCallback(
@@ -114,6 +116,7 @@ export const useTimelineTypes = ({
{getFilterOrTabs(TimelineTabsStyle.tab).map((tab: TimelineTab) => (
{
return getFilterOrTabs(TimelineTabsStyle.filter).map((tab: TimelineTab) => (