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) => (