From 46abdbeb91038783c9469e40b7b37a250500d002 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Mon, 28 Sep 2020 07:59:18 -0600 Subject: [PATCH] [Security Solution] [ALL] Eliminates all console.error and console.warn from Jest output (#78523) --- .../components/add_comment/index.test.tsx | 3 +- .../cases/components/all_cases/index.test.tsx | 267 +++++----- .../components/all_cases_modal/index.test.tsx | 45 +- .../cases/components/case_view/index.test.tsx | 161 +++--- .../cases/components/create/index.test.tsx | 22 +- .../components/edit_connector/index.test.tsx | 37 +- .../cases/components/tag_list/index.test.tsx | 43 +- .../user_action_tree/index.test.tsx | 261 +++++----- .../index.test.tsx | 59 +-- .../autocomplete/field_value_lists.test.tsx | 3 +- .../components/autocomplete/helpers.test.ts | 4 +- .../components/charts/barchart.test.tsx | 19 +- .../charts/draggable_legend.test.tsx | 15 +- .../charts/draggable_legend_item.test.tsx | 14 +- .../drag_and_drop/draggable_wrapper.test.tsx | 16 +- .../draggable_wrapper_hover_content.test.tsx | 16 - .../components/endpoint/link_to_app.test.tsx | 26 +- .../event_details/event_details.test.tsx | 11 +- .../components/events_viewer/index.test.tsx | 3 +- .../add_exception_modal/index.test.tsx | 35 +- .../exceptions/builder/index.test.tsx | 2 +- .../edit_exception_modal/index.test.tsx | 37 +- .../components/last_event_time/index.test.tsx | 4 +- .../common/components/link_icon/index.tsx | 57 ++- .../components/ml/entity_draggable.test.tsx | 1 - .../ml/score/anomaly_score.test.tsx | 1 - .../ml/score/anomaly_scores.test.tsx | 1 - .../get_anomalies_host_table_columns.test.tsx | 1 - ...t_anomalies_network_table_columns.test.tsx | 1 - .../ml_popover/jobs_table/job_switch.test.tsx | 16 +- .../ml_popover/jobs_table/jobs_table.test.tsx | 7 +- .../components/query_bar/index.test.tsx | 43 +- .../components/sourcerer/index.test.tsx | 9 +- .../common/components/tables/helpers.test.tsx | 1 - .../common/components/top_n/index.test.tsx | 24 +- .../common/components/top_n/top_n.test.tsx | 56 +-- .../components/url_state/index.test.tsx | 3 +- .../common/lib/kibana/kibana_react.mock.ts | 53 +- .../public/common/mock/formatted_relative.ts | 15 + .../public/common/mock/react_beautiful_dnd.ts | 53 ++ .../public/common/mock/test_providers.tsx | 2 +- .../rules/step_about_rule/index.test.tsx | 137 ++--- .../form.test.tsx | 4 +- .../modal.test.tsx | 16 +- .../detection_engine.test.tsx | 9 +- .../detection_engine/rules/all/index.test.tsx | 23 +- .../rules/details/index.test.tsx | 8 +- .../detection_engine/rules/helpers.test.tsx | 4 +- .../first_last_seen_host/index.test.tsx | 77 +-- .../store/endpoint_pagination.test.ts | 4 + .../endpoint_hosts/store/middleware.test.ts | 3 +- .../pages/endpoint_hosts/view/index.test.tsx | 23 +- .../public/management/pages/index.test.tsx | 11 + .../public/management/pages/index.tsx | 14 +- .../public/network/pages/network.test.tsx | 26 +- .../alerts_by_category/index.test.tsx | 4 +- .../endpoint_overview/index.test.tsx | 1 + .../public/resolver/view/use_camera.test.tsx | 18 +- .../fields_browser/category.test.tsx | 1 - .../fields_browser/field_name.test.tsx | 16 +- .../components/fields_browser/index.test.tsx | 60 +-- .../components/flyout/index.test.tsx | 1 + .../components/notes/note_card/index.test.tsx | 1 + .../notes/note_card/note_card_header.test.tsx | 1 + .../notes/note_card/note_created.test.tsx | 1 + .../notes/note_cards/index.test.tsx | 1 + .../components/open_timeline/index.test.tsx | 4 +- .../note_previews/index.test.tsx | 1 + .../note_previews/note_preview.test.tsx | 1 + .../open_timeline/open_timeline.test.tsx | 10 +- .../open_timeline_modal_body.test.tsx | 2 +- .../open_timeline_modal_button.test.tsx | 3 +- .../timelines_table/common_columns.test.tsx | 1 + .../header_tooltip_content/index.tsx | 3 +- .../components/timeline/body/index.test.tsx | 19 +- .../timeline/properties/index.test.tsx | 28 +- .../properties/use_create_timeline.test.tsx | 4 +- .../timeline/query_bar/index.test.tsx | 16 - .../selectable_timeline/index.test.tsx | 33 +- .../public/timelines/containers/api.test.ts | 472 ++++-------------- .../timeline/epic_local_storage.test.tsx | 2 +- 81 files changed, 1126 insertions(+), 1384 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/mock/formatted_relative.ts create mode 100644 x-pack/plugins/security_solution/public/common/mock/react_beautiful_dnd.ts diff --git a/x-pack/plugins/security_solution/public/cases/components/add_comment/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/add_comment/index.test.tsx index a800bd690f710..a85d7a310bc06 100644 --- a/x-pack/plugins/security_solution/public/cases/components/add_comment/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/add_comment/index.test.tsx @@ -17,8 +17,7 @@ import { usePostComment } from '../../containers/use_post_comment'; import { useForm } from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form'; import { useFormData } from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data'; -// 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 { waitFor } from '@testing-library/react'; jest.mock( '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form' diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx index e6e0823214195..e301e80c9561d 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { mount } from 'enzyme'; import moment from 'moment-timezone'; - +import { waitFor } from '@testing-library/react'; import '../../../common/mock/match_media'; import { AllCases } from '.'; import { TestProviders } from '../../../common/mock'; @@ -85,16 +85,6 @@ describe('AllCases', () => { let navigateToApp: jest.Mock; - /* eslint-disable no-console */ - // Silence until enzyme fixed to use ReactTestUtils.act() - const originalError = console.error; - beforeAll(() => { - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - }); - /* eslint-enable no-console */ beforeEach(() => { jest.clearAllMocks(); navigateToApp = jest.fn(); @@ -106,36 +96,38 @@ describe('AllCases', () => { moment.tz.setDefault('UTC'); }); - it('should render AllCases', () => { + it('should render AllCases', async () => { const wrapper = mount( ); - expect(wrapper.find(`a[data-test-subj="case-details-link"]`).first().prop('href')).toEqual( - `/${useGetCasesMockState.data.cases[0].id}` - ); - expect(wrapper.find(`a[data-test-subj="case-details-link"]`).first().text()).toEqual( - useGetCasesMockState.data.cases[0].title - ); - expect( - wrapper.find(`span[data-test-subj="case-table-column-tags-0"]`).first().prop('title') - ).toEqual(useGetCasesMockState.data.cases[0].tags[0]); - expect(wrapper.find(`[data-test-subj="case-table-column-createdBy"]`).first().text()).toEqual( - useGetCasesMockState.data.cases[0].createdBy.fullName - ); - expect( - wrapper - .find(`[data-test-subj="case-table-column-createdAt"]`) - .first() - .childAt(0) - .prop('value') - ).toBe(useGetCasesMockState.data.cases[0].createdAt); - expect(wrapper.find(`[data-test-subj="case-table-case-count"]`).first().text()).toEqual( - 'Showing 10 cases' - ); + await waitFor(() => { + expect(wrapper.find(`a[data-test-subj="case-details-link"]`).first().prop('href')).toEqual( + `/${useGetCasesMockState.data.cases[0].id}` + ); + expect(wrapper.find(`a[data-test-subj="case-details-link"]`).first().text()).toEqual( + useGetCasesMockState.data.cases[0].title + ); + expect( + wrapper.find(`span[data-test-subj="case-table-column-tags-0"]`).first().prop('title') + ).toEqual(useGetCasesMockState.data.cases[0].tags[0]); + expect(wrapper.find(`[data-test-subj="case-table-column-createdBy"]`).first().text()).toEqual( + useGetCasesMockState.data.cases[0].createdBy.fullName + ); + expect( + wrapper + .find(`[data-test-subj="case-table-column-createdAt"]`) + .first() + .childAt(0) + .prop('value') + ).toBe(useGetCasesMockState.data.cases[0].createdAt); + expect(wrapper.find(`[data-test-subj="case-table-case-count"]`).first().text()).toEqual( + 'Showing 10 cases' + ); + }); }); - it('should render empty fields', () => { + it('should render empty fields', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, data: { @@ -166,53 +158,63 @@ describe('AllCases', () => { expect(column.find('.euiTableRowCell--hideForDesktop').text()).toEqual(columnName); expect(column.find('span').text()).toEqual(emptyTag); }; - getCasesColumns([], 'open', false).map((i, key) => i.name != null && checkIt(`${i.name}`, key)); + await waitFor(() => { + getCasesColumns([], 'open', false).map( + (i, key) => i.name != null && checkIt(`${i.name}`, key) + ); + }); }); - it('should not render case link or actions on modal=true', () => { + it('should not render case link or actions on modal=true', async () => { const wrapper = mount( ); - const checkIt = (columnName: string) => { - expect(columnName).not.toEqual(i18n.ACTIONS); - }; - getCasesColumns([], 'open', true).map((i, key) => i.name != null && checkIt(`${i.name}`)); - expect(wrapper.find(`a[data-test-subj="case-details-link"]`).exists()).toBeFalsy(); + await waitFor(() => { + const checkIt = (columnName: string) => { + expect(columnName).not.toEqual(i18n.ACTIONS); + }; + getCasesColumns([], 'open', true).map((i, key) => i.name != null && checkIt(`${i.name}`)); + expect(wrapper.find(`a[data-test-subj="case-details-link"]`).exists()).toBeFalsy(); + }); }); - it('should tableHeaderSortButton AllCases', () => { + it('should tableHeaderSortButton AllCases', async () => { const wrapper = mount( ); - wrapper.find('[data-test-subj="tableHeaderSortButton"]').first().simulate('click'); - expect(setQueryParams).toBeCalledWith({ - page: 1, - perPage: 5, - sortField: 'createdAt', - sortOrder: 'asc', + await waitFor(() => { + wrapper.find('[data-test-subj="tableHeaderSortButton"]').first().simulate('click'); + expect(setQueryParams).toBeCalledWith({ + page: 1, + perPage: 5, + sortField: 'createdAt', + sortOrder: 'asc', + }); }); }); - it('closes case when row action icon clicked', () => { + it('closes case when row action icon clicked', async () => { const wrapper = mount( ); - wrapper.find('[data-test-subj="action-close"]').first().simulate('click'); - const firstCase = useGetCasesMockState.data.cases[0]; - expect(dispatchUpdateCaseProperty).toBeCalledWith({ - caseId: firstCase.id, - updateKey: 'status', - updateValue: 'closed', - refetchCasesStatus: fetchCasesStatus, - version: firstCase.version, + await waitFor(() => { + wrapper.find('[data-test-subj="action-close"]').first().simulate('click'); + const firstCase = useGetCasesMockState.data.cases[0]; + expect(dispatchUpdateCaseProperty).toBeCalledWith({ + caseId: firstCase.id, + updateKey: 'status', + updateValue: 'closed', + refetchCasesStatus: fetchCasesStatus, + version: firstCase.version, + }); }); }); - it('opens case when row action icon clicked', () => { + it('opens case when row action icon clicked', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, filterOptions: { ...defaultGetCases.filterOptions, status: 'closed' }, @@ -223,17 +225,19 @@ describe('AllCases', () => { ); - wrapper.find('[data-test-subj="action-open"]').first().simulate('click'); - const firstCase = useGetCasesMockState.data.cases[0]; - expect(dispatchUpdateCaseProperty).toBeCalledWith({ - caseId: firstCase.id, - updateKey: 'status', - updateValue: 'open', - refetchCasesStatus: fetchCasesStatus, - version: firstCase.version, + await waitFor(() => { + wrapper.find('[data-test-subj="action-open"]').first().simulate('click'); + const firstCase = useGetCasesMockState.data.cases[0]; + expect(dispatchUpdateCaseProperty).toBeCalledWith({ + caseId: firstCase.id, + updateKey: 'status', + updateValue: 'open', + refetchCasesStatus: fetchCasesStatus, + version: firstCase.version, + }); }); }); - it('Bulk delete', () => { + it('Bulk delete', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, selectedCases: useGetCasesMockState.data.cases, @@ -254,21 +258,23 @@ describe('AllCases', () => { ); - wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); - wrapper.find('[data-test-subj="cases-bulk-delete-button"]').first().simulate('click'); - expect(handleToggleModal).toBeCalled(); + await waitFor(() => { + wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); + wrapper.find('[data-test-subj="cases-bulk-delete-button"]').first().simulate('click'); + expect(handleToggleModal).toBeCalled(); - wrapper - .find( - '[data-test-subj="confirm-delete-case-modal"] [data-test-subj="confirmModalConfirmButton"]' - ) - .last() - .simulate('click'); - expect(handleOnDeleteConfirm.mock.calls[0][0]).toStrictEqual( - useGetCasesMockState.data.cases.map(({ id }) => ({ id })) - ); + wrapper + .find( + '[data-test-subj="confirm-delete-case-modal"] [data-test-subj="confirmModalConfirmButton"]' + ) + .last() + .simulate('click'); + expect(handleOnDeleteConfirm.mock.calls[0][0]).toStrictEqual( + useGetCasesMockState.data.cases.map(({ id }) => ({ id })) + ); + }); }); - it('Bulk close status update', () => { + it('Bulk close status update', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, selectedCases: useGetCasesMockState.data.cases, @@ -279,11 +285,13 @@ describe('AllCases', () => { ); - wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); - wrapper.find('[data-test-subj="cases-bulk-close-button"]').first().simulate('click'); - expect(updateBulkStatus).toBeCalledWith(useGetCasesMockState.data.cases, 'closed'); + await waitFor(() => { + wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); + wrapper.find('[data-test-subj="cases-bulk-close-button"]').first().simulate('click'); + expect(updateBulkStatus).toBeCalledWith(useGetCasesMockState.data.cases, 'closed'); + }); }); - it('Bulk open status update', () => { + it('Bulk open status update', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, selectedCases: useGetCasesMockState.data.cases, @@ -298,11 +306,13 @@ describe('AllCases', () => { ); - wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); - wrapper.find('[data-test-subj="cases-bulk-open-button"]').first().simulate('click'); - expect(updateBulkStatus).toBeCalledWith(useGetCasesMockState.data.cases, 'open'); + await waitFor(() => { + wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); + wrapper.find('[data-test-subj="cases-bulk-open-button"]').first().simulate('click'); + expect(updateBulkStatus).toBeCalledWith(useGetCasesMockState.data.cases, 'open'); + }); }); - it('isDeleted is true, refetch', () => { + it('isDeleted is true, refetch', async () => { useDeleteCasesMock.mockReturnValue({ ...defaultDeleteCases, isDeleted: true, @@ -313,11 +323,13 @@ describe('AllCases', () => { ); - expect(refetchCases).toBeCalled(); - expect(fetchCasesStatus).toBeCalled(); - expect(dispatchResetIsDeleted).toBeCalled(); + await waitFor(() => { + expect(refetchCases).toBeCalled(); + expect(fetchCasesStatus).toBeCalled(); + expect(dispatchResetIsDeleted).toBeCalled(); + }); }); - it('isUpdated is true, refetch', () => { + it('isUpdated is true, refetch', async () => { useUpdateCasesMock.mockReturnValue({ ...defaultUpdateCases, isUpdated: true, @@ -328,42 +340,51 @@ describe('AllCases', () => { ); - expect(refetchCases).toBeCalled(); - expect(fetchCasesStatus).toBeCalled(); - expect(dispatchResetIsUpdated).toBeCalled(); + await waitFor(() => { + expect(refetchCases).toBeCalled(); + expect(fetchCasesStatus).toBeCalled(); + expect(dispatchResetIsUpdated).toBeCalled(); + }); }); - it('should not render header when modal=true', () => { + it('should not render header when modal=true', async () => { const wrapper = mount( ); - - expect(wrapper.find('[data-test-subj="all-cases-header"]').exists()).toBe(false); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="all-cases-header"]').exists()).toBe(false); + }); }); - it('should not render table utility bar when modal=true', () => { + it('should not render table utility bar when modal=true', async () => { const wrapper = mount( ); - - expect(wrapper.find('[data-test-subj="case-table-utility-bar-actions"]').exists()).toBe(false); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="case-table-utility-bar-actions"]').exists()).toBe( + false + ); + }); }); - it('case table should not be selectable when modal=true', () => { + it('case table should not be selectable when modal=true', async () => { const wrapper = mount( ); - - expect(wrapper.find('[data-test-subj="cases-table"]').first().prop('isSelectable')).toBe(false); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="cases-table"]').first().prop('isSelectable')).toBe( + false + ); + }); }); - it('should call onRowClick with no cases and modal=true', () => { + it('should call onRowClick with no cases and modal=true', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, data: { @@ -378,12 +399,13 @@ describe('AllCases', () => { ); - - wrapper.find('[data-test-subj="cases-table-add-case"]').first().simulate('click'); - expect(onRowClick).toHaveBeenCalled(); + await waitFor(() => { + wrapper.find('[data-test-subj="cases-table-add-case"]').first().simulate('click'); + expect(onRowClick).toHaveBeenCalled(); + }); }); - it('should call navigateToApp with no cases and modal=false', () => { + it('should call navigateToApp with no cases and modal=false', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, data: { @@ -398,30 +420,33 @@ describe('AllCases', () => { ); - - wrapper.find('[data-test-subj="cases-table-add-case"]').first().simulate('click'); - expect(navigateToApp).toHaveBeenCalledWith('securitySolution:case', { path: '/create' }); + await waitFor(() => { + wrapper.find('[data-test-subj="cases-table-add-case"]').first().simulate('click'); + expect(navigateToApp).toHaveBeenCalledWith('securitySolution:case', { path: '/create' }); + }); }); - it('should call onRowClick when clicking a case with modal=true', () => { + it('should call onRowClick when clicking a case with modal=true', async () => { const wrapper = mount( ); - - wrapper.find('[data-test-subj="cases-table-row-1"]').first().simulate('click'); - expect(onRowClick).toHaveBeenCalledWith('1'); + await waitFor(() => { + wrapper.find('[data-test-subj="cases-table-row-1"]').first().simulate('click'); + expect(onRowClick).toHaveBeenCalledWith('1'); + }); }); - it('should NOT call onRowClick when clicking a case with modal=true', () => { + it('should NOT call onRowClick when clicking a case with modal=true', async () => { const wrapper = mount( ); - - wrapper.find('[data-test-subj="cases-table-row-1"]').first().simulate('click'); - expect(onRowClick).not.toHaveBeenCalled(); + await waitFor(() => { + wrapper.find('[data-test-subj="cases-table-row-1"]').first().simulate('click'); + expect(onRowClick).not.toHaveBeenCalled(); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases_modal/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases_modal/index.test.tsx index b93de014f5c18..725759068a3ea 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases_modal/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases_modal/index.test.tsx @@ -5,6 +5,7 @@ */ import { mount } from 'enzyme'; import React from 'react'; +import { waitFor } from '@testing-library/react'; import '../../../common/mock/match_media'; import { AllCasesModal } from '.'; import { TestProviders } from '../../../common/mock'; @@ -96,16 +97,6 @@ describe('AllCasesModal', () => { dispatchResetIsUpdated, updateBulkStatus, }; - /* eslint-disable no-console */ - // Silence until enzyme fixed to use ReactTestUtils.act() - const originalError = console.error; - beforeAll(() => { - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - }); - /* eslint-enable no-console */ beforeEach(() => { jest.resetAllMocks(); useUpdateCasesMock.mockImplementation(() => defaultUpdateCases); @@ -114,41 +105,49 @@ describe('AllCasesModal', () => { useGetCasesStatusMock.mockImplementation(() => defaultCasesStatus); }); - it('renders with unselectable rows', () => { + it('renders with unselectable rows', async () => { const wrapper = mount( ); - expect(wrapper.find(`[data-test-subj='all-cases-modal']`).exists()).toBeTruthy(); - expect(wrapper.find(EuiTableRow).first().prop('isSelectable')).toBeFalsy(); + await waitFor(() => { + expect(wrapper.find(`[data-test-subj='all-cases-modal']`).exists()).toBeTruthy(); + expect(wrapper.find(EuiTableRow).first().prop('isSelectable')).toBeFalsy(); + }); }); - it('does not render modal if showCaseModal: false', () => { + it('does not render modal if showCaseModal: false', async () => { const wrapper = mount( ); - expect(wrapper.find(`[data-test-subj='all-cases-modal']`).exists()).toBeFalsy(); + await waitFor(() => { + expect(wrapper.find(`[data-test-subj='all-cases-modal']`).exists()).toBeFalsy(); + }); }); - it('onRowClick called when row is clicked', () => { + it('onRowClick called when row is clicked', async () => { const wrapper = mount( ); - const firstRow = wrapper.find(EuiTableRow).first(); - firstRow.simulate('click'); - expect(onRowClick.mock.calls[0][0]).toEqual(basicCaseId); + await waitFor(() => { + const firstRow = wrapper.find(EuiTableRow).first(); + firstRow.simulate('click'); + expect(onRowClick.mock.calls[0][0]).toEqual(basicCaseId); + }); }); - it('Closing modal calls onCloseCaseModal', () => { + it('Closing modal calls onCloseCaseModal', async () => { const wrapper = mount( ); - const modalClose = wrapper.find('.euiModal__closeIcon').first(); - modalClose.simulate('click'); - expect(onCloseCaseModal).toBeCalled(); + await waitFor(() => { + const modalClose = wrapper.find('.euiModal__closeIcon').first(); + modalClose.simulate('click'); + expect(onCloseCaseModal).toBeCalled(); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/cases/components/case_view/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/case_view/index.test.tsx index 246df1c94b817..3859b4527991b 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_view/index.test.tsx @@ -15,9 +15,7 @@ import { TestProviders } from '../../../common/mock'; import { useUpdateCase } from '../../containers/use_update_case'; import { useGetCase } from '../../containers/use_get_case'; import { useGetCaseUserActions } from '../../containers/use_get_case_user_actions'; - -// 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 { waitFor } from '@testing-library/react'; import { useConnectors } from '../../containers/configure/use_connectors'; import { connectorsMock } from '../../containers/configure/mock'; @@ -29,6 +27,7 @@ jest.mock('../../containers/use_get_case_user_actions'); jest.mock('../../containers/use_get_case'); jest.mock('../../containers/configure/use_connectors'); jest.mock('../../containers/use_post_push_to_service'); +jest.mock('../user_action_tree/user_action_timestamp'); const useUpdateCaseMock = useUpdateCase as jest.Mock; const useGetCaseUserActionsMock = useGetCaseUserActions as jest.Mock; @@ -63,16 +62,6 @@ describe('CaseView ', () => { updateCase, fetchCase, }; - /* eslint-disable no-console */ - // Silence until enzyme fixed to use ReactTestUtils.act() - const originalError = console.error; - beforeAll(() => { - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - }); - /* eslint-enable no-console */ const defaultUpdateCaseState = { isLoading: false, @@ -96,6 +85,7 @@ describe('CaseView ', () => { beforeEach(() => { jest.resetAllMocks(); useUpdateCaseMock.mockImplementation(() => defaultUpdateCaseState); + jest.spyOn(routeData, 'useLocation').mockReturnValue(mockLocation); useGetCaseUserActionsMock.mockImplementation(() => defaultUseGetCaseUserActions); usePostPushToServiceMock.mockImplementation(() => ({ isLoading: false, postPushToService })); @@ -191,7 +181,7 @@ describe('CaseView ', () => { }); }); - it('should display EditableTitle isLoading', () => { + it('should display EditableTitle isLoading', async () => { useUpdateCaseMock.mockImplementation(() => ({ ...defaultUpdateCaseState, isLoading: true, @@ -204,13 +194,17 @@ describe('CaseView ', () => { ); - expect(wrapper.find('[data-test-subj="editable-title-loading"]').first().exists()).toBeTruthy(); - expect( - wrapper.find('[data-test-subj="editable-title-edit-icon"]').first().exists() - ).toBeFalsy(); + await waitFor(() => { + expect( + wrapper.find('[data-test-subj="editable-title-loading"]').first().exists() + ).toBeTruthy(); + expect( + wrapper.find('[data-test-subj="editable-title-edit-icon"]').first().exists() + ).toBeFalsy(); + }); }); - it('should display Toggle Status isLoading', () => { + it('should display Toggle Status isLoading', async () => { useUpdateCaseMock.mockImplementation(() => ({ ...defaultUpdateCaseState, isLoading: true, @@ -223,12 +217,14 @@ describe('CaseView ', () => { ); - expect( - wrapper.find('[data-test-subj="toggle-case-status"]').first().prop('isLoading') - ).toBeTruthy(); + await waitFor(() => { + expect( + wrapper.find('[data-test-subj="toggle-case-status"]').first().prop('isLoading') + ).toBeTruthy(); + }); }); - it('should display description isLoading', () => { + it('should display description isLoading', async () => { useUpdateCaseMock.mockImplementation(() => ({ ...defaultUpdateCaseState, isLoading: true, @@ -241,21 +237,25 @@ describe('CaseView ', () => { ); - expect( - wrapper - .find('[data-test-subj="description-action"] [data-test-subj="user-action-title-loading"]') - .first() - .exists() - ).toBeTruthy(); - expect( - wrapper - .find('[data-test-subj="description-action"] [data-test-subj="property-actions"]') - .first() - .exists() - ).toBeFalsy(); + await waitFor(() => { + expect( + wrapper + .find( + '[data-test-subj="description-action"] [data-test-subj="user-action-title-loading"]' + ) + .first() + .exists() + ).toBeTruthy(); + expect( + wrapper + .find('[data-test-subj="description-action"] [data-test-subj="property-actions"]') + .first() + .exists() + ).toBeFalsy(); + }); }); - it('should display tags isLoading', () => { + it('should display tags isLoading', async () => { useUpdateCaseMock.mockImplementation(() => ({ ...defaultUpdateCaseState, isLoading: true, @@ -268,16 +268,18 @@ describe('CaseView ', () => { ); - expect( - wrapper - .find('[data-test-subj="case-view-tag-list"] [data-test-subj="tag-list-loading"]') - .first() - .exists() - ).toBeTruthy(); - expect(wrapper.find('[data-test-subj="tag-list-edit"]').first().exists()).toBeFalsy(); + await waitFor(() => { + expect( + wrapper + .find('[data-test-subj="case-view-tag-list"] [data-test-subj="tag-list-loading"]') + .first() + .exists() + ).toBeTruthy(); + expect(wrapper.find('[data-test-subj="tag-list-edit"]').first().exists()).toBeFalsy(); + }); }); - it('should update title', () => { + it('should update title', async () => { const wrapper = mount( @@ -285,21 +287,23 @@ describe('CaseView ', () => { ); - const newTitle = 'The new title'; - wrapper.find(`[data-test-subj="editable-title-edit-icon"]`).first().simulate('click'); - wrapper.update(); - wrapper - .find(`[data-test-subj="editable-title-input-field"]`) - .last() - .simulate('change', { target: { value: newTitle } }); - - wrapper.update(); - wrapper.find(`[data-test-subj="editable-title-submit-btn"]`).first().simulate('click'); - - wrapper.update(); - const updateObject = updateCaseProperty.mock.calls[0][0]; - expect(updateObject.updateKey).toEqual('title'); - expect(updateObject.updateValue).toEqual(newTitle); + await waitFor(() => { + const newTitle = 'The new title'; + wrapper.find(`[data-test-subj="editable-title-edit-icon"]`).first().simulate('click'); + wrapper.update(); + wrapper + .find(`[data-test-subj="editable-title-input-field"]`) + .last() + .simulate('change', { target: { value: newTitle } }); + + wrapper.update(); + wrapper.find(`[data-test-subj="editable-title-submit-btn"]`).first().simulate('click'); + + wrapper.update(); + const updateObject = updateCaseProperty.mock.calls[0][0]; + expect(updateObject.updateKey).toEqual('title'); + expect(updateObject.updateValue).toEqual(newTitle); + }); }); it('should push updates on button click', async () => { @@ -329,7 +333,7 @@ describe('CaseView ', () => { }); }); - it('should return null if error', () => { + it('should return null if error', async () => { (useGetCase as jest.Mock).mockImplementation(() => ({ ...defaultGetCase, isError: true, @@ -346,10 +350,12 @@ describe('CaseView ', () => { ); - expect(wrapper).toEqual({}); + await waitFor(() => { + expect(wrapper).toEqual({}); + }); }); - it('should return spinner if loading', () => { + it('should return spinner if loading', async () => { (useGetCase as jest.Mock).mockImplementation(() => ({ ...defaultGetCase, isLoading: true, @@ -366,10 +372,12 @@ describe('CaseView ', () => { ); - expect(wrapper.find('[data-test-subj="case-view-loading"]').exists()).toBeTruthy(); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="case-view-loading"]').exists()).toBeTruthy(); + }); }); - it('should return case view when data is there', () => { + it('should return case view when data is there', async () => { (useGetCase as jest.Mock).mockImplementation(() => defaultGetCase); const wrapper = mount( @@ -383,10 +391,12 @@ describe('CaseView ', () => { ); - expect(wrapper.find('[data-test-subj="case-view-title"]').exists()).toBeTruthy(); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="case-view-title"]').exists()).toBeTruthy(); + }); }); - it('should refresh data on refresh', () => { + it('should refresh data on refresh', async () => { (useGetCase as jest.Mock).mockImplementation(() => defaultGetCase); const wrapper = mount( @@ -400,12 +410,14 @@ describe('CaseView ', () => { ); - wrapper.find('[data-test-subj="case-refresh"]').first().simulate('click'); - expect(fetchCaseUserActions).toBeCalledWith(caseProps.caseData.id); - expect(fetchCase).toBeCalled(); + await waitFor(() => { + wrapper.find('[data-test-subj="case-refresh"]').first().simulate('click'); + expect(fetchCaseUserActions).toBeCalledWith(caseProps.caseData.id); + expect(fetchCase).toBeCalled(); + }); }); - it('should disable the push button when connector is invalid', () => { + it('should disable the push button when connector is invalid', async () => { useGetCaseUserActionsMock.mockImplementation(() => ({ ...defaultUseGetCaseUserActions, hasDataToPush: true, @@ -424,10 +436,11 @@ describe('CaseView ', () => { ); - - expect( - wrapper.find('button[data-test-subj="push-to-external-service"]').first().prop('disabled') - ).toBeTruthy(); + await waitFor(() => { + expect( + wrapper.find('button[data-test-subj="push-to-external-service"]').first().prop('disabled') + ).toBeTruthy(); + }); }); it('should revert to the initial connector in case of failure', async () => { diff --git a/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx index 4f1e45ae7c115..d27f00aacff2c 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx @@ -6,7 +6,6 @@ import React from 'react'; import { mount } from 'enzyme'; - import { Create } from '.'; import { TestProviders } from '../../../common/mock'; import { getFormMock } from '../__mock__/form'; @@ -19,9 +18,16 @@ import { useGetTags } from '../../containers/use_get_tags'; import { useForm } from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form'; import { useFormData } from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data'; -// 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 { waitFor } from '@testing-library/react'; +jest.mock('@elastic/eui', () => { + const original = jest.requireActual('@elastic/eui'); + return { + ...original, + // eslint-disable-next-line react/display-name + EuiFieldText: () => , + }; +}); jest.mock('../../../timelines/components/timeline/insert_timeline_popover/use_insert_timeline'); jest.mock('../../containers/use_post_case'); @@ -74,16 +80,6 @@ const defaultPostCase = { postCase, }; describe('Create case', () => { - // Suppress warnings about "noSuggestions" prop - /* eslint-disable no-console */ - const originalError = console.error; - beforeAll(() => { - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - }); - /* eslint-enable no-console */ const fetchTags = jest.fn(); const formHookMock = getFormMock(sampleData); beforeEach(() => { diff --git a/x-pack/plugins/security_solution/public/cases/components/edit_connector/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/edit_connector/index.test.tsx index e531b71e8c90c..12d549a2f71a9 100644 --- a/x-pack/plugins/security_solution/public/cases/components/edit_connector/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/edit_connector/index.test.tsx @@ -11,9 +11,8 @@ import { EditConnector } from './index'; import { getFormMock, useFormMock } from '../__mock__/form'; import { TestProviders } from '../../../common/mock'; import { connectorsMock } from '../../containers/configure/mock'; -// 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 { act } from 'react-dom/test-utils'; +import { waitFor } from '@testing-library/react'; + jest.mock( '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form' ); @@ -67,10 +66,8 @@ describe('EditConnector ', () => { expect(wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().exists()).toBeTruthy(); - await act(async () => { - wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().simulate('click'); - await waitFor(() => expect(onSubmit.mock.calls[0][0]).toBe(sampleConnector)); - }); + wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().simulate('click'); + await waitFor(() => expect(onSubmit.mock.calls[0][0]).toBe(sampleConnector)); }); it('Revert to initial external service on error', async () => { @@ -90,12 +87,10 @@ describe('EditConnector ', () => { expect(wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().exists()).toBeTruthy(); - await act(async () => { - wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().simulate('click'); - await waitFor(() => { - wrapper.update(); - expect(formHookMock.setFieldValue).toHaveBeenCalledWith('connector', 'none'); - }); + wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().simulate('click'); + await waitFor(() => { + wrapper.update(); + expect(formHookMock.setFieldValue).toHaveBeenCalledWith('connector', 'none'); }); }); @@ -114,15 +109,13 @@ describe('EditConnector ', () => { wrapper.find('button[data-test-subj="dropdown-connector-servicenow-2"]').simulate('click'); wrapper.update(); - await act(async () => { - wrapper.find(`[data-test-subj="edit-connectors-cancel"]`).last().simulate('click'); - await waitFor(() => { - wrapper.update(); - expect(formHookMock.setFieldValue).toBeCalledWith( - 'connector', - defaultProps.selectedConnector - ); - }); + wrapper.find(`[data-test-subj="edit-connectors-cancel"]`).last().simulate('click'); + await waitFor(() => { + wrapper.update(); + expect(formHookMock.setFieldValue).toBeCalledWith( + 'connector', + defaultProps.selectedConnector + ); }); }); diff --git a/x-pack/plugins/security_solution/public/cases/components/tag_list/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/tag_list/index.test.tsx index a60167a18762f..013f7bd0a9ba7 100644 --- a/x-pack/plugins/security_solution/public/cases/components/tag_list/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/tag_list/index.test.tsx @@ -6,13 +6,11 @@ import React from 'react'; import { mount } from 'enzyme'; -import { act } from 'react-dom/test-utils'; import { TagList } from '.'; import { getFormMock } from '../__mock__/form'; import { TestProviders } from '../../../common/mock'; -// 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 { waitFor } from '@testing-library/react'; import { useForm } from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form'; import { useGetTags } from '../../containers/use_get_tags'; @@ -27,6 +25,14 @@ jest.mock( children({ tags: ['rad', 'dude'] }), }) ); +jest.mock('@elastic/eui', () => { + const original = jest.requireActual('@elastic/eui'); + return { + ...original, + // eslint-disable-next-line react/display-name + EuiFieldText: () => , + }; +}); const onSubmit = jest.fn(); const defaultProps = { disabled: false, @@ -36,16 +42,6 @@ const defaultProps = { }; describe('TagList ', () => { - // Suppress warnings about "noSuggestions" prop - /* eslint-disable no-console */ - const originalError = console.error; - beforeAll(() => { - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - }); - /* eslint-enable no-console */ const sampleTags = ['coke', 'pepsi']; const fetchTags = jest.fn(); const formHookMock = getFormMock({ tags: sampleTags }); @@ -78,10 +74,8 @@ describe('TagList ', () => { ); wrapper.find(`[data-test-subj="tag-list-edit-button"]`).last().simulate('click'); - await act(async () => { - wrapper.find(`[data-test-subj="edit-tags-submit"]`).last().simulate('click'); - await waitFor(() => expect(onSubmit).toBeCalledWith(sampleTags)); - }); + wrapper.find(`[data-test-subj="edit-tags-submit"]`).last().simulate('click'); + await waitFor(() => expect(onSubmit).toBeCalledWith(sampleTags)); }); it('Tag options render with new tags added', () => { @@ -96,7 +90,7 @@ describe('TagList ', () => { ).toEqual([{ label: 'coke' }, { label: 'pepsi' }, { label: 'rad' }, { label: 'dude' }]); }); - it('Cancels on cancel', async () => { + it('Cancels on cancel', () => { const props = { ...defaultProps, tags: ['pepsi'], @@ -109,14 +103,11 @@ describe('TagList ', () => { expect(wrapper.find(`[data-test-subj="tag-pepsi"]`).last().exists()).toBeTruthy(); wrapper.find(`[data-test-subj="tag-list-edit-button"]`).last().simulate('click'); - await act(async () => { - expect(wrapper.find(`[data-test-subj="tag-pepsi"]`).last().exists()).toBeFalsy(); - wrapper.find(`[data-test-subj="edit-tags-cancel"]`).last().simulate('click'); - await waitFor(() => { - wrapper.update(); - expect(wrapper.find(`[data-test-subj="tag-pepsi"]`).last().exists()).toBeTruthy(); - }); - }); + + expect(wrapper.find(`[data-test-subj="tag-pepsi"]`).last().exists()).toBeFalsy(); + wrapper.find(`[data-test-subj="edit-tags-cancel"]`).last().simulate('click'); + wrapper.update(); + expect(wrapper.find(`[data-test-subj="tag-pepsi"]`).last().exists()).toBeTruthy(); }); it('Renders disabled button', () => { diff --git a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/index.test.tsx index 0b376f26a1ae0..4d9b7d030fec0 100644 --- a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/index.test.tsx @@ -7,7 +7,6 @@ import React from 'react'; import { mount } from 'enzyme'; import { waitFor } from '@testing-library/react'; -import { act } from 'react-dom/test-utils'; import { Router, routeData, mockHistory, mockLocation } from '../__mock__/router'; import { getFormMock, useFormMock, useFormDataMock } from '../__mock__/form'; @@ -33,6 +32,7 @@ const defaultProps = { }; const useUpdateCommentMock = useUpdateComment as jest.Mock; jest.mock('../../containers/use_update_comment'); +jest.mock('./user_action_timestamp'); const patchComment = jest.fn(); describe('UserActionTree ', () => { @@ -90,16 +90,14 @@ describe('UserActionTree ', () => { }, caseUserActions: ourActions, }; - - await act(async () => { - const wrapper = mount( - - - - - - ); - + const wrapper = mount( + + + + + + ); + await waitFor(() => { expect(wrapper.find(`[data-test-subj="top-footer"]`).exists()).toBeTruthy(); expect(wrapper.find(`[data-test-subj="bottom-footer"]`).exists()).toBeTruthy(); }); @@ -121,14 +119,14 @@ describe('UserActionTree ', () => { }, }; - await act(async () => { - const wrapper = mount( - - - - - - ); + const wrapper = mount( + + + + + + ); + await waitFor(() => { expect(wrapper.find(`[data-test-subj="top-footer"]`).exists()).toBeTruthy(); expect(wrapper.find(`[data-test-subj="bottom-footer"]`).exists()).toBeFalsy(); }); @@ -141,15 +139,15 @@ describe('UserActionTree ', () => { caseUserActions: ourActions, }; - await act(async () => { - const wrapper = mount( - - - - - - ); + const wrapper = mount( + + + + + + ); + await waitFor(() => { expect( wrapper .find(`[data-test-subj="comment-create-action-${props.data.comments[0].id}"]`) @@ -164,34 +162,32 @@ describe('UserActionTree ', () => { .first() .simulate('click'); - await waitFor(() => { - wrapper.update(); - expect( - wrapper - .find(`[data-test-subj="comment-create-action-${props.data.comments[0].id}"]`) - .first() - .hasClass('outlined') - ).toBeTruthy(); - }); + wrapper.update(); + expect( + wrapper + .find(`[data-test-subj="comment-create-action-${props.data.comments[0].id}"]`) + .first() + .hasClass('outlined') + ).toBeTruthy(); }); }); it('Switches to markdown when edit is clicked and back to panel when canceled', async () => { - await waitFor(() => { - const ourActions = [getUserAction(['comment'], 'create')]; - const props = { - ...defaultProps, - caseUserActions: ourActions, - }; - - const wrapper = mount( - - - - - - ); + const ourActions = [getUserAction(['comment'], 'create')]; + const props = { + ...defaultProps, + caseUserActions: ourActions, + }; + + const wrapper = mount( + + + + + + ); + await waitFor(() => { expect( wrapper .find( @@ -277,24 +273,22 @@ describe('UserActionTree ', () => { .first() .simulate('click'); - await act(async () => { - await waitFor(() => { - wrapper.update(); - expect( - wrapper - .find( - `[data-test-subj="comment-create-action-${props.data.comments[0].id}"] [data-test-subj="user-action-markdown-form"]` - ) - .exists() - ).toEqual(false); - expect(patchComment).toBeCalledWith({ - commentUpdate: sampleData.content, - caseId: props.data.id, - commentId: props.data.comments[0].id, - fetchUserActions, - updateCase, - version: props.data.comments[0].version, - }); + await waitFor(() => { + wrapper.update(); + expect( + wrapper + .find( + `[data-test-subj="comment-create-action-${props.data.comments[0].id}"] [data-test-subj="user-action-markdown-form"]` + ) + .exists() + ).toEqual(false); + expect(patchComment).toBeCalledWith({ + commentUpdate: sampleData.content, + caseId: props.data.id, + commentId: props.data.comments[0].id, + fetchUserActions, + updateCase, + version: props.data.comments[0].version, }); }); }); @@ -319,89 +313,86 @@ describe('UserActionTree ', () => { .first() .simulate('click'); - await act(async () => { - wrapper - .find(`[data-test-subj="description-action"] [data-test-subj="user-action-save-markdown"]`) - .first() - .simulate('click'); - }); - - wrapper.update(); + wrapper + .find(`[data-test-subj="description-action"] [data-test-subj="user-action-save-markdown"]`) + .first() + .simulate('click'); + await waitFor(() => { + wrapper.update(); - expect( - wrapper - .find(`[data-test-subj="description-action"] [data-test-subj="user-action-markdown-form"]`) - .exists() - ).toEqual(false); + expect( + wrapper + .find( + `[data-test-subj="description-action"] [data-test-subj="user-action-markdown-form"]` + ) + .exists() + ).toEqual(false); - expect(onUpdateField).toBeCalledWith({ key: 'description', value: sampleData.content }); + expect(onUpdateField).toBeCalledWith({ key: 'description', value: sampleData.content }); + }); }); it('quotes', async () => { - await act(async () => { - const commentData = { - comment: '', - }; - const setFieldValue = jest.fn(); - - const formHookMock = getFormMock(commentData); - useFormMock.mockImplementation(() => ({ form: { ...formHookMock, setFieldValue } })); - - const props = defaultProps; - const wrapper = mount( - - - - - - ); + const commentData = { + comment: '', + }; + const setFieldValue = jest.fn(); - wrapper - .find(`[data-test-subj="description-action"] [data-test-subj="property-actions-ellipses"]`) - .first() - .simulate('click'); + const formHookMock = getFormMock(commentData); + useFormMock.mockImplementation(() => ({ form: { ...formHookMock, setFieldValue } })); - await waitFor(() => { - wrapper.update(); + const props = defaultProps; + const wrapper = mount( + + + + + + ); - wrapper - .find(`[data-test-subj="description-action"] [data-test-subj="property-actions-quote"]`) - .first() - .simulate('click'); - }); + wrapper + .find(`[data-test-subj="description-action"] [data-test-subj="property-actions-ellipses"]`) + .first() + .simulate('click'); + + await waitFor(() => { + wrapper.update(); - expect(setFieldValue).toBeCalledWith('comment', `> ${props.data.description} \n`); + wrapper + .find(`[data-test-subj="description-action"] [data-test-subj="property-actions-quote"]`) + .first() + .simulate('click'); }); + + expect(setFieldValue).toBeCalledWith('comment', `> ${props.data.description} \n`); }); it('Outlines comment when url param is provided', async () => { const commentId = 'basic-comment-id'; jest.spyOn(routeData, 'useParams').mockReturnValue({ commentId }); - await act(async () => { - const ourActions = [getUserAction(['comment'], 'create')]; - const props = { - ...defaultProps, - caseUserActions: ourActions, - }; - - const wrapper = mount( - - - - - - ); - - await waitFor(() => { - wrapper.update(); - expect( - wrapper - .find(`[data-test-subj="comment-create-action-${commentId}"]`) - .first() - .hasClass('outlined') - ).toBeTruthy(); - }); + const ourActions = [getUserAction(['comment'], 'create')]; + const props = { + ...defaultProps, + caseUserActions: ourActions, + }; + + const wrapper = mount( + + + + + + ); + + await waitFor(() => { + wrapper.update(); + expect( + wrapper + .find(`[data-test-subj="comment-create-action-${commentId}"]`) + .first() + .hasClass('outlined') + ).toBeTruthy(); }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx index eced73e9c3d67..c8e12adef656a 100644 --- a/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx @@ -6,7 +6,7 @@ import { mount, shallow } from 'enzyme'; import React from 'react'; - +import { waitFor } from '@testing-library/react'; import { apolloClientObservable, mockGlobalState, @@ -157,39 +157,40 @@ describe('AddFilterToGlobalSearchBar Component', () => { ); + await waitFor(() => { + wrapper.find('[data-test-subj="withHoverActionsButton"]').simulate('mouseenter'); + wrapper.update(); + jest.runAllTimers(); + wrapper.update(); - wrapper.find('[data-test-subj="withHoverActionsButton"]').simulate('mouseenter'); - wrapper.update(); - jest.runAllTimers(); - wrapper.update(); - - wrapper - .find('[data-test-subj="hover-actions-container"] [data-euiicon-type]') - .first() - .simulate('click'); - wrapper.update(); + wrapper + .find('[data-test-subj="hover-actions-container"] [data-euiicon-type]') + .first() + .simulate('click'); + wrapper.update(); - expect(mockAddFilters.mock.calls[0][0]).toEqual({ - meta: { - alias: null, - disabled: false, - key: 'host.name', - negate: false, - params: { - query: 'siem-kibana', - }, - type: 'phrase', - value: 'siem-kibana', - }, - query: { - match: { - 'host.name': { + expect(mockAddFilters.mock.calls[0][0]).toEqual({ + meta: { + alias: null, + disabled: false, + key: 'host.name', + negate: false, + params: { query: 'siem-kibana', - type: 'phrase', }, + type: 'phrase', + value: 'siem-kibana', }, - }, + query: { + match: { + 'host.name': { + query: 'siem-kibana', + type: 'phrase', + }, + }, + }, + }); + expect(onFilterAdded).toHaveBeenCalledTimes(1); }); - expect(onFilterAdded).toHaveBeenCalledTimes(1); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_lists.test.tsx b/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_lists.test.tsx index eef6e09d496db..e38aaeedad8fd 100644 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_lists.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_lists.test.tsx @@ -8,8 +8,7 @@ import { ThemeProvider } from 'styled-components'; import { mount } from 'enzyme'; import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -// 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 { waitFor } from '@testing-library/react'; import { getField } from '../../../../../../../src/plugins/data/common/index_patterns/fields/fields.mocks'; import { ListSchema } from '../../../lists_plugin_deps'; diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.test.ts b/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.test.ts index bbcbcbcf928b3..225b407e4649e 100644 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.test.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import moment from 'moment'; import '../../../common/mock/match_media'; import { getField } from '../../../../../../../src/plugins/data/common/index_patterns/fields/fields.mocks'; @@ -17,6 +17,8 @@ import { import { getOperators, paramIsValid, getGenericComboBoxProps } from './helpers'; describe('helpers', () => { + // @ts-ignore + moment.suppressDeprecationWarnings = true; describe('#getOperators', () => { test('it returns "isOperator" if passed in field is "undefined"', () => { const operator = getOperators(undefined); diff --git a/x-pack/plugins/security_solution/public/common/components/charts/barchart.test.tsx b/x-pack/plugins/security_solution/public/common/components/charts/barchart.test.tsx index 64c8fde87a6bc..aa638abf65f7e 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/barchart.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/barchart.test.tsx @@ -13,6 +13,7 @@ import { ThemeProvider } from 'styled-components'; import { escapeDataProviderId } from '../drag_and_drop/helpers'; import { TestProviders } from '../../mock'; import '../../mock/match_media'; +import '../../mock/react_beautiful_dnd'; import { BarChartBaseComponent, BarChartComponent } from './barchart'; import { ChartSeriesData } from './common'; @@ -131,19 +132,6 @@ const mockConfig = { customHeight: 324, }; -// Suppress warnings about "react-beautiful-dnd" -/* eslint-disable no-console */ -const originalError = console.error; -const originalWarn = console.warn; -beforeAll(() => { - console.warn = jest.fn(); - console.error = jest.fn(); -}); -afterAll(() => { - console.error = originalError; - console.warn = originalWarn; -}); - describe('BarChartBaseComponent', () => { let shallowWrapper: ShallowWrapper; const mockBarChartData: ChartSeriesData[] = [ @@ -350,7 +338,10 @@ describe.each(chartDataSets)('BarChart with stackByField', () => { )}-${escapeDataProviderId(datum.key)}`; expect( - wrapper.find(`div [data-rbd-draggable-id="${dataProviderId}"]`).first().text() + wrapper + .find(`[draggableId="${dataProviderId}"] [data-test-subj="providerContainer"]`) + .first() + .text() ).toEqual(datum.key); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend.test.tsx b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend.test.tsx index 8fd2fa1fdef12..ffc2404bd4321 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend.test.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { ThemeProvider } from 'styled-components'; import '../../mock/match_media'; +import '../../mock/react_beautiful_dnd'; import { TestProviders } from '../../mock'; import { MIN_LEGEND_HEIGHT, DraggableLegend } from './draggable_legend'; @@ -58,20 +59,6 @@ const legendItems: LegendItem[] = [ describe('DraggableLegend', () => { const height = 400; - - // Suppress warnings about "react-beautiful-dnd" - /* eslint-disable no-console */ - const originalError = console.error; - const originalWarn = console.warn; - beforeAll(() => { - console.warn = jest.fn(); - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - console.warn = originalWarn; - }); - describe('rendering', () => { let wrapper: ReactWrapper; diff --git a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.test.tsx b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.test.tsx index 9f6e614c3c285..72e44da3297ea 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.test.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { ThemeProvider } from 'styled-components'; import '../../mock/match_media'; +import '../../mock/react_beautiful_dnd'; import { TestProviders } from '../../mock'; import { DraggableLegendItem, LegendItem } from './draggable_legend_item'; @@ -17,19 +18,6 @@ import { DraggableLegendItem, LegendItem } from './draggable_legend_item'; const theme = () => ({ eui: euiDarkVars, darkMode: true }); describe('DraggableLegendItem', () => { - // Suppress warnings about "react-beautiful-dnd" - /* eslint-disable no-console */ - const originalError = console.error; - const originalWarn = console.warn; - beforeAll(() => { - console.warn = jest.fn(); - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - console.warn = originalWarn; - }); - describe('rendering a regular (non "All others") legend item', () => { const legendItem: LegendItem = { color: '#1EA593', diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx index 46e7298677f49..5223452c8b93d 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx @@ -7,7 +7,7 @@ import { shallow } from 'enzyme'; import React from 'react'; import { DraggableStateSnapshot, DraggingStyle } from 'react-beautiful-dnd'; - +import { waitFor } from '@testing-library/react'; import '../../mock/match_media'; import { mockBrowserFields } from '../../containers/source/mock'; import { TestProviders } from '../../mock'; @@ -62,7 +62,7 @@ describe('DraggableWrapper', () => { expect(wrapper.find('[data-test-subj="copy-to-clipboard"]').exists()).toBe(false); }); - test('it renders hover actions when the mouse is over the text of draggable wrapper', () => { + test('it renders hover actions when the mouse is over the text of draggable wrapper', async () => { const wrapper = mount( @@ -71,11 +71,13 @@ describe('DraggableWrapper', () => { ); - wrapper.find('[data-test-subj="withHoverActionsButton"]').simulate('mouseenter'); - wrapper.update(); - jest.runAllTimers(); - wrapper.update(); - expect(wrapper.find('[data-test-subj="copy-to-clipboard"]').exists()).toBe(true); + await waitFor(() => { + wrapper.find('[data-test-subj="withHoverActionsButton"]').simulate('mouseenter'); + wrapper.update(); + jest.runAllTimers(); + wrapper.update(); + expect(wrapper.find('[data-test-subj="copy-to-clipboard"]').exists()).toBe(true); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx index 8aa926a36988b..af7e9ad5f1492 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx @@ -85,22 +85,6 @@ describe('DraggableWrapperHoverContent', () => { }); }); - // Suppress warnings about "react-beautiful-dnd" - /* eslint-disable no-console */ - const originalError = console.error; - const originalWarn = console.warn; - beforeEach(() => { - jest.clearAllMocks(); - }); - beforeAll(() => { - console.warn = jest.fn(); - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - console.warn = originalWarn; - }); - /** * The tests for "Filter for value" and "Filter out value" are similar enough * to combine them into "table tests" using this array diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/link_to_app.test.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/link_to_app.test.tsx index 79773630e0dc0..d791ea44f8198 100644 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/link_to_app.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/link_to_app.test.tsx @@ -44,7 +44,7 @@ describe('LinkToApp component', () => { }); it('should support onClick prop', () => { // Take `_event` (even though it is not used) so that `jest.fn` will have a type that expects to be called with an event - const spyOnClickHandler: LinkToAppOnClickMock = jest.fn((_event) => {}); + const spyOnClickHandler: LinkToAppOnClickMock = jest.fn().mockImplementation((_event) => {}); const renderResult = render( {'link'} @@ -98,20 +98,24 @@ describe('LinkToApp component', () => { }); it('should still preventDefault if onClick callback throws', () => { // Take `_event` (even though it is not used) so that `jest.fn` will have a type that expects to be called with an event - const spyOnClickHandler: LinkToAppOnClickMock = jest.fn((_event) => { + const spyOnClickHandler = jest.fn().mockImplementation((_event) => { throw new Error('test'); }); - const renderResult = render( - - {'link'} - - ); - expect(() => renderResult.find('EuiLink').simulate('click')).toThrow(); - const clickEventArg = spyOnClickHandler.mock.calls[0][0]; - expect(clickEventArg.isDefaultPrevented()).toBe(true); + // eslint-disable-next-line no-empty + try { + } catch (e) { + const renderResult = render( + + {'link'} + + ); + expect(() => renderResult.find('EuiLink').simulate('click')).toThrowError(); + const clickEventArg = spyOnClickHandler.mock.calls[0][0]; + expect(clickEventArg.isDefaultPrevented()).toBe(true); + } }); it('should not navigate if onClick callback prevents default', () => { - const spyOnClickHandler: LinkToAppOnClickMock = jest.fn((ev) => { + const spyOnClickHandler: LinkToAppOnClickMock = jest.fn().mockImplementation((ev) => { ev.preventDefault(); }); const renderResult = render( diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx index 01b0810830dd8..c3c7c864ac99b 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx @@ -8,16 +8,19 @@ import { shallow } from 'enzyme'; import React from 'react'; import '../../mock/match_media'; -import { mockDetailItemData, mockDetailItemDataId } from '../../mock/mock_detail_item'; -import { TestProviders } from '../../mock/test_providers'; +import '../../mock/react_beautiful_dnd'; +import { + defaultHeaders, + mockDetailItemData, + mockDetailItemDataId, + TestProviders, +} from '../../mock'; import { EventDetails, View } from './event_details'; import { mockBrowserFields } from '../../containers/source/mock'; -import { defaultHeaders } from '../../mock/header'; import { useMountAppended } from '../../utils/use_mount_appended'; jest.mock('../link_to'); - describe('EventDetails', () => { const mount = useMountAppended(); const onEventToggled = jest.fn(); diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx index 9a3c0fa1cad2e..928521c118ba5 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx @@ -8,8 +8,7 @@ import React from 'react'; import useResizeObserver from 'use-resize-observer/polyfilled'; import '../../mock/match_media'; -// 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 { waitFor } from '@testing-library/react'; import { TestProviders } from '../../mock'; import { useMountAppended } from '../../utils/use_mount_appended'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx index ed1c1c1cdad1f..fa9838aa37015 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx @@ -8,10 +8,11 @@ import React from 'react'; import { ThemeProvider } from 'styled-components'; import { mount, ReactWrapper } from 'enzyme'; import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; -import { act } from 'react-dom/test-utils'; +import { waitFor } from '@testing-library/react'; import { AddExceptionModal } from './'; import { useCurrentUser } from '../../../../common/lib/kibana'; +import { useAsync } from '../../../../shared_imports'; import { getExceptionListSchemaMock } from '../../../../../../lists/common/schemas/response/exception_list_schema.mock'; import { useFetchIndex } from '../../../containers/source'; import { stubIndexPattern } from 'src/plugins/data/common/index_patterns/index_pattern.stub'; @@ -33,6 +34,7 @@ jest.mock('../../../../detections/containers/detection_engine/rules'); jest.mock('../use_add_exception'); jest.mock('../use_fetch_or_create_rule_exception_list'); jest.mock('../builder'); +jest.mock('../../../../shared_imports'); describe('When the add exception modal is opened', () => { const ruleName = 'test rule'; @@ -48,6 +50,11 @@ describe('When the add exception modal is opened', () => { .spyOn(builder, 'ExceptionBuilderComponent') .mockReturnValue(<>); + (useAsync as jest.Mock).mockImplementation(() => ({ + start: jest.fn(), + loading: false, + })); + (useAddOrUpdateException as jest.Mock).mockImplementation(() => [ { isLoading: false }, jest.fn(), @@ -104,7 +111,7 @@ describe('When the add exception modal is opened', () => { describe('when there is no alert data passed to an endpoint list exception', () => { let wrapper: ReactWrapper; - beforeEach(() => { + beforeEach(async () => { wrapper = mount( ({ eui: euiLightVars, darkMode: false })}> { ); const callProps = ExceptionBuilderComponent.mock.calls[0][0]; - act(() => callProps.onChange({ exceptionItems: [] })); + await waitFor(() => callProps.onChange({ exceptionItems: [] })); }); it('has the add exception button disabled', () => { expect( @@ -140,7 +147,7 @@ describe('When the add exception modal is opened', () => { describe('when there is alert data passed to an endpoint list exception', () => { let wrapper: ReactWrapper; - beforeEach(() => { + beforeEach(async () => { const alertDataMock: { ecsData: Ecs; nonEcsData: TimelineNonEcsData[] } = { ecsData: { _id: 'test-id' }, nonEcsData: [{ field: 'file.path', value: ['test/path'] }], @@ -159,7 +166,9 @@ describe('When the add exception modal is opened', () => { ); const callProps = ExceptionBuilderComponent.mock.calls[0][0]; - act(() => callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] })); + await waitFor(() => + callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }) + ); }); it('has the add exception button enabled', () => { expect( @@ -191,7 +200,7 @@ describe('When the add exception modal is opened', () => { describe('when there is alert data passed to a detection list exception', () => { let wrapper: ReactWrapper; - beforeEach(() => { + beforeEach(async () => { const alertDataMock: { ecsData: Ecs; nonEcsData: TimelineNonEcsData[] } = { ecsData: { _id: 'test-id' }, nonEcsData: [{ field: 'file.path', value: ['test/path'] }], @@ -210,7 +219,9 @@ describe('When the add exception modal is opened', () => { ); const callProps = ExceptionBuilderComponent.mock.calls[0][0]; - act(() => callProps.onChange({ exceptionItems: [getExceptionListItemSchemaMock()] })); + await waitFor(() => + callProps.onChange({ exceptionItems: [getExceptionListItemSchemaMock()] }) + ); }); it('has the add exception button enabled', () => { expect( @@ -243,7 +254,7 @@ describe('When the add exception modal is opened', () => { onChange: (props: { exceptionItems: ExceptionListItemSchema[] }) => void; exceptionListItems: ExceptionListItemSchema[]; }; - beforeEach(() => { + beforeEach(async () => { // Mocks the index patterns to contain the pre-populated endpoint fields so that the exception qualifies as bulk closable (useFetchIndex as jest.Mock).mockImplementation(() => [ false, @@ -278,7 +289,9 @@ describe('When the add exception modal is opened', () => { ); callProps = ExceptionBuilderComponent.mock.calls[0][0]; - act(() => callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] })); + await waitFor(() => + callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }) + ); }); it('has the add exception button enabled', () => { expect( @@ -307,8 +320,8 @@ describe('When the add exception modal is opened', () => { ).not.toBeDisabled(); }); describe('when a "is in list" entry is added', () => { - it('should have the bulk close checkbox disabled', () => { - act(() => + it('should have the bulk close checkbox disabled', async () => { + await waitFor(() => callProps.onChange({ exceptionItems: [ ...callProps.exceptionListItems, diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.test.tsx index 2d389a7dbcee1..e6f57fe666780 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { ThemeProvider } from 'styled-components'; import { mount } from 'enzyme'; import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; -import { wait as waitFor } from '@testing-library/react'; +import { waitFor } from '@testing-library/react'; import { fields, diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.test.tsx index d5d2091cc9bc8..551a9173351fc 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.test.tsx @@ -5,10 +5,10 @@ */ import React from 'react'; +import { waitFor } from '@testing-library/react'; import { ThemeProvider } from 'styled-components'; import { mount, ReactWrapper } from 'enzyme'; import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; -import { act } from 'react-dom/test-utils'; import { EditExceptionModal } from './'; import { useCurrentUser } from '../../../../common/lib/kibana'; @@ -66,15 +66,14 @@ describe('When the edit exception modal is opened', () => { }); describe('when the modal is loading', () => { - let wrapper: ReactWrapper; - beforeEach(() => { + it('renders the loading spinner', async () => { (useFetchIndex as jest.Mock).mockImplementation(() => [ true, { indexPatterns: stubIndexPattern, }, ]); - wrapper = mount( + const wrapper = mount( ({ eui: euiLightVars, darkMode: false })}> { /> ); - }); - it('renders the loading spinner', () => { - expect(wrapper.find('[data-test-subj="loadingEditExceptionModal"]').exists()).toBeTruthy(); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="loadingEditExceptionModal"]').exists()).toBeTruthy(); + }); }); }); describe('when an endpoint exception with exception data is passed', () => { describe('when exception entry fields are included in the index pattern', () => { let wrapper: ReactWrapper; - beforeEach(() => { + beforeEach(async () => { const exceptionItemMock = { ...getExceptionListItemSchemaMock(), entries: [ @@ -117,7 +116,9 @@ describe('When the edit exception modal is opened', () => { ); const callProps = ExceptionBuilderComponent.mock.calls[0][0]; - act(() => callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] })); + await waitFor(() => { + callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }); + }); }); it('has the edit exception button enabled', () => { expect( @@ -145,7 +146,7 @@ describe('When the edit exception modal is opened', () => { describe("when exception entry fields aren't included in the index pattern", () => { let wrapper: ReactWrapper; - beforeEach(() => { + beforeEach(async () => { wrapper = mount( ({ eui: euiLightVars, darkMode: false })}> { ); const callProps = ExceptionBuilderComponent.mock.calls[0][0]; - act(() => callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] })); + await waitFor(() => { + callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }); + }); }); it('has the edit exception button enabled', () => { expect( @@ -189,7 +192,7 @@ describe('When the edit exception modal is opened', () => { describe('when an detection exception with entries is passed', () => { let wrapper: ReactWrapper; - beforeEach(() => { + beforeEach(async () => { wrapper = mount( ({ eui: euiLightVars, darkMode: false })}> { ); const callProps = ExceptionBuilderComponent.mock.calls[0][0]; - act(() => callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] })); + await waitFor(() => { + callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }); + }); }); it('has the edit exception button enabled', () => { expect( @@ -228,7 +233,7 @@ describe('When the edit exception modal is opened', () => { describe('when an exception with no entries is passed', () => { let wrapper: ReactWrapper; - beforeEach(() => { + beforeEach(async () => { const exceptionItemMock = { ...getExceptionListItemSchemaMock(), entries: [] }; wrapper = mount( ({ eui: euiLightVars, darkMode: false })}> @@ -244,7 +249,9 @@ describe('When the edit exception modal is opened', () => { ); const callProps = ExceptionBuilderComponent.mock.calls[0][0]; - act(() => callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] })); + await waitFor(() => { + callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }); + }); }); it('has the edit exception button disabled', () => { expect( diff --git a/x-pack/plugins/security_solution/public/common/components/last_event_time/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/last_event_time/index.test.tsx index cc0c4d4c837a3..d9b5c5e10893c 100644 --- a/x-pack/plugins/security_solution/public/common/components/last_event_time/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/last_event_time/index.test.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; - +import '../../../common/mock/formatted_relative'; import { getEmptyValue } from '../empty_value'; import { LastEventIndexKey } from '../../../../common/search_strategy'; import { mockLastEventTimeQuery } from '../../containers/events/last_event_time/mock'; @@ -57,7 +57,7 @@ describe('Last Event Time Stat', () => { ); - expect(wrapper.html()).toBe('Last event: 12 minutes ago'); + expect(wrapper.html()).toBe('Last event: 20 hours ago'); }); test('Bad date time string', async () => { (useTimelineLastEventTime as jest.Mock).mockReturnValue([ diff --git a/x-pack/plugins/security_solution/public/common/components/link_icon/index.tsx b/x-pack/plugins/security_solution/public/common/components/link_icon/index.tsx index 19f1d70e6e230..55842342c6677 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_icon/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/link_icon/index.tsx @@ -6,16 +6,16 @@ import { EuiIcon, EuiLink, IconSize, IconType } from '@elastic/eui'; import { LinkAnchorProps } from '@elastic/eui/src/components/link/link'; -import React, { ReactNode } from 'react'; +import React, { ReactNode, useCallback, useMemo } from 'react'; import styled, { css } from 'styled-components'; interface LinkProps { + ariaLabel?: string; color?: LinkAnchorProps['color']; disabled?: boolean; href?: string; iconSide?: 'left' | 'right'; onClick?: Function; - ariaLabel?: string; } export const Link = styled(({ iconSide, children, ...rest }) => ( @@ -55,6 +55,7 @@ export interface LinkIconProps extends LinkProps { export const LinkIcon = React.memo( ({ + ariaLabel, children, color, dataTestSubj, @@ -64,21 +65,41 @@ export const LinkIcon = React.memo( iconSize = 's', iconType, onClick, - ariaLabel, - }) => ( - - - {children} - - ) + }) => { + const getChildrenString = useCallback((theChild: string | ReactNode): string => { + if ( + typeof theChild === 'object' && + theChild != null && + 'props' in theChild && + theChild.props && + theChild.props.children + ) { + return getChildrenString(theChild.props.children); + } + return theChild != null && Object.keys(theChild).length > 0 ? (theChild as string) : ''; + }, []); + const aria = useMemo(() => { + if (ariaLabel) { + return ariaLabel; + } + return getChildrenString(children); + }, [ariaLabel, children, getChildrenString]); + + return ( + + + {children} + + ); + } ); LinkIcon.displayName = 'LinkIcon'; diff --git a/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.test.tsx index e9940d088e606..07d148ff96dfa 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.test.tsx @@ -11,7 +11,6 @@ import '../../mock/match_media'; import { EntityDraggableComponent } from './entity_draggable'; import { TestProviders } from '../../mock/test_providers'; import { useMountAppended } from '../../utils/use_mount_appended'; - describe('entity_draggable', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_score.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_score.test.tsx index 434cbd8ada88e..cb10c61302d3c 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_score.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_score.test.tsx @@ -19,7 +19,6 @@ const startDate: string = '2020-07-07T08:20:18.966Z'; const endDate: string = '3000-01-01T00:00:00.000Z'; const narrowDateRange = jest.fn(); - describe('anomaly_scores', () => { let anomalies: Anomalies = cloneDeep(mockAnomalies); const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_scores.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_scores.test.tsx index a900c3e49f912..52151f217e01a 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_scores.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_scores.test.tsx @@ -19,7 +19,6 @@ import { useMountAppended } from '../../../utils/use_mount_appended'; const startDate: string = '2020-07-07T08:20:18.966Z'; const endDate: string = '3000-01-01T00:00:00.000Z'; const narrowDateRange = jest.fn(); - describe('anomaly_scores', () => { let anomalies: Anomalies = cloneDeep(mockAnomalies); const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_host_table_columns.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_host_table_columns.test.tsx index d370a901a6262..3092cbf265ea9 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_host_table_columns.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_host_table_columns.test.tsx @@ -19,7 +19,6 @@ const startDate = new Date(2001).toISOString(); const endDate = new Date(3000).toISOString(); const interval = 'days'; const narrowDateRange = jest.fn(); - describe('get_anomalies_host_table_columns', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_network_table_columns.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_network_table_columns.test.tsx index 69a4e383413f2..89f94a3819e65 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_network_table_columns.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_network_table_columns.test.tsx @@ -16,7 +16,6 @@ import { useMountAppended } from '../../../utils/use_mount_appended'; const startDate = new Date(2001).toISOString(); const endDate = new Date(3000).toISOString(); - describe('get_anomalies_network_table_columns', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/job_switch.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/job_switch.test.tsx index e58d76bd1dde0..13518acdefdde 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/job_switch.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/job_switch.test.tsx @@ -7,6 +7,7 @@ import { shallow, mount } from 'enzyme'; import React from 'react'; +import { waitFor } from '@testing-library/react'; import { JobSwitchComponent } from './job_switch'; import { cloneDeep } from 'lodash/fp'; import { mockSecurityJobs } from '../api.mock'; @@ -31,7 +32,7 @@ describe('JobSwitch', () => { expect(wrapper).toMatchSnapshot(); }); - test('should call onJobStateChange when the switch is clicked to be true/open', () => { + test('should call onJobStateChange when the switch is clicked to be true/open', async () => { const wrapper = mount( { .simulate('click', { target: { checked: true }, }); - - expect(onJobStateChangeMock.mock.calls[0][0].id).toEqual( - 'linux_anomalous_network_activity_ecs' - ); - expect(onJobStateChangeMock.mock.calls[0][1]).toEqual(1571022859393); - expect(onJobStateChangeMock.mock.calls[0][2]).toEqual(true); + await waitFor(() => { + expect(onJobStateChangeMock.mock.calls[0][0].id).toEqual( + 'linux_anomalous_network_activity_ecs' + ); + expect(onJobStateChangeMock.mock.calls[0][1]).toEqual(1571022859393); + expect(onJobStateChangeMock.mock.calls[0][2]).toEqual(true); + }); }); test('should have a switch when it is not in the loading state', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx index dfcabe7b5aedf..3d7e47a15fc1e 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx @@ -6,6 +6,7 @@ import { shallow, mount } from 'enzyme'; import React from 'react'; +import { waitFor } from '@testing-library/react'; import { JobsTableComponent } from './jobs_table'; import { mockSecurityJobs } from '../api.mock'; import { cloneDeep } from 'lodash/fp'; @@ -59,7 +60,7 @@ describe('JobsTableComponent', () => { ); }); - test('should call onJobStateChange when the switch is clicked to be true/open', () => { + test('should call onJobStateChange when the switch is clicked to be true/open', async () => { const wrapper = mount( { .simulate('click', { target: { checked: true }, }); - expect(onJobStateChangeMock.mock.calls[0]).toEqual([securityJobs[0], 1571022859393, true]); + await waitFor(() => { + expect(onJobStateChangeMock.mock.calls[0]).toEqual([securityJobs[0], 1571022859393, true]); + }); }); test('should have a switch when it is not in the loading state', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx index aa61688f1f986..12199ce5c1b66 100644 --- a/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx @@ -6,7 +6,7 @@ import { mount } from 'enzyme'; import React from 'react'; - +import { waitFor } from '@testing-library/react'; import { coreMock } from '../../../../../../../src/core/public/mocks'; import { DEFAULT_FROM, DEFAULT_TO } from '../../../../common/constants'; import { TestProviders, mockIndexPattern } from '../../mock'; @@ -16,22 +16,6 @@ import { QueryBar, QueryBarComponentProps } from '.'; const mockUiSettingsForFilterManager = coreMock.createStart().uiSettings; describe('QueryBar ', () => { - // We are doing that because we need to wrapped this component with redux - // and redux does not like to be updated and since we need to update our - // child component (BODY) and we do not want to scare anyone with this error - // we are hiding it!!! - // eslint-disable-next-line no-console - const originalError = console.error; - beforeAll(() => { - // eslint-disable-next-line no-console - console.error = (...args: string[]) => { - if (/ does not support changing `store` on the fly/.test(args[0])) { - return; - } - originalError.call(console, ...args); - }; - }); - const mockOnChangeQuery = jest.fn(); const mockOnSubmitQuery = jest.fn(); const mockOnSavedQuery = jest.fn(); @@ -372,7 +356,7 @@ describe('QueryBar ', () => { }); describe('SavedQueryManagementComponent state', () => { - test('popover should hidden when "Save current query" button was clicked', () => { + test('popover should hidden when "Save current query" button was clicked', async () => { const Proxy = (props: QueryBarComponentProps) => ( @@ -397,21 +381,24 @@ describe('QueryBar ', () => { onSavedQuery={mockOnSavedQuery} /> ); + await waitFor(() => { + const isSavedQueryPopoverOpen = () => + wrapper.find('EuiPopover[id="savedQueryPopover"]').prop('isOpen'); - const isSavedQueryPopoverOpen = () => - wrapper.find('EuiPopover[id="savedQueryPopover"]').prop('isOpen'); - - expect(isSavedQueryPopoverOpen()).toBeFalsy(); + expect(isSavedQueryPopoverOpen()).toBeFalsy(); - wrapper - .find('button[data-test-subj="saved-query-management-popover-button"]') - .simulate('click'); + wrapper + .find('button[data-test-subj="saved-query-management-popover-button"]') + .simulate('click'); - expect(isSavedQueryPopoverOpen()).toBeTruthy(); + expect(isSavedQueryPopoverOpen()).toBeTruthy(); - wrapper.find('button[data-test-subj="saved-query-management-save-button"]').simulate('click'); + wrapper + .find('button[data-test-subj="saved-query-management-save-button"]') + .simulate('click'); - expect(isSavedQueryPopoverOpen()).toBeFalsy(); + expect(isSavedQueryPopoverOpen()).toBeFalsy(); + }); }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx index bd9f2677ec966..2696b115cdc18 100644 --- a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx @@ -20,8 +20,7 @@ import { } from '../../mock'; import { createStore, State } from '../../store'; import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -import { act } from 'react-dom/test-utils'; -import { wait as waitFor } from '@testing-library/react'; +import { waitFor } from '@testing-library/react'; const mockDispatch = jest.fn(); jest.mock('react-redux', () => { @@ -126,13 +125,11 @@ describe('Sourcerer component', () => { expect(true).toBeTruthy(); wrapper.find(`[data-test-subj="sourcerer-trigger"]`).first().simulate('click'); - await act(async () => { + await waitFor(() => { ((wrapper.find(EuiComboBox).props() as unknown) as { onChange: (a: EuiComboBoxOptionOption[]) => void; }).onChange([mockOptions[0], mockOptions[1]]); - await waitFor(() => { - wrapper.update(); - }); + wrapper.update(); }); wrapper.find(`[data-test-subj="add-index"]`).first().simulate('click'); diff --git a/x-pack/plugins/security_solution/public/common/components/tables/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/tables/helpers.test.tsx index b28c7e70b8ae8..f091f22abcb94 100644 --- a/x-pack/plugins/security_solution/public/common/components/tables/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/tables/helpers.test.tsx @@ -17,7 +17,6 @@ import { import { TestProviders } from '../../mock'; import { getEmptyValue } from '../empty_value'; import { useMountAppended } from '../../utils/use_mount_appended'; - describe('Table Helpers', () => { const items = ['item1', 'item2', 'item3']; const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx index 594bffbd4ff63..fd1fa1c29a807 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx @@ -6,7 +6,7 @@ import { mount, ReactWrapper } from 'enzyme'; import React from 'react'; - +import { waitFor } from '@testing-library/react'; import '../../mock/match_media'; import { mockBrowserFields } from '../../containers/source/mock'; import { @@ -180,19 +180,6 @@ let testProps = { }; describe('StatefulTopN', () => { - // Suppress warnings about "react-beautiful-dnd" - /* eslint-disable no-console */ - const originalError = console.error; - const originalWarn = console.warn; - beforeAll(() => { - console.warn = jest.fn(); - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - console.warn = originalWarn; - }); - describe('rendering in a global NON-timeline context', () => { let wrapper: ReactWrapper; @@ -343,7 +330,7 @@ describe('StatefulTopN', () => { }); }); describe('rendering in a NON-active timeline context', () => { - test(`defaults to the 'Alert events' option when rendering in a NON-active timeline context (e.g. the Alerts table on the Detections page) when 'documentType' from 'useTimelineTypeContext()' is 'alerts'`, () => { + test(`defaults to the 'Alert events' option when rendering in a NON-active timeline context (e.g. the Alerts table on the Detections page) when 'documentType' from 'useTimelineTypeContext()' is 'alerts'`, async () => { const filterManager = new FilterManager(mockUiSettingsForFilterManager); const manageTimelineForTesting = { @@ -365,10 +352,11 @@ describe('StatefulTopN', () => { ); + await waitFor(() => { + const props = wrapper.find('[data-test-subj="top-n"]').first().props() as Props; - const props = wrapper.find('[data-test-subj="top-n"]').first().props() as Props; - - expect(props.defaultView).toEqual('alert'); + expect(props.defaultView).toEqual('alert'); + }); }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/top_n.test.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/top_n.test.tsx index 829f918ddfe1b..f7ad35f2c5a37 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/top_n.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/top_n.test.tsx @@ -6,15 +6,13 @@ import { mount, ReactWrapper } from 'enzyme'; import React from 'react'; - +import { waitFor } from '@testing-library/react'; import '../../mock/match_media'; import { TestProviders, mockIndexPattern } from '../../mock'; import { setAbsoluteRangeDatePicker } from '../../store/inputs/actions'; import { allEvents, defaultOptions } from './helpers'; -import { TopN } from './top_n'; -import { TimelineEventsType } from '../../../../common/types/timeline'; -import { InputsModelId } from '../../store/inputs/constants'; +import { TopN, Props as TopNProps } from './top_n'; jest.mock('react-router-dom', () => { const original = jest.requireActual('react-router-dom'); @@ -90,28 +88,15 @@ const combinedQueries = { }; describe('TopN', () => { - // Suppress warnings about "react-beautiful-dnd" - /* eslint-disable no-console */ - const originalError = console.error; - const originalWarn = console.warn; - beforeAll(() => { - console.warn = jest.fn(); - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - console.warn = originalWarn; - }); - const query = { query: '', language: 'kuery' }; const toggleTopN = jest.fn(); - const eventTypes: { [id: string]: TimelineEventsType } = { + const eventTypes: { [id: string]: TopNProps['defaultView'] } = { raw: 'raw', alert: 'alert', all: 'all', }; - let testProps = { + let testProps: TopNProps = { defaultView: eventTypes.raw, field, filters: [], @@ -121,7 +106,7 @@ describe('TopN', () => { options: defaultOptions, query, setAbsoluteRangeDatePicker, - setAbsoluteRangeDatePickerTarget: 'global' as InputsModelId, + setAbsoluteRangeDatePickerTarget: 'global', setQuery: jest.fn(), to: '2020-04-15T00:31:47.695Z', toggleTopN, @@ -172,28 +157,35 @@ describe('TopN', () => { }); describe('alerts view', () => { - let wrapper: ReactWrapper; - - beforeEach(() => { + beforeAll(() => { testProps = { ...testProps, defaultView: eventTypes.alert, }; - wrapper = mount( + }); + + test(`it renders SignalsByCategory when defaultView is 'alert'`, async () => { + const wrapper = mount( ); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="alerts-histogram-panel"]').exists()).toBe(true); + }); }); - test(`it renders SignalsByCategory when defaultView is 'alert'`, () => { - expect(wrapper.find('[data-test-subj="alerts-histogram-panel"]').exists()).toBe(true); - }); - - test(`it does NOT render EventsByDataset when defaultView is 'alert'`, () => { - expect( - wrapper.find('[data-test-subj="eventsByDatasetOverview-uuid.v4()Panel"]').exists() - ).toBe(false); + test(`it does NOT render EventsByDataset when defaultView is 'alert'`, async () => { + const wrapper = mount( + + + + ); + await waitFor(() => { + expect( + wrapper.find('[data-test-subj="eventsByDatasetOverview-uuid.v4()Panel"]').exists() + ).toBe(false); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/index.test.tsx index fc970c066e8a5..f4a48eaea69c2 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/url_state/index.test.tsx @@ -21,8 +21,7 @@ import { } from './test_dependencies'; import { UrlStateContainerPropTypes } from './types'; import { useUrlStateHooks } from './use_url_state'; -// 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 { waitFor } from '@testing-library/react'; let mockProps: UrlStateContainerPropTypes; diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts index c026b65853a4c..6f8ff2e1bb21a 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts @@ -13,20 +13,20 @@ import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_r import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks'; import { securityMock } from '../../../../../../plugins/security/public/mocks'; import { - DEFAULT_APP_TIME_RANGE, DEFAULT_APP_REFRESH_INTERVAL, - DEFAULT_INDEX_KEY, + DEFAULT_APP_TIME_RANGE, + DEFAULT_BYTES_FORMAT, + DEFAULT_DARK_MODE, DEFAULT_DATE_FORMAT, DEFAULT_DATE_FORMAT_TZ, - DEFAULT_DARK_MODE, - DEFAULT_TIME_RANGE, - DEFAULT_REFRESH_RATE_INTERVAL, DEFAULT_FROM, - DEFAULT_TO, + DEFAULT_INDEX_KEY, + DEFAULT_INDEX_PATTERN, DEFAULT_INTERVAL_PAUSE, DEFAULT_INTERVAL_VALUE, - DEFAULT_BYTES_FORMAT, - DEFAULT_INDEX_PATTERN, + DEFAULT_REFRESH_RATE_INTERVAL, + DEFAULT_TIME_RANGE, + DEFAULT_TO, } from '../../../../common/constants'; import { StartServices } from '../../../types'; import { createSecuritySolutionStorageMock } from '../../mock/mock_local_storage'; @@ -77,14 +77,43 @@ export const createStartServicesMock = (): StartServices => { const data = dataPluginMock.createStartContract(); const security = securityMock.createSetup(); - const services = ({ + return ({ ...core, - data, + data: { + ...data, + query: { + ...data.query, + savedQueries: { + ...data.query.savedQueries, + getAllSavedQueries: jest.fn(() => + Promise.resolve({ + id: '123', + attributes: { + total: 123, + }, + }) + ), + findSavedQueries: jest.fn(() => + Promise.resolve({ + total: 123, + queries: [], + }) + ), + }, + }, + search: { + ...data.search, + search: jest.fn().mockImplementation(() => ({ + subscribe: jest.fn().mockImplementation(() => ({ + error: jest.fn(), + next: jest.fn(), + })), + })), + }, + }, security, storage, } as unknown) as StartServices; - - return services; }; export const createWithKibanaMock = () => { diff --git a/x-pack/plugins/security_solution/public/common/mock/formatted_relative.ts b/x-pack/plugins/security_solution/public/common/mock/formatted_relative.ts new file mode 100644 index 0000000000000..0eb1c9a478ca0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/mock/formatted_relative.ts @@ -0,0 +1,15 @@ +/* + * 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. + */ + +jest.mock('@kbn/i18n/react', () => { + const originalModule = jest.requireActual('@kbn/i18n/react'); + const FormattedRelative = jest.fn().mockImplementation(() => '20 hours ago'); + + return { + ...originalModule, + FormattedRelative, + }; +}); diff --git a/x-pack/plugins/security_solution/public/common/mock/react_beautiful_dnd.ts b/x-pack/plugins/security_solution/public/common/mock/react_beautiful_dnd.ts new file mode 100644 index 0000000000000..e077d28925912 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/mock/react_beautiful_dnd.ts @@ -0,0 +1,53 @@ +/* + * 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 { + DraggableProvided, + DraggableStateSnapshot, + DroppableProvided, + DroppableStateSnapshot, +} from 'react-beautiful-dnd'; +import React from 'react'; + +jest.mock('react-beautiful-dnd', () => ({ + Droppable: ({ + children, + }: { + children: (a: DroppableProvided, b: DroppableStateSnapshot) => void; + }) => + children( + { + droppableProps: { + 'data-rbd-droppable-context-id': '123', + 'data-rbd-droppable-id': '123', + }, + innerRef: jest.fn(), + }, + { + isDraggingOver: false, + isUsingPlaceholder: false, + } + ), + Draggable: ({ + children, + }: { + children: (a: DraggableProvided, b: DraggableStateSnapshot) => void; + }) => + children( + { + draggableProps: { + 'data-rbd-draggable-context-id': '123', + 'data-rbd-draggable-id': '123', + }, + innerRef: jest.fn(), + }, + { + isDragging: false, + isDropAnimating: false, + } + ), + DragDropContext: ({ children }: { children: React.ReactNode }) => children, +})); diff --git a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx index 8c186addf783d..e84f80655fbe4 100644 --- a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx @@ -46,7 +46,7 @@ export const kibanaObservable = new BehaviorSubject(createStartServicesMock()); Object.defineProperty(window, 'localStorage', { value: localStorageMock(), }); - +window.scrollTo = jest.fn(); const MockKibanaContextProvider = createKibanaContextProviderMock(); const { storage } = createSecuritySolutionStorageMock(); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx index 4312be0b46990..2bdc813639740 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { mount, shallow } from 'enzyme'; import { ThemeProvider } from 'styled-components'; import euiDarkVars from '@elastic/eui/dist/eui_theme_light.json'; +import { act } from '@testing-library/react'; import { stubIndexPattern } from 'src/plugins/data/common/index_patterns/index_pattern.stub'; import { StepAboutRule } from '.'; @@ -23,18 +24,17 @@ import { fillEmptySeverityMappings } from '../../../pages/detection_engine/rules jest.mock('../../../../common/containers/source'); const theme = () => ({ eui: euiDarkVars, darkMode: true }); - -/* eslint-disable no-console */ -// Silence until enzyme fixed to use ReactTestUtils.act() -const originalError = console.error; -beforeAll(() => { - console.error = jest.fn(); -}); -afterAll(() => { - console.error = originalError; +jest.mock('@elastic/eui', () => { + const original = jest.requireActual('@elastic/eui'); + return { + ...original, + // eslint-disable-next-line react/display-name, @typescript-eslint/no-explicit-any + EuiFieldText: (props: any) => { + const { isInvalid, isLoading, fullWidth, inputRef, isDisabled, ...validInputProps } = props; + return ; + }, + }; }); -/* eslint-enable no-console */ - describe('StepAboutRuleComponent', () => { let formHook: RuleStepsFormHooks[RuleStep.aboutRule] | null = null; const setFormHook = ( @@ -54,7 +54,7 @@ describe('StepAboutRuleComponent', () => { ]); }); - test('it renders StepRuleDescription if isReadOnlyView is true and "name" property exists', () => { + it('it renders StepRuleDescription if isReadOnlyView is true and "name" property exists', () => { const wrapper = shallow( { ); - if (!formHook) { - throw new Error('Form hook not set, but tests depend on it'); - } - - wrapper - .find('[data-test-subj="detectionEngineStepAboutRuleName"] input') - .first() - .simulate('change', { target: { value: 'Test name text' } }); - - const result = await formHook(); - expect(result?.isValid).toEqual(false); + await act(async () => { + if (!formHook) { + throw new Error('Form hook not set, but tests depend on it'); + } + wrapper + .find('[data-test-subj="detectionEngineStepAboutRuleName"] input') + .first() + .simulate('change', { target: { value: 'Test name text' } }); + + const result = await formHook(); + expect(result?.isValid).toEqual(false); + }); }); it('is invalid if no "name" is present', async () => { @@ -109,17 +110,18 @@ describe('StepAboutRuleComponent', () => { ); - if (!formHook) { - throw new Error('Form hook not set, but tests depend on it'); - } - - wrapper - .find('[data-test-subj="detectionEngineStepAboutRuleDescription"] textarea') - .first() - .simulate('change', { target: { value: 'Test description text' } }); - - const result = await formHook(); - expect(result?.isValid).toEqual(false); + await act(async () => { + if (!formHook) { + throw new Error('Form hook not set, but tests depend on it'); + } + + wrapper + .find('[data-test-subj="detectionEngineStepAboutRuleDescription"] textarea') + .first() + .simulate('change', { target: { value: 'Test description text' } }); + const result = await formHook(); + expect(result?.isValid).toEqual(false); + }); }); it('is valid if both "name" and "description" are present', async () => { @@ -136,10 +138,6 @@ describe('StepAboutRuleComponent', () => { ); - if (!formHook) { - throw new Error('Form hook not set, but tests depend on it'); - } - wrapper .find('[data-test-subj="detectionEngineStepAboutRuleDescription"] textarea') .first() @@ -173,12 +171,17 @@ describe('StepAboutRuleComponent', () => { ], }; - const result = await formHook(); - expect(result?.isValid).toEqual(true); - expect(result?.data).toEqual(expected); + await act(async () => { + if (!formHook) { + throw new Error('Form hook not set, but tests depend on it'); + } + const result = await formHook(); + expect(result?.isValid).toEqual(true); + expect(result?.data).toEqual(expected); + }); }); - test('it allows user to set the risk score as a number (and not a string)', async () => { + it('it allows user to set the risk score as a number (and not a string)', async () => { const wrapper = mount( { ); - if (!formHook) { - throw new Error('Form hook not set, but tests depend on it'); - } - wrapper .find('[data-test-subj="detectionEngineStepAboutRuleName"] input') .first() @@ -235,9 +234,14 @@ describe('StepAboutRuleComponent', () => { ], }; - const result = await formHook(); - expect(result?.isValid).toEqual(true); - expect(result?.data).toEqual(expected); + await act(async () => { + if (!formHook) { + throw new Error('Form hook not set, but tests depend on it'); + } + const result = await formHook(); + expect(result?.isValid).toEqual(true); + expect(result?.data).toEqual(expected); + }); }); it('does not modify the provided risk score until the user changes the severity', async () => { @@ -254,10 +258,6 @@ describe('StepAboutRuleComponent', () => { ); - if (!formHook) { - throw new Error('Form hook not set, but tests depend on it'); - } - wrapper .find('[data-test-subj="detectionEngineStepAboutRuleName"] input') .first() @@ -268,18 +268,23 @@ describe('StepAboutRuleComponent', () => { .first() .simulate('change', { target: { value: 'Test description text' } }); - const result = await formHook(); - expect(result?.isValid).toEqual(true); - expect(result?.data?.riskScore.value).toEqual(21); - - wrapper - .find('[data-test-subj="detectionEngineStepAboutRuleSeverity"] [data-test-subj="select"]') - .last() - .simulate('click'); - wrapper.find('button#medium').simulate('click'); - - const result2 = await formHook(); - expect(result2?.isValid).toEqual(true); - expect(result2?.data?.riskScore.value).toEqual(47); + await act(async () => { + if (!formHook) { + throw new Error('Form hook not set, but tests depend on it'); + } + const result = await formHook(); + expect(result?.isValid).toEqual(true); + expect(result?.data?.riskScore.value).toEqual(21); + + wrapper + .find('[data-test-subj="detectionEngineStepAboutRuleSeverity"] [data-test-subj="select"]') + .last() + .simulate('click'); + wrapper.find('button#medium').simulate('click'); + + const result2 = await formHook(); + expect(result2?.isValid).toEqual(true); + expect(result2?.data?.riskScore.value).toEqual(47); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/form.test.tsx b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/form.test.tsx index 591e1c81cd2ad..bae5a237bd124 100644 --- a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/form.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/form.test.tsx @@ -5,7 +5,7 @@ */ import React, { FormEvent } from 'react'; import { mount, ReactWrapper } from 'enzyme'; -import { act } from 'react-dom/test-utils'; +import { waitFor } from '@testing-library/react'; import { TestProviders } from '../../../common/mock'; import { ValueListsForm } from './form'; @@ -24,7 +24,7 @@ const mockSelectFile:

(container: ReactWrapper

, file: File) => Promise { const fileChange = container.find('EuiFilePicker').prop('onChange'); - act(() => { + await waitFor(() => { if (fileChange) { fileChange(({ item: () => file } as unknown) as FormEvent); } diff --git a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/modal.test.tsx b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/modal.test.tsx index ff743d1d5090a..dc40c997bc840 100644 --- a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/modal.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/modal.test.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { mount } from 'enzyme'; -import { act } from 'react-dom/test-utils'; +import { waitFor } from '@testing-library/react'; import { getListResponseMock } from '../../../../../lists/common/schemas/response/list_schema.mock'; import { exportList, useDeleteList, useFindLists, ListSchema } from '../../../shared_imports'; @@ -46,7 +46,6 @@ describe('ValueListsModal', () => { ); expect(container.find('EuiModal')).toHaveLength(0); - container.unmount(); }); it('renders modal if showModal is true', () => { @@ -57,7 +56,6 @@ describe('ValueListsModal', () => { ); expect(container.find('EuiModal')).toHaveLength(1); - container.unmount(); }); it('calls onClose when modal is closed', () => { @@ -71,7 +69,6 @@ describe('ValueListsModal', () => { container.find('button[data-test-subj="value-lists-modal-close-action"]').simulate('click'); expect(onClose).toHaveBeenCalled(); - container.unmount(); }); it('renders ValueListsForm and an EuiTable', () => { @@ -83,29 +80,27 @@ describe('ValueListsModal', () => { expect(container.find('ValueListsForm')).toHaveLength(1); expect(container.find('EuiBasicTable')).toHaveLength(1); - container.unmount(); }); describe('modal table actions', () => { - it('calls exportList when export is clicked', () => { + it('calls exportList when export is clicked', async () => { const container = mount( ); - act(() => { + await waitFor(() => { container .find('button[data-test-subj="action-export-value-list"]') .first() .simulate('click'); - container.unmount(); }); expect(exportList).toHaveBeenCalledWith(expect.objectContaining({ listId: 'some-list-id' })); }); - it('calls deleteList when delete is clicked', () => { + it('calls deleteList when delete is clicked', async () => { const deleteListMock = jest.fn(); (useDeleteList as jest.Mock).mockReturnValue({ start: deleteListMock, @@ -117,12 +112,11 @@ describe('ValueListsModal', () => { ); - act(() => { + await waitFor(() => { container .find('button[data-test-subj="action-delete-value-list"]') .first() .simulate('click'); - container.unmount(); }); expect(deleteListMock).toHaveBeenCalledWith(expect.objectContaining({ id: 'some-list-id' })); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx index a5d21d2847586..0982b5740b893 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { mount } from 'enzyme'; import { useParams } from 'react-router-dom'; - +import { waitFor } from '@testing-library/react'; import '../../../common/mock/match_media'; import { apolloClientObservable, @@ -80,7 +80,7 @@ describe('DetectionEnginePageComponent', () => { }); }); - it('renders correctly', () => { + it('renders correctly', async () => { const wrapper = mount( @@ -93,7 +93,8 @@ describe('DetectionEnginePageComponent', () => { ); - - expect(wrapper.find('FiltersGlobal').exists()).toBe(true); + await waitFor(() => { + expect(wrapper.find('FiltersGlobal').exists()).toBe(true); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx index 9f486dc11e99d..13c6985a30c2b 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx @@ -6,12 +6,11 @@ import React from 'react'; import { shallow, mount } from 'enzyme'; -import { act } from 'react-dom/test-utils'; import '../../../../../common/mock/match_media'; +import '../../../../../common/mock/formatted_relative'; import { TestProviders } from '../../../../../common/mock'; -// 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 { waitFor } from '@testing-library/react'; import { AllRules } from './index'; jest.mock('react-router-dom', () => { @@ -198,11 +197,9 @@ describe('AllRules', () => { ); - await act(async () => { - await waitFor(() => { - expect(wrapper.exists('[data-test-subj="monitoring-table"]')).toBeFalsy(); - expect(wrapper.exists('[data-test-subj="rules-table"]')).toBeTruthy(); - }); + await waitFor(() => { + expect(wrapper.exists('[data-test-subj="monitoring-table"]')).toBeFalsy(); + expect(wrapper.exists('[data-test-subj="rules-table"]')).toBeTruthy(); }); }); @@ -226,12 +223,10 @@ describe('AllRules', () => { const monitoringTab = wrapper.find('[data-test-subj="allRulesTableTab-monitoring"] button'); monitoringTab.simulate('click'); - await act(async () => { - await waitFor(() => { - wrapper.update(); - expect(wrapper.exists('[data-test-subj="monitoring-table"]')).toBeTruthy(); - expect(wrapper.exists('[data-test-subj="rules-table"]')).toBeFalsy(); - }); + await waitFor(() => { + wrapper.update(); + expect(wrapper.exists('[data-test-subj="monitoring-table"]')).toBeTruthy(); + expect(wrapper.exists('[data-test-subj="rules-table"]')).toBeFalsy(); }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx index 22c3c43fb2356..afa4777e74856 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx @@ -6,6 +6,7 @@ import React from 'react'; import { mount } from 'enzyme'; +import { waitFor } from '@testing-library/react'; import '../../../../../common/mock/match_media'; import { @@ -77,7 +78,7 @@ describe('RuleDetailsPageComponent', () => { }); }); - it('renders correctly', () => { + it('renders correctly', async () => { const wrapper = mount( @@ -93,7 +94,8 @@ describe('RuleDetailsPageComponent', () => { wrappingComponent: TestProviders, } ); - - expect(wrapper.find('[data-test-subj="header-page-title"]').exists()).toBe(true); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="header-page-title"]').exists()).toBe(true); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx index f11b0ac4ec3f8..8545e5da512bb 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import moment from 'moment'; import { GetStepsData, getDefineStepsData, @@ -31,6 +31,8 @@ import { } from './types'; describe('rule helpers', () => { + // @ts-ignore + moment.suppressDeprecationWarnings = true; describe('getStepsData', () => { test('returns object with about, define, schedule and actions step properties formatted', () => { const { diff --git a/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.test.tsx index 4f64cca45d162..4f2dff47af5a6 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.test.tsx @@ -6,8 +6,7 @@ import React from 'react'; -// we don't have the types for waitFor just yet, so using "as waitFor" until when we do -import { render, act, wait as waitFor } from '@testing-library/react'; +import { render, waitFor } from '@testing-library/react'; import { useFirstLastSeenHost } from '../../containers/hosts/first_last_seen'; import { TestProviders } from '../../../common/mock'; @@ -26,16 +25,6 @@ describe('FirstLastSeen Component', () => { const firstSeen = 'Apr 8, 2019 @ 16:09:40.692'; const lastSeen = 'Apr 8, 2019 @ 18:35:45.064'; - // Suppress warnings about "react-apollo" until we migrate to apollo@3 - /* eslint-disable no-console */ - const originalError = console.error; - beforeAll(() => { - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - }); - test('Loading', async () => { useFirstLastSeenHostMock.mockReturnValue([true, MOCKED_RESPONSE]); const { container } = render( @@ -66,13 +55,11 @@ describe('FirstLastSeen Component', () => { ); - await act(() => - waitFor(() => { - expect(container.innerHTML).toBe( - `

${firstSeen}
` - ); - }) - ); + await waitFor(() => { + expect(container.innerHTML).toBe( + `
${firstSeen}
` + ); + }); }); test('Last Seen', async () => { @@ -87,13 +74,11 @@ describe('FirstLastSeen Component', () => { /> ); - await act(() => - waitFor(() => { - expect(container.innerHTML).toBe( - `
${lastSeen}
` - ); - }) - ); + await waitFor(() => { + expect(container.innerHTML).toBe( + `
${lastSeen}
` + ); + }); }); test('First Seen is empty but not Last Seen', async () => { @@ -115,13 +100,11 @@ describe('FirstLastSeen Component', () => { ); - await act(() => - waitFor(() => { - expect(container.innerHTML).toBe( - `
${lastSeen}
` - ); - }) - ); + await waitFor(() => { + expect(container.innerHTML).toBe( + `
${lastSeen}
` + ); + }); }); test('Last Seen is empty but not First Seen', async () => { @@ -143,13 +126,11 @@ describe('FirstLastSeen Component', () => { ); - await act(() => - waitFor(() => { - expect(container.innerHTML).toBe( - `
${firstSeen}
` - ); - }) - ); + await waitFor(() => { + expect(container.innerHTML).toBe( + `
${firstSeen}
` + ); + }); }); test('First Seen With a bad date time string', async () => { @@ -170,11 +151,9 @@ describe('FirstLastSeen Component', () => { /> ); - await act(() => - waitFor(() => { - expect(container.textContent).toBe('something-invalid'); - }) - ); + await waitFor(() => { + expect(container.textContent).toBe('something-invalid'); + }); }); test('Last Seen With a bad date time string', async () => { @@ -195,10 +174,8 @@ describe('FirstLastSeen Component', () => { /> ); - await act(() => - waitFor(() => { - expect(container.textContent).toBe('something-invalid'); - }) - ); + await waitFor(() => { + expect(container.textContent).toBe('something-invalid'); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts index b4e00319485e9..1e3a92e6ec135 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts @@ -26,6 +26,10 @@ import { } from '../../../../common/store/test_utils'; import { getEndpointListPath } from '../../../common/routing'; +jest.mock('../../policy/store/policy_list/services/ingest', () => ({ + sendGetAgentPolicyList: () => Promise.resolve({ items: [] }), + sendGetEndpointSecurityPackage: () => Promise.resolve({}), +})); describe('endpoint list pagination: ', () => { let fakeCoreStart: jest.Mocked; let depsStart: DepsStartMock; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts index c4d2886f3e8e5..d19b3a0ce4177 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts @@ -24,8 +24,9 @@ import { endpointMiddlewareFactory } from './middleware'; import { getEndpointListPath } from '../../../common/routing'; jest.mock('../../policy/store/policy_list/services/ingest', () => ({ - sendGetEndpointSecurityPackage: () => Promise.resolve({}), sendGetAgentConfigList: () => Promise.resolve({ items: [] }), + sendGetAgentPolicyList: () => Promise.resolve({ items: [] }), + sendGetEndpointSecurityPackage: () => Promise.resolve({}), })); describe('endpoint list middleware', () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index 51a6be18471aa..bb4be42b04d4e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -7,7 +7,8 @@ import React from 'react'; import * as reactTestingLibrary from '@testing-library/react'; import { EndpointList } from './index'; -import '../../../../common/mock/match_media.ts'; +import '../../../../common/mock/match_media'; + import { mockEndpointDetailsApiResult, mockEndpointResultList, @@ -26,8 +27,25 @@ import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_da import { POLICY_STATUS_TO_HEALTH_COLOR, POLICY_STATUS_TO_TEXT } from './host_constants'; import { mockPolicyResultList } from '../../policy/store/policy_list/test_mock_utils'; -jest.mock('../../../../common/components/link_to'); +// not sure why this can't be imported from '../../../../common/mock/formatted_relative'; +// but sure enough it needs to be inline in this one file +jest.mock('@kbn/i18n/react', () => { + const originalModule = jest.requireActual('@kbn/i18n/react'); + const FormattedRelative = jest.fn().mockImplementation(() => '20 hours ago'); + return { + ...originalModule, + FormattedRelative, + }; +}); +jest.mock('../../../../common/components/link_to'); +jest.mock('../../policy/store/policy_list/services/ingest', () => { + const originalModule = jest.requireActual('../../policy/store/policy_list/services/ingest'); + return { + ...originalModule, + sendGetEndpointSecurityPackage: () => Promise.resolve({}), + }; +}); describe('when on the list page', () => { const docGenerator = new EndpointDocGenerator(); let render: () => ReturnType; @@ -35,7 +53,6 @@ describe('when on the list page', () => { let store: AppContextTestRender['store']; let coreStart: AppContextTestRender['coreStart']; let middlewareSpy: AppContextTestRender['middlewareSpy']; - beforeEach(() => { const mockedContext = createAppRootMockRenderer(); ({ history, store, coreStart, middlewareSpy } = mockedContext); diff --git a/x-pack/plugins/security_solution/public/management/pages/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/index.test.tsx index c04d3b1ec1a90..bb947310644f8 100644 --- a/x-pack/plugins/security_solution/public/management/pages/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/index.test.tsx @@ -15,10 +15,21 @@ jest.mock('../../common/hooks/endpoint/ingest_enabled'); describe('when in the Admistration tab', () => { let render: () => ReturnType; + let coreStart: AppContextTestRender['coreStart']; beforeEach(() => { const mockedContext = createAppRootMockRenderer(); + coreStart = mockedContext.coreStart; render = () => mockedContext.render(); + coreStart.http.get.mockImplementation(() => + Promise.resolve({ + response: [ + { + name: 'endpoint', + }, + ], + }) + ); }); it('should display the No Permissions view when Ingest is OFF', async () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/index.tsx b/x-pack/plugins/security_solution/public/management/pages/index.tsx index 959753cba7bd7..2d29e33c8d3d5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/index.tsx @@ -71,14 +71,12 @@ const NoPermissions = memo(() => { /> } body={ -

- - - -

+ + + } /> diff --git a/x-pack/plugins/security_solution/public/network/pages/network.test.tsx b/x-pack/plugins/security_solution/public/network/pages/network.test.tsx index 3539fe717e14d..f600c15f4c7d8 100644 --- a/x-pack/plugins/security_solution/public/network/pages/network.test.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/network.test.tsx @@ -7,7 +7,7 @@ import { mount } from 'enzyme'; import React from 'react'; import { Router } from 'react-router-dom'; - +import { waitFor } from '@testing-library/react'; import '../../common/mock/match_media'; import { Filter } from '../../../../../../src/plugins/data/common/es_query'; import { useSourcererScope } from '../../common/containers/sourcerer'; @@ -71,7 +71,7 @@ const mockProps = { hasMlUserPermissions: true, }; const mockUseSourcererScope = useSourcererScope as jest.Mock; -describe('rendering - rendering', () => { +describe('Network page - rendering', () => { test('it renders the Setup Instructions text when no index is available', () => { mockUseSourcererScope.mockReturnValue({ selectedPatterns: [], @@ -88,7 +88,7 @@ describe('rendering - rendering', () => { expect(wrapper.find('[data-test-subj="empty-page"]').exists()).toBe(true); }); - test('it DOES NOT render the Setup Instructions text when an index is available', () => { + test('it DOES NOT render the Setup Instructions text when an index is available', async () => { mockUseSourcererScope.mockReturnValue({ selectedPatterns: [], indicesExist: true, @@ -101,10 +101,12 @@ describe('rendering - rendering', () => { ); - expect(wrapper.find('[data-test-subj="empty-page"]').exists()).toBe(false); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="empty-page"]').exists()).toBe(false); + }); }); - test('it should add the new filters after init', () => { + test('it should add the new filters after init', async () => { const newFilters: Filter[] = [ { query: { @@ -157,12 +159,14 @@ describe('rendering - rendering', () => { ); - wrapper.update(); + await waitFor(() => { + wrapper.update(); - myStore.dispatch(inputsActions.setSearchBarFilter({ id: 'global', filters: newFilters })); - wrapper.update(); - expect(wrapper.find(NetworkRoutes).props().filterQuery).toEqual( - '{"bool":{"must":[],"filter":[{"match_all":{}},{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"host.name":"ItRocks"}}],"minimum_should_match":1}}]}}],"should":[],"must_not":[]}}' - ); + myStore.dispatch(inputsActions.setSearchBarFilter({ id: 'global', filters: newFilters })); + wrapper.update(); + expect(wrapper.find(NetworkRoutes).props().filterQuery).toEqual( + '{"bool":{"must":[],"filter":[{"match_all":{}},{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"host.name":"ItRocks"}}],"minimum_should_match":1}}]}}],"should":[],"must_not":[]}}' + ); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.test.tsx index 6f1b7e95e763d..704506d9813d9 100644 --- a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.test.tsx @@ -10,9 +10,9 @@ import React from 'react'; import { ThemeProvider } from 'styled-components'; import '../../../common/mock/match_media'; +import '../../../common/mock/react_beautiful_dnd'; import { useMatrixHistogram } from '../../../common/containers/matrix_histogram'; -// 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 { waitFor } from '@testing-library/react'; import { mockIndexPattern, TestProviders } from '../../../common/mock'; import { AlertsByCategory } from '.'; diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/endpoint_overview/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/host_overview/endpoint_overview/index.test.tsx index fee38ad3c6289..bb47fcd5512fc 100644 --- a/x-pack/plugins/security_solution/public/overview/components/host_overview/endpoint_overview/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/endpoint_overview/index.test.tsx @@ -8,6 +8,7 @@ import { mount } from 'enzyme'; import React from 'react'; import '../../../../common/mock/match_media'; +import '../../../../common/mock/react_beautiful_dnd'; import { TestProviders } from '../../../../common/mock'; import { EndpointOverview } from './index'; diff --git a/x-pack/plugins/security_solution/public/resolver/view/use_camera.test.tsx b/x-pack/plugins/security_solution/public/resolver/view/use_camera.test.tsx index 5406b444cee56..3d275a961bb2a 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/use_camera.test.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/use_camera.test.tsx @@ -5,8 +5,8 @@ */ import React, { FunctionComponent } from 'react'; -import { render, act, RenderResult, fireEvent } from '@testing-library/react'; -import { renderHook, act as hooksAct } from '@testing-library/react-hooks'; +import { render, waitFor, RenderResult, fireEvent } from '@testing-library/react'; +import { renderHook, act } from '@testing-library/react-hooks'; import { useCamera, useAutoUpdatingClientRect } from './use_camera'; import { Provider } from 'react-redux'; import * as selectors from '../store/selectors'; @@ -69,8 +69,8 @@ describe('useCamera on an unpainted element', () => { const topMargin = 20; const centerX = width / 2 + leftMargin; const centerY = height / 2 + topMargin; - beforeEach(() => { - act(() => { + beforeEach(async () => { + await waitFor(() => { simulator.controls.simulateElementResize(element, { width, height, @@ -97,11 +97,11 @@ describe('useCamera on an unpainted element', () => { const resizeObserverSpy = jest.spyOn(simulator.mock.ResizeObserver.prototype, 'observe'); let [rect, ref] = result.current; - hooksAct(() => ref(element)); + act(() => ref(element)); expect(resizeObserverSpy).toHaveBeenCalledWith(element); const div = document.createElement('div'); - hooksAct(() => ref(div)); + act(() => ref(div)); expect(resizeObserverSpy).toHaveBeenCalledWith(div); [rect, ref] = result.current; @@ -161,7 +161,7 @@ describe('useCamera on an unpainted element', () => { }); describe('when the camera begins animation', () => { let process: SafeResolverEvent; - beforeEach(() => { + beforeEach(async () => { const events: SafeResolverEvent[] = []; const numberOfEvents: number = 10; @@ -184,7 +184,7 @@ describe('useCamera on an unpainted element', () => { type: 'serverReturnedResolverData', payload: { result: tree, parameters: mockTreeFetcherParameters() }, }; - act(() => { + await waitFor(() => { store.dispatch(serverResponseAction); }); } else { @@ -205,7 +205,7 @@ describe('useCamera on an unpainted element', () => { process, }, }; - act(() => { + await waitFor(() => { store.dispatch(cameraAction); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/category.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/category.test.tsx index 62306046c7b8c..4f5e3c814751c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/category.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/category.test.tsx @@ -16,7 +16,6 @@ import { TestProviders } from '../../../common/mock'; import { useMountAppended } from '../../../common/utils/use_mount_appended'; import * as i18n from './translations'; - describe('Category', () => { const timelineId = 'test'; const selectedCategoryId = 'client'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.test.tsx index 90f4444562c5a..a3a19d3877fa1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.test.tsx @@ -6,7 +6,7 @@ import { mount } from 'enzyme'; import React from 'react'; - +import { waitFor } from '@testing-library/react'; import { mockBrowserFields } from '../../../common/containers/source/mock'; import { TestProviders } from '../../../common/mock'; import '../../../common/mock/match_media'; @@ -44,17 +44,19 @@ describe('FieldName', () => { ).toEqual(timestampFieldId); }); - test('it renders a copy to clipboard action menu item a user hovers over the name', () => { + test('it renders a copy to clipboard action menu item a user hovers over the name', async () => { const wrapper = mount( ); - wrapper.find('[data-test-subj="withHoverActionsButton"]').at(0).simulate('mouseenter'); - wrapper.update(); - jest.runAllTimers(); - wrapper.update(); - expect(wrapper.find('[data-test-subj="copy-to-clipboard"]').exists()).toBe(true); + await waitFor(() => { + wrapper.find('[data-test-subj="withHoverActionsButton"]').at(0).simulate('mouseenter'); + wrapper.update(); + jest.runAllTimers(); + wrapper.update(); + expect(wrapper.find('[data-test-subj="copy-to-clipboard"]').exists()).toBe(true); + }); }); test('it highlights the text specified by the `highlight` prop', () => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.test.tsx index a3c7440bece24..3bfeabc614ea9 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.test.tsx @@ -6,8 +6,10 @@ import { mount } from 'enzyme'; import React from 'react'; +import { waitFor } from '@testing-library/react'; import '../../../common/mock/match_media'; +import '../../../common/mock/react_beautiful_dnd'; import { mockBrowserFields } from '../../../common/containers/source/mock'; import { TestProviders } from '../../../common/mock'; @@ -15,19 +17,6 @@ import { FIELD_BROWSER_HEIGHT, FIELD_BROWSER_WIDTH } from './helpers'; import { StatefulFieldsBrowserComponent } from '.'; -// Suppress warnings about "react-beautiful-dnd" until we migrate to @testing-library/react -/* eslint-disable no-console */ -const originalError = console.error; -const originalWarn = console.warn; -beforeAll(() => { - console.warn = jest.fn(); - console.error = jest.fn(); -}); -afterAll(() => { - console.error = originalError; - console.warn = originalWarn; -}); - describe('StatefulFieldsBrowser', () => { const timelineId = 'test'; @@ -93,7 +82,7 @@ describe('StatefulFieldsBrowser', () => { beforeEach(() => { jest.useFakeTimers(); }); - test('it updates the selectedCategoryId state, which makes the category bold, when the user clicks a category name in the left hand side of the field browser', () => { + test('it updates the selectedCategoryId state, which makes the category bold, when the user clicks a category name in the left hand side of the field browser', async () => { const wrapper = mount( { wrapper.find('[data-test-subj="show-field-browser"]').first().simulate('click'); wrapper.find(`.field-browser-category-pane-auditd-${timelineId}`).first().simulate('click'); - - wrapper.update(); - expect( - wrapper.find(`.field-browser-category-pane-auditd-${timelineId}`).at(1) - ).toHaveStyleRule('font-weight', 'bold', { modifier: '.euiText' }); + await waitFor(() => { + wrapper.update(); + expect( + wrapper.find(`.field-browser-category-pane-auditd-${timelineId}`).at(1) + ).toHaveStyleRule('font-weight', 'bold', { modifier: '.euiText' }); + }); }); - test('it updates the selectedCategoryId state according to most fields returned', () => { + test('it updates the selectedCategoryId state according to most fields returned', async () => { const wrapper = mount( { ); - wrapper.find('[data-test-subj="show-field-browser"]').first().simulate('click'); - expect( - wrapper.find(`.field-browser-category-pane-cloud-${timelineId}`).at(1) - ).toHaveStyleRule('font-weight', 'normal', { modifier: '.euiText' }); - wrapper - .find('[data-test-subj="field-search"]') - .last() - .simulate('change', { target: { value: 'cloud' } }); - - jest.runOnlyPendingTimers(); - wrapper.update(); - expect( - wrapper.find(`.field-browser-category-pane-cloud-${timelineId}`).at(1) - ).toHaveStyleRule('font-weight', 'bold', { modifier: '.euiText' }); + await waitFor(() => { + wrapper.find('[data-test-subj="show-field-browser"]').first().simulate('click'); + expect( + wrapper.find(`.field-browser-category-pane-cloud-${timelineId}`).at(1) + ).toHaveStyleRule('font-weight', 'normal', { modifier: '.euiText' }); + wrapper + .find('[data-test-subj="field-search"]') + .last() + .simulate('change', { target: { value: 'cloud' } }); + + jest.runOnlyPendingTimers(); + wrapper.update(); + expect( + wrapper.find(`.field-browser-category-pane-cloud-${timelineId}`).at(1) + ).toHaveStyleRule('font-weight', 'bold', { modifier: '.euiText' }); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx index aa19fb6f68ed4..95ad5285507c5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx @@ -8,6 +8,7 @@ import { mount, shallow } from 'enzyme'; import { set } from '@elastic/safer-lodash-set/fp'; import React from 'react'; import { ActionCreator } from 'typescript-fsa'; +import '../../../common/mock/react_beautiful_dnd'; import { apolloClientObservable, diff --git a/x-pack/plugins/security_solution/public/timelines/components/notes/note_card/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/notes/note_card/index.test.tsx index a927627353f69..8aebc8519bcb7 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/notes/note_card/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/notes/note_card/index.test.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { ThemeProvider } from 'styled-components'; import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json'; +import '../../../../common/mock/formatted_relative'; import { NoteCard } from '.'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/notes/note_card/note_card_header.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/notes/note_card/note_card_header.test.tsx index fcb0d0294fa22..bc46c238c5ae8 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/notes/note_card/note_card_header.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/notes/note_card/note_card_header.test.tsx @@ -7,6 +7,7 @@ import moment from 'moment-timezone'; import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import '../../../../common/mock/formatted_relative'; import * as i18n from '../translations'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/notes/note_card/note_created.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/notes/note_card/note_created.test.tsx index 4bb9a6b666a87..7b51a9eaa1a2d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/notes/note_card/note_created.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/notes/note_card/note_created.test.tsx @@ -7,6 +7,7 @@ import moment from 'moment-timezone'; import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import '../../../../common/mock/formatted_relative'; import { NoteCreated } from './note_created'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/notes/note_cards/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/notes/note_cards/index.test.tsx index 952295d0858ee..5506514999f35 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/notes/note_cards/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/notes/note_cards/index.test.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { ThemeProvider } from 'styled-components'; import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json'; +import '../../../../common/mock/formatted_relative'; import { Note } from '../../../../common/lib/note'; 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 64b9db59467e1..f6ac1ab4cec3e 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 @@ -14,11 +14,11 @@ import { waitFor } from '@testing-library/react'; import { useHistory, useParams } from 'react-router-dom'; import '../../../common/mock/match_media'; +import '../../../common/mock/formatted_relative'; 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 { TestProviders, apolloClient, mockOpenTimelineQueryResults } from '../../../common/mock'; import { getTimelineTabsUrl } from '../../../common/components/link_to'; import { DEFAULT_SEARCH_RESULTS_PER_PAGE } from '../../pages/timelines_page'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.test.tsx index 3f737a5ba73db..cb0eba910c342 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.test.tsx @@ -10,6 +10,7 @@ import moment from 'moment'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import React from 'react'; import { ThemeProvider } from 'styled-components'; +import '../../../../common/mock/formatted_relative'; import { mockTimelineResults } from '../../../../common/mock/timeline_results'; import { OpenTimelineResult, TimelineResultNote } from '../types'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/note_preview.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/note_preview.test.tsx index e23c9b7fe2083..feabe46edfa82 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/note_preview.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/note_preview.test.tsx @@ -8,6 +8,7 @@ import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import React from 'react'; import { ThemeProvider } from 'styled-components'; +import '../../../../common/mock/formatted_relative'; import { getEmptyValue } from '../../../../common/components/empty_value'; import { NotePreview } from './note_preview'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.test.tsx index 3d5c5f60d1d9b..2d5849463270b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.test.tsx @@ -9,7 +9,7 @@ import { cloneDeep } from 'lodash/fp'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import React from 'react'; import { ThemeProvider } from 'styled-components'; -import { act } from '@testing-library/react'; +import { waitFor } from '@testing-library/react'; import '../../../common/mock/match_media'; import { DEFAULT_SEARCH_RESULTS_PER_PAGE } from '../../pages/timelines_page'; @@ -293,7 +293,7 @@ describe('OpenTimeline', () => { ); wrapper.find('[data-test-subj="utility-bar-action"]').find('EuiLink').simulate('click'); - await act(async () => { + await waitFor(() => { expect( wrapper.find('[data-test-subj="export-timeline-action"]').first().prop('disabled') ).toEqual(true); @@ -313,7 +313,7 @@ describe('OpenTimeline', () => { ); wrapper.find('[data-test-subj="utility-bar-action"]').find('EuiLink').simulate('click'); - await act(async () => { + await waitFor(() => { expect( wrapper.find('[data-test-subj="delete-timeline-action"]').first().prop('disabled') ).toEqual(true); @@ -333,7 +333,7 @@ describe('OpenTimeline', () => { ); wrapper.find('[data-test-subj="utility-bar-action"]').find('EuiLink').simulate('click'); - await act(async () => { + await waitFor(() => { expect( wrapper.find('[data-test-subj="export-timeline-action"]').first().prop('disabled') ).toEqual(false); @@ -353,7 +353,7 @@ describe('OpenTimeline', () => { ); wrapper.find('[data-test-subj="utility-bar-action"]').find('EuiLink').simulate('click'); - await act(async () => { + await waitFor(() => { expect( wrapper.find('[data-test-subj="delete-timeline-action"]').first().prop('disabled') ).toEqual(false); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/open_timeline_modal_body.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/open_timeline_modal_body.test.tsx index 9632b0e6ecea4..2744e0b42efce 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/open_timeline_modal_body.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/open_timeline_modal_body.test.tsx @@ -49,7 +49,7 @@ describe('OpenTimelineModal', () => { sortField: DEFAULT_SORT_FIELD, timelineType: TimelineType.default, timelineStatus: TimelineStatus.active, - templateTimelineFilter: [
], + templateTimelineFilter: [
], title, totalSearchResultsCount: mockSearchResults.length, }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/open_timeline_modal_button.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/open_timeline_modal_button.test.tsx index ea587aeca2061..35b6c99c04176 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/open_timeline_modal_button.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/open_timeline_modal_button.test.tsx @@ -10,8 +10,7 @@ import React from 'react'; import { MockedProvider } from 'react-apollo/test-utils'; import { ThemeProvider } from 'styled-components'; -// 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 { waitFor } from '@testing-library/react'; import { TestProviders } from '../../../../common/mock/test_providers'; import { mockOpenTimelineQueryResults } from '../../../../common/mock/timeline_results'; import * as i18n from '../translations'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/common_columns.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/common_columns.test.tsx index b8b2630e09c6e..18270a30eb0e1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/common_columns.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/common_columns.test.tsx @@ -12,6 +12,7 @@ import { ThemeProvider } from 'styled-components'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import '../../../../common/mock/match_media'; +import '../../../../common/mock/formatted_relative'; import { getEmptyValue } from '../../../../common/components/empty_value'; import { OpenTimelineResult } from '../types'; import { mockTimelineResults } from '../../../../common/mock/timeline_results'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.tsx index efad85775a9e4..a08820e9435d3 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header_tooltip_content/index.tsx @@ -20,13 +20,14 @@ const IconType = styled(EuiIcon)` `; IconType.displayName = 'IconType'; -const P = styled.p` +const P = styled.span` margin-bottom: 5px; `; P.displayName = 'P'; const ToolTipTableMetadata = styled.span` margin-right: 5px; + display: block; `; ToolTipTableMetadata.displayName = 'ToolTipTableMetadata'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx index 23a449cca972d..bc32eaca081bb 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx @@ -16,8 +16,7 @@ import { TestProviders } from '../../../../common/mock/test_providers'; import { Body, BodyProps } from '.'; import { columnRenderers, rowRenderers } from './renderers'; import { Sort } from './sort'; -// 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 { waitFor } from '@testing-library/react'; import { useMountAppended } from '../../../../common/utils/use_mount_appended'; import { SELECTOR_TIMELINE_BODY_CLASS_NAME, TimelineBody } from '../styles'; import { TimelineType } from '../../../../../common/types/timeline'; @@ -201,22 +200,6 @@ describe('Body', () => { wrapper.update(); }; - // We are doing that because we need to wrapped this component with redux - // and redux does not like to be updated and since we need to update our - // child component (BODY) and we do not want to scare anyone with this error - // we are hiding it!!! - // eslint-disable-next-line no-console - const originalError = console.error; - beforeAll(() => { - // eslint-disable-next-line no-console - console.error = (...args: string[]) => { - if (/ does not support changing `store` on the fly/.test(args[0])) { - return; - } - originalError.call(console, ...args); - }; - }); - beforeEach(() => { dispatchAddNoteToEvent.mockClear(); dispatchOnPinEvent.mockClear(); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx index efb19275336db..19344a7fd7c9b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx @@ -22,8 +22,7 @@ import { useThrottledResizeObserver } from '../../../../common/components/utils' import { Properties, showDescriptionThreshold, showNotesThreshold } from '.'; import { setInsertTimeline } from '../../../store/timeline/actions'; export { nextTick } from '../../../../../../../test_utils'; - -import { act } from 'react-dom/test-utils'; +import { waitFor } from '@testing-library/react'; jest.mock('../../../../common/components/link_to'); @@ -372,18 +371,16 @@ describe('Properties', () => { wrapper.find('[data-test-subj="settings-gear"]').at(0).simulate('click'); wrapper.find('[data-test-subj="attach-timeline-case"]').first().simulate('click'); - await act(async () => { - await Promise.resolve({}); + await waitFor(() => { + expect(mockNavigateToApp).toBeCalledWith('securitySolution:case', { path: '/create' }); + expect(mockDispatch).toBeCalledWith( + setInsertTimeline({ + timelineId: defaultProps.timelineId, + timelineSavedObjectId: '1', + timelineTitle: 'coolness', + }) + ); }); - - expect(mockNavigateToApp).toBeCalledWith('securitySolution:case', { path: '/create' }); - expect(mockDispatch).toBeCalledWith( - setInsertTimeline({ - timelineId: defaultProps.timelineId, - timelineSavedObjectId: '1', - timelineTitle: 'coolness', - }) - ); }); test('insert timeline - existing case', async () => { @@ -397,9 +394,8 @@ describe('Properties', () => { wrapper.find('[data-test-subj="settings-gear"]').at(0).simulate('click'); wrapper.find('[data-test-subj="attach-timeline-existing-case"]').first().simulate('click'); - await act(async () => { - await Promise.resolve({}); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="all-cases-modal"]').exists()).toBeTruthy(); }); - expect(wrapper.find('[data-test-subj="all-cases-modal"]').exists()).toBeTruthy(); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.test.tsx index 7da3cf940da50..c21592bed12e0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.test.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { act as actDom } from 'react-dom/test-utils'; +import { waitFor } from '@testing-library/react'; import { renderHook, act } from '@testing-library/react-hooks'; import { mount, shallow } from 'enzyme'; @@ -86,7 +86,7 @@ describe('useCreateTimelineButton', () => { await waitForNextUpdate(); const button = result.current.getButton({ outline: false, title: 'mock title' }); - actDom(() => { + await waitFor(() => { const wrapper = mount(button); wrapper.update(); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.test.tsx index 6c8fd4975c657..87956647c11f1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.test.tsx @@ -24,22 +24,6 @@ const mockUiSettingsForFilterManager = coreMock.createStart().uiSettings; jest.mock('../../../../common/lib/kibana'); describe('Timeline QueryBar ', () => { - // We are doing that because we need to wrapped this component with redux - // and redux does not like to be updated and since we need to update our - // child component (BODY) and we do not want to scare anyone with this error - // we are hiding it!!! - // eslint-disable-next-line no-console - const originalError = console.error; - beforeAll(() => { - // eslint-disable-next-line no-console - console.error = (...args: string[]) => { - if (/ does not support changing `store` on the fly/.test(args[0])) { - return; - } - originalError.call(console, ...args); - }; - }); - const mockApplyKqlFilterQuery = jest.fn(); const mockSetFilters = jest.fn(); const mockSetKqlFilterQueryDraft = jest.fn(); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.test.tsx index b64ca0ccc0b35..519372d0ac797 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.test.tsx @@ -7,31 +7,18 @@ import React from 'react'; import { shallow, ShallowWrapper, mount } from 'enzyme'; import { TimelineType } from '../../../../../common/types/timeline'; import { SortFieldTimeline, Direction } from '../../../../graphql/types'; -import { SearchProps } from './'; +import { SelectableTimeline, ORIGINAL_PAGE_SIZE, SearchProps } from './'; +const mockFetchAllTimeline = jest.fn(); +jest.mock('../../../containers/all', () => { + return { + useGetAllTimeline: jest.fn(() => ({ + fetchAllTimeline: mockFetchAllTimeline, + timelines: [], + })), + }; +}); describe('SelectableTimeline', () => { - const mockFetchAllTimeline = jest.fn(); - const mockEuiSelectable = jest.fn(); - - jest.doMock('@elastic/eui', () => { - const originalModule = jest.requireActual('@elastic/eui'); - return { - ...originalModule, - EuiSelectable: mockEuiSelectable.mockImplementation(({ children }) =>
{children}
), - }; - }); - - jest.doMock('../../../containers/all', () => { - return { - useGetAllTimeline: jest.fn(() => ({ - fetchAllTimeline: mockFetchAllTimeline, - timelines: [], - })), - }; - }); - - const { SelectableTimeline, ORIGINAL_PAGE_SIZE } = jest.requireActual('./'); - const props = { hideUntitled: false, getSelectableOptions: jest.fn(), diff --git a/x-pack/plugins/security_solution/public/timelines/containers/api.test.ts b/x-pack/plugins/security_solution/public/timelines/containers/api.test.ts index 8ef740f7bc1d7..a439699d27f6d 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/api.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/api.test.ts @@ -11,157 +11,101 @@ import { ImportDataProps } from '../../detections/containers/detection_engine/ru jest.mock('../../common/lib/kibana', () => { return { - KibanaServices: { get: jest.fn(() => ({ http: { fetch: jest.fn() } })) }, + KibanaServices: { + get: jest.fn(() => ({ + http: { + fetch: jest.fn(), + }, + })), + }, }; }); +const timelineData = { + columns: [ + { + columnHeaderType: 'not-filtered', + id: '@timestamp', + }, + { + columnHeaderType: 'not-filtered', + id: 'message', + }, + { + columnHeaderType: 'not-filtered', + id: 'event.category', + }, + { + columnHeaderType: 'not-filtered', + id: 'event.action', + }, + { + columnHeaderType: 'not-filtered', + id: 'host.name', + }, + { + columnHeaderType: 'not-filtered', + id: 'source.ip', + }, + { + columnHeaderType: 'not-filtered', + id: 'destination.ip', + }, + { + columnHeaderType: 'not-filtered', + id: 'user.name', + }, + ], + dataProviders: [], + description: 'x', + eventType: 'all', + filters: [], + kqlMode: 'filter', + kqlQuery: { + filterQuery: null, + }, + title: '', + timelineType: TimelineType.default, + templateTimelineVersion: null, + templateTimelineId: null, + dateRange: { + start: 1590998565409, + end: 1591084965409, + }, + savedQueryId: null, + sort: { + columnId: '@timestamp', + sortDirection: 'desc', + }, + status: TimelineStatus.active, +}; +const mockPatchTimelineResponse = { + data: { + persistTimeline: { + code: 200, + message: 'success', + timeline: { + ...timelineData, + savedObjectId: '9d5693e0-a42a-11ea-b8f4-c5434162742a', + version: 'WzM0NSwxXQ==', + }, + }, + }, +}; describe('persistTimeline', () => { describe('create draft timeline', () => { const timelineId = null; const initialDraftTimeline = { - columns: [ - { - columnHeaderType: 'not-filtered', - id: '@timestamp', - }, - { - columnHeaderType: 'not-filtered', - id: 'message', - }, - { - columnHeaderType: 'not-filtered', - id: 'event.category', - }, - { - columnHeaderType: 'not-filtered', - id: 'event.action', - }, - { - columnHeaderType: 'not-filtered', - id: 'host.name', - }, - { - columnHeaderType: 'not-filtered', - id: 'source.ip', - }, - { - columnHeaderType: 'not-filtered', - id: 'destination.ip', - }, - { - columnHeaderType: 'not-filtered', - id: 'user.name', - }, - ], - dataProviders: [], - description: 'x', - eventType: 'all', - filters: [], - kqlMode: 'filter', - kqlQuery: { - filterQuery: null, - }, - title: '', - timelineType: TimelineType.default, - templateTimelineVersion: null, - templateTimelineId: null, - dateRange: { - start: 1590998565409, - end: 1591084965409, - }, - savedQueryId: null, - sort: { - columnId: '@timestamp', - sortDirection: 'desc', - }, + ...timelineData, status: TimelineStatus.draft, }; const mockDraftResponse = { data: { persistTimeline: { timeline: { + ...initialDraftTimeline, savedObjectId: '9d5693e0-a42a-11ea-b8f4-c5434162742a', version: 'WzMzMiwxXQ==', - columns: [ - { columnHeaderType: 'not-filtered', id: '@timestamp' }, - { columnHeaderType: 'not-filtered', id: 'message' }, - { columnHeaderType: 'not-filtered', id: 'event.category' }, - { columnHeaderType: 'not-filtered', id: 'event.action' }, - { columnHeaderType: 'not-filtered', id: 'host.name' }, - { columnHeaderType: 'not-filtered', id: 'source.ip' }, - { columnHeaderType: 'not-filtered', id: 'destination.ip' }, - { columnHeaderType: 'not-filtered', id: 'user.name' }, - ], - dataProviders: [], - description: '', - eventType: 'all', - filters: [], - kqlMode: 'filter', - timelineType: 'default', - kqlQuery: { filterQuery: null }, - title: '', - sort: { columnId: '@timestamp', sortDirection: 'desc' }, - status: 'draft', - created: 1591091394733, - createdBy: 'angela', - updated: 1591091394733, - updatedBy: 'angela', - templateTimelineId: null, - templateTimelineVersion: null, - dateRange: { start: 1590998565409, end: 1591084965409 }, - savedQueryId: null, - favorite: [], - eventIdToNoteIds: [], - noteIds: [], - notes: [], - pinnedEventIds: [], - pinnedEventsSaveObject: [], - }, - }, - }, - }; - const mockPatchTimelineResponse = { - data: { - persistTimeline: { - code: 200, - message: 'success', - timeline: { - savedObjectId: '9d5693e0-a42a-11ea-b8f4-c5434162742a', - version: 'WzM0NSwxXQ==', - columns: [ - { columnHeaderType: 'not-filtered', id: '@timestamp' }, - { columnHeaderType: 'not-filtered', id: 'message' }, - { columnHeaderType: 'not-filtered', id: 'event.category' }, - { columnHeaderType: 'not-filtered', id: 'event.action' }, - { columnHeaderType: 'not-filtered', id: 'host.name' }, - { columnHeaderType: 'not-filtered', id: 'source.ip' }, - { columnHeaderType: 'not-filtered', id: 'destination.ip' }, - { columnHeaderType: 'not-filtered', id: 'user.name' }, - ], - dataProviders: [], - description: 'x', - eventType: 'all', - filters: [], - kqlMode: 'filter', - timelineType: 'default', - kqlQuery: { filterQuery: null }, - title: '', - sort: { columnId: '@timestamp', sortDirection: 'desc' }, - status: 'draft', - created: 1591092702804, - createdBy: 'angela', - updated: 1591092705206, - updatedBy: 'angela', - templateTimelineId: null, - templateTimelineVersion: null, - dateRange: { start: 1590998565409, end: 1591084965409 }, - savedQueryId: null, - favorite: [], - eventIdToNoteIds: [], - noteIds: [], - notes: [], - pinnedEventIds: [], - pinnedEventsSaveObject: [], }, }, }, @@ -223,61 +167,7 @@ describe('persistTimeline', () => { describe('create draft timeline in read-only permission', () => { const timelineId = null; const initialDraftTimeline = { - columns: [ - { - columnHeaderType: 'not-filtered', - id: '@timestamp', - }, - { - columnHeaderType: 'not-filtered', - id: 'message', - }, - { - columnHeaderType: 'not-filtered', - id: 'event.category', - }, - { - columnHeaderType: 'not-filtered', - id: 'event.action', - }, - { - columnHeaderType: 'not-filtered', - id: 'host.name', - }, - { - columnHeaderType: 'not-filtered', - id: 'source.ip', - }, - { - columnHeaderType: 'not-filtered', - id: 'destination.ip', - }, - { - columnHeaderType: 'not-filtered', - id: 'user.name', - }, - ], - dataProviders: [], - description: 'x', - eventType: 'all', - filters: [], - kqlMode: 'filter', - kqlQuery: { - filterQuery: null, - }, - title: '', - timelineType: TimelineType.default, - templateTimelineVersion: null, - templateTimelineId: null, - dateRange: { - start: 1590998565409, - end: 1591084965409, - }, - savedQueryId: null, - sort: { - columnId: '@timestamp', - sortDirection: 'desc', - }, + ...timelineData, status: TimelineStatus.draft, }; @@ -325,104 +215,14 @@ describe('persistTimeline', () => { describe('create active timeline (import)', () => { const timelineId = null; - const importTimeline = { - columns: [ - { - columnHeaderType: 'not-filtered', - id: '@timestamp', - }, - { - columnHeaderType: 'not-filtered', - id: 'message', - }, - { - columnHeaderType: 'not-filtered', - id: 'event.category', - }, - { - columnHeaderType: 'not-filtered', - id: 'event.action', - }, - { - columnHeaderType: 'not-filtered', - id: 'host.name', - }, - { - columnHeaderType: 'not-filtered', - id: 'source.ip', - }, - { - columnHeaderType: 'not-filtered', - id: 'destination.ip', - }, - { - columnHeaderType: 'not-filtered', - id: 'user.name', - }, - ], - dataProviders: [], - description: 'x', - eventType: 'all', - filters: [], - kqlMode: 'filter', - kqlQuery: { - filterQuery: null, - }, - title: '', - timelineType: TimelineType.default, - templateTimelineVersion: null, - templateTimelineId: null, - dateRange: { - start: 1590998565409, - end: 1591084965409, - }, - savedQueryId: null, - sort: { - columnId: '@timestamp', - sortDirection: 'desc', - }, - status: TimelineStatus.active, - }; + const importTimeline = timelineData; const mockPostTimelineResponse = { data: { persistTimeline: { timeline: { + ...timelineData, savedObjectId: '9d5693e0-a42a-11ea-b8f4-c5434162742a', version: 'WzMzMiwxXQ==', - columns: [ - { columnHeaderType: 'not-filtered', id: '@timestamp' }, - { columnHeaderType: 'not-filtered', id: 'message' }, - { columnHeaderType: 'not-filtered', id: 'event.category' }, - { columnHeaderType: 'not-filtered', id: 'event.action' }, - { columnHeaderType: 'not-filtered', id: 'host.name' }, - { columnHeaderType: 'not-filtered', id: 'source.ip' }, - { columnHeaderType: 'not-filtered', id: 'destination.ip' }, - { columnHeaderType: 'not-filtered', id: 'user.name' }, - ], - dataProviders: [], - description: '', - eventType: 'all', - filters: [], - kqlMode: 'filter', - timelineType: 'default', - kqlQuery: { filterQuery: null }, - title: '', - sort: { columnId: '@timestamp', sortDirection: 'desc' }, - status: 'draft', - created: 1591091394733, - createdBy: 'angela', - updated: 1591091394733, - updatedBy: 'angela', - templateTimelineId: null, - templateTimelineVersion: null, - dateRange: { start: 1590998565409, end: 1591084965409 }, - savedQueryId: null, - favorite: [], - eventIdToNoteIds: [], - noteIds: [], - notes: [], - pinnedEventIds: [], - pinnedEventsSaveObject: [], }, }, }, @@ -462,104 +262,16 @@ describe('persistTimeline', () => { describe('update active timeline', () => { const timelineId = '9d5693e0-a42a-11ea-b8f4-c5434162742a'; - const inputTimeline = { - columns: [ - { - columnHeaderType: 'not-filtered', - id: '@timestamp', - }, - { - columnHeaderType: 'not-filtered', - id: 'message', - }, - { - columnHeaderType: 'not-filtered', - id: 'event.category', - }, - { - columnHeaderType: 'not-filtered', - id: 'event.action', - }, - { - columnHeaderType: 'not-filtered', - id: 'host.name', - }, - { - columnHeaderType: 'not-filtered', - id: 'source.ip', - }, - { - columnHeaderType: 'not-filtered', - id: 'destination.ip', - }, - { - columnHeaderType: 'not-filtered', - id: 'user.name', - }, - ], - dataProviders: [], - description: 'x', - eventType: 'all', - filters: [], - kqlMode: 'filter', - kqlQuery: { - filterQuery: null, - }, - title: '', - timelineType: TimelineType.default, - templateTimelineVersion: null, - templateTimelineId: null, - dateRange: { - start: 1590998565409, - end: 1591084965409, - }, - savedQueryId: null, - sort: { - columnId: '@timestamp', - sortDirection: 'desc', - }, - status: TimelineStatus.active, - }; - const mockPatchTimelineResponse = { + const inputTimeline = timelineData; + const mockPatchTimelineResponseNew = { data: { persistTimeline: { timeline: { - savedObjectId: '9d5693e0-a42a-11ea-b8f4-c5434162742a', + ...mockPatchTimelineResponse.data.persistTimeline.timeline, version: 'WzMzMiwxXQ==', - columns: [ - { columnHeaderType: 'not-filtered', id: '@timestamp' }, - { columnHeaderType: 'not-filtered', id: 'message' }, - { columnHeaderType: 'not-filtered', id: 'event.category' }, - { columnHeaderType: 'not-filtered', id: 'event.action' }, - { columnHeaderType: 'not-filtered', id: 'host.name' }, - { columnHeaderType: 'not-filtered', id: 'source.ip' }, - { columnHeaderType: 'not-filtered', id: 'destination.ip' }, - { columnHeaderType: 'not-filtered', id: 'user.name' }, - ], - dataProviders: [], - description: '', - eventType: 'all', - filters: [], - kqlMode: 'filter', - timelineType: 'default', - kqlQuery: { filterQuery: null }, - title: '', - sort: { columnId: '@timestamp', sortDirection: 'desc' }, - status: 'draft', - created: 1591091394733, - createdBy: 'angela', - updated: 1591091394733, - updatedBy: 'angela', - templateTimelineId: null, - templateTimelineVersion: null, - dateRange: { start: 1590998565409, end: 1591084965409 }, - savedQueryId: null, - favorite: [], - eventIdToNoteIds: [], - noteIds: [], - notes: [], - pinnedEventIds: [], - pinnedEventsSaveObject: [], + description: 'x', + created: 1591092702804, + updated: 1591092705206, }, }, }, @@ -578,7 +290,7 @@ describe('persistTimeline', () => { http: { fetch: fetchMock, post: postMock, - patch: patchMock.mockReturnValue(mockPatchTimelineResponse), + patch: patchMock.mockReturnValue(mockPatchTimelineResponseNew), }, }); api.persistTimeline({ timelineId, timeline: inputTimeline, version }); @@ -674,7 +386,7 @@ describe('getDraftTimeline', () => { (KibanaServices.get as jest.Mock).mockReturnValue({ http: { - get: getMock, + get: getMock.mockImplementation(() => Promise.resolve(mockPatchTimelineResponse)), }, }); api.getDraftTimeline(timelineType); @@ -696,7 +408,7 @@ describe('cleanDraftTimeline', () => { (KibanaServices.get as jest.Mock).mockReturnValue({ http: { - post: postMock, + post: postMock.mockImplementation(() => Promise.resolve(mockPatchTimelineResponse)), }, }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx index 6507603d30444..1f79b26394a69 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { shallow } from 'enzyme'; // we don't have the types for waitFor just yet, so using "as waitFor" for when we do -import { wait as waitFor } from '@testing-library/react'; +import { waitFor } from '@testing-library/react'; import '../../../common/mock/match_media'; import { mockGlobalState,