Skip to content

Commit

Permalink
[Security Solutions][Cases - Timeline] Fix bug when adding a timeline…
Browse files Browse the repository at this point in the history
… to a case (#76967)

Co-authored-by: Gloria Hornero <[email protected]>
Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
3 people authored Sep 15, 2020
1 parent 6dd558e commit 7524891
Show file tree
Hide file tree
Showing 22 changed files with 2,941 additions and 122 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import { TIMELINE_DESCRIPTION, TIMELINE_QUERY, TIMELINE_TITLE } from '../screens

import { goToCaseDetails, goToCreateNewCase } from '../tasks/all_cases';
import { openCaseTimeline } from '../tasks/case_details';
import { backToCases, createNewCase } from '../tasks/create_new_case';
import { backToCases, createNewCaseWithTimeline } from '../tasks/create_new_case';
import { loginAndWaitForPageWithoutDateRange } from '../tasks/login';
import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver';

Expand All @@ -58,7 +58,7 @@ describe('Cases', () => {
it('Creates a new case with timeline and opens the timeline', () => {
loginAndWaitForPageWithoutDateRange(CASES_URL);
goToCreateNewCase();
createNewCase(case1);
createNewCaseWithTimeline(case1);
backToCases();

cy.get(ALL_CASES_PAGE_TITLE).should('have.text', 'Cases Beta');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* 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 { loginAndWaitForTimeline } from '../tasks/login';
import {
attachTimelineToNewCase,
attachTimelineToExistingCase,
addNewCase,
selectCase,
} from '../tasks/timeline';
import { DESCRIPTION_INPUT } from '../screens/create_new_case';
import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver';
import { caseTimeline, TIMELINE_CASE_ID } from '../objects/case';

describe('attach timeline to case', () => {
beforeEach(() => {
loginAndWaitForTimeline(caseTimeline.id);
});
context('without cases created', () => {
before(() => {
esArchiverLoad('timeline');
});

after(() => {
esArchiverUnload('timeline');
});

it('attach timeline to a new case', () => {
attachTimelineToNewCase();

cy.location('origin').then((origin) => {
cy.get(DESCRIPTION_INPUT).should(
'have.text',
`[${caseTimeline.title}](${origin}/app/security/timelines?timeline=(id:'${caseTimeline.id}',isOpen:!t))`
);
});
});

it('attach timeline to an existing case with no case', () => {
attachTimelineToExistingCase();
addNewCase();

cy.location('origin').then((origin) => {
cy.get(DESCRIPTION_INPUT).should(
'have.text',
`[${caseTimeline.title}](${origin}/app/security/timelines?timeline=(id:'${caseTimeline.id}',isOpen:!t))`
);
});
});
});

context('with cases created', () => {
before(() => {
esArchiverLoad('case_and_timeline');
});

after(() => {
esArchiverUnload('case_and_timeline');
});

it('attach timeline to an existing case', () => {
attachTimelineToExistingCase();
selectCase(TIMELINE_CASE_ID);

cy.location('origin').then((origin) => {
cy.get(DESCRIPTION_INPUT).should(
'have.text',
`[${caseTimeline.title}](${origin}/app/security/timelines?timeline=(id:'${caseTimeline.id}',isOpen:!t))`
);
});
});
});
});
7 changes: 5 additions & 2 deletions x-pack/plugins/security_solution/cypress/objects/case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { Timeline } from './timeline';
import { Timeline, TimelineWithId } from './timeline';

export interface TestCase {
name: string;
Expand All @@ -21,10 +21,11 @@ export interface Connector {
password: string;
}

const caseTimeline: Timeline = {
export const caseTimeline: TimelineWithId = {
title: 'SIEM test',
description: 'description',
query: 'host.name:*',
id: '0162c130-78be-11ea-9718-118a926974a4',
};

export const case1: TestCase = {
Expand All @@ -41,3 +42,5 @@ export const serviceNowConnector: Connector = {
username: 'Username Name',
password: 'password',
};

export const TIMELINE_CASE_ID = '68248e00-f689-11ea-9ab2-59238b522856';
4 changes: 4 additions & 0 deletions x-pack/plugins/security_solution/cypress/objects/timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ export interface Timeline {
description: string;
query: string;
}

export interface TimelineWithId extends Timeline {
id: string;
}
6 changes: 6 additions & 0 deletions x-pack/plugins/security_solution/cypress/screens/all_cases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/

export const ALL_CASES_CASE = (id: string) => {
return `[data-test-subj="cases-table-row-${id}"]`;
};

export const ALL_CASES_CLOSE_ACTION = '[data-test-subj="action-close"]';

export const ALL_CASES_CLOSED_CASES_COUNT = '[data-test-subj="closed-case-count"]';
Expand All @@ -14,6 +18,8 @@ export const ALL_CASES_COMMENTS_COUNT = '[data-test-subj="case-table-column-comm

export const ALL_CASES_CREATE_NEW_CASE_BTN = '[data-test-subj="createNewCaseBtn"]';

export const ALL_CASES_CREATE_NEW_CASE_TABLE_BTN = '[data-test-subj="cases-table-add-case"]';

export const ALL_CASES_DELETE_ACTION = '[data-test-subj="action-delete"]';

export const ALL_CASES_NAME = '[data-test-subj="case-details-link"]';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@

export const BACK_TO_CASES_BTN = '[data-test-subj="backToCases"]';

export const DESCRIPTION_INPUT =
'[data-test-subj="caseDescription"] [data-test-subj="textAreaInput"]';
export const DESCRIPTION_INPUT = '[data-test-subj="textAreaInput"]';

export const INSERT_TIMELINE_BTN = '[data-test-subj="insert-timeline-button"]';

Expand Down
11 changes: 11 additions & 0 deletions x-pack/plugins/security_solution/cypress/screens/timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,17 @@
* you may not use this file except in compliance with the Elastic License.
*/

export const ATTACH_TIMELINE_TO_NEW_CASE_ICON = '[data-test-subj="attach-timeline-case"]';

export const ATTACH_TIMELINE_TO_EXISTING_CASE_ICON =
'[data-test-subj="attach-timeline-existing-case"]';

export const BULK_ACTIONS = '[data-test-subj="utility-bar-action-button"]';

export const CASE = (id: string) => {
return `[data-test-subj="cases-table-row-${id}"]`;
};

export const CLOSE_TIMELINE_BTN = '[data-test-subj="close-timeline"]';

export const CREATE_NEW_TIMELINE = '[data-test-subj="timeline-new"]';
Expand All @@ -25,6 +34,8 @@ export const ID_FIELD = '[data-test-subj="timeline"] [data-test-subj="field-name

export const ID_TOGGLE_FIELD = '[data-test-subj="toggle-field-_id"]';

export const OPEN_TIMELINE_ICON = '[data-test-subj="open-timeline-button"]';

export const PIN_EVENT = '[data-test-subj="pin"]';

export const PROVIDER_BADGE = '[data-test-subj="providerBadge"]';
Expand Down
12 changes: 12 additions & 0 deletions x-pack/plugins/security_solution/cypress/tasks/create_new_case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ export const createNewCase = (newCase: TestCase) => {
});
cy.get(DESCRIPTION_INPUT).type(`${newCase.description} `, { force: true });

cy.get(SUBMIT_BTN).click({ force: true });
cy.get(LOADING_SPINNER).should('exist');
cy.get(LOADING_SPINNER).should('not.exist');
};

export const createNewCaseWithTimeline = (newCase: TestCase) => {
cy.get(TITLE_INPUT).type(newCase.name, { force: true });
newCase.tags.forEach((tag) => {
cy.get(TAGS_INPUT).type(`${tag}{enter}`, { force: true });
});
cy.get(DESCRIPTION_INPUT).type(`${newCase.description} `, { force: true });

cy.get(INSERT_TIMELINE_BTN).click({ force: true });
cy.get(TIMELINE_SEARCHBOX).type(`${newCase.timeline.title}{enter}`);
cy.get(TIMELINE).should('be.visible');
Expand Down
9 changes: 9 additions & 0 deletions x-pack/plugins/security_solution/cypress/tasks/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import * as yaml from 'js-yaml';
import { TIMELINE_FLYOUT_BODY } from '../screens/timeline';

/**
* Credentials in the `kibana.dev.yml` config file will be used to authenticate
Expand Down Expand Up @@ -143,3 +144,11 @@ export const loginAndWaitForPageWithoutDateRange = (url: string) => {
cy.visit(url);
cy.get('[data-test-subj="headerGlobalNav"]', { timeout: 120000 });
};

export const loginAndWaitForTimeline = (timelineId: string) => {
login();
cy.viewport('macbook-15');
cy.visit(`/app/security/timelines?timeline=(id:'${timelineId}',isOpen:!t)`);
cy.get('[data-test-subj="headerGlobalNav"]');
cy.get(TIMELINE_FLYOUT_BODY).should('be.visible');
};
28 changes: 28 additions & 0 deletions x-pack/plugins/security_solution/cypress/tasks/timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { ALL_CASES_CREATE_NEW_CASE_TABLE_BTN } from '../screens/all_cases';
import {
BULK_ACTIONS,
CLOSE_TIMELINE_BTN,
Expand All @@ -28,6 +29,10 @@ import {
TOGGLE_TIMELINE_EXPAND_EVENT,
REMOVE_COLUMN,
RESET_FIELDS,
ATTACH_TIMELINE_TO_NEW_CASE_ICON,
OPEN_TIMELINE_ICON,
ATTACH_TIMELINE_TO_EXISTING_CASE_ICON,
CASE,
} from '../screens/timeline';

import { drag, drop } from '../tasks/common';
Expand All @@ -44,6 +49,20 @@ export const addNameToTimeline = (name: string) => {
cy.get(TIMELINE_TITLE).should('have.attr', 'value', name);
};

export const addNewCase = () => {
cy.get(ALL_CASES_CREATE_NEW_CASE_TABLE_BTN).click();
};

export const attachTimelineToNewCase = () => {
cy.get(TIMELINE_SETTINGS_ICON).click({ force: true });
cy.get(ATTACH_TIMELINE_TO_NEW_CASE_ICON).click({ force: true });
};

export const attachTimelineToExistingCase = () => {
cy.get(TIMELINE_SETTINGS_ICON).click({ force: true });
cy.get(ATTACH_TIMELINE_TO_EXISTING_CASE_ICON).click({ force: true });
};

export const checkIdToggleField = () => {
cy.get(ID_HEADER_FIELD).should('not.exist');

Expand Down Expand Up @@ -85,6 +104,11 @@ export const openTimelineInspectButton = () => {
cy.get(TIMELINE_INSPECT_BUTTON).trigger('click', { force: true });
};

export const openTimelineFromSettings = () => {
cy.get(TIMELINE_SETTINGS_ICON).click({ force: true });
cy.get(OPEN_TIMELINE_ICON).click({ force: true });
};

export const openTimelineSettings = () => {
cy.get(TIMELINE_SETTINGS_ICON).trigger('click', { force: true });
};
Expand Down Expand Up @@ -132,6 +156,10 @@ export const resetFields = () => {
cy.get(RESET_FIELDS).click({ force: true });
};

export const selectCase = (caseId: string) => {
cy.get(CASE(caseId)).click();
};

export const waitForTimelinesPanelToBeLoaded = () => {
cy.get(TIMELINES_TABLE).should('exist');
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/
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';

jest.mock(
'../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form'
);
jest.mock(
'../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data'
);

export const mockFormHook = {
isSubmitted: false,
isSubmitting: false,
Expand Down Expand Up @@ -41,3 +47,4 @@ export const getFormMock = (sampleData: any) => ({
});

export const useFormMock = useForm as jest.Mock;
export const useFormDataMock = useFormData as jest.Mock;
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Router, routeData, mockHistory, mockLocation } from '../__mock__/router
import { useInsertTimeline } from '../../../timelines/components/timeline/insert_timeline_popover/use_insert_timeline';
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';
Expand All @@ -23,10 +24,15 @@ jest.mock(
'../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form'
);

jest.mock(
'../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data'
);

jest.mock('../../../timelines/components/timeline/insert_timeline_popover/use_insert_timeline');
jest.mock('../../containers/use_post_comment');

export const useFormMock = useForm as jest.Mock;
const useFormMock = useForm as jest.Mock;
const useFormDataMock = useFormData as jest.Mock;

const useInsertTimelineMock = useInsertTimeline as jest.Mock;
const usePostCommentMock = usePostComment as jest.Mock;
Expand Down Expand Up @@ -73,6 +79,7 @@ describe('AddComment ', () => {
useInsertTimelineMock.mockImplementation(() => defaultInsertTimeline);
usePostCommentMock.mockImplementation(() => defaultPostCommment);
useFormMock.mockImplementation(() => ({ form: formHookMock }));
useFormDataMock.mockImplementation(() => [{ comment: sampleData.comment }]);
jest.spyOn(routeData, 'useLocation').mockReturnValue(mockLocation);
});

Expand Down
Loading

0 comments on commit 7524891

Please sign in to comment.