Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solutions][Cases - Timeline] Fix bug when adding a timeline to a case #76967

Merged
merged 11 commits into from
Sep 15, 2020
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('#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 @@ -6,64 +6,17 @@

import React from 'react';
import { mount } from 'enzyme';
/* eslint-disable @kbn/eslint/module_migration */
import routeData from 'react-router';
/* eslint-enable @kbn/eslint/module_migration */
import { InsertTimelinePopoverComponent } from '.';

const mockDispatch = jest.fn();
jest.mock('react-redux', () => {
const reactRedux = jest.requireActual('react-redux');
return {
...reactRedux,
useDispatch: () => mockDispatch,
useSelector: jest
.fn()
.mockReturnValueOnce({
timelineId: 'timeline-id',
timelineSavedObjectId: '34578-3497-5893-47589-34759',
timelineTitle: 'Timeline title',
})
.mockReturnValue(null),
};
});
const mockLocation = {
pathname: '/apath',
hash: '',
search: '',
state: '',
};
const onTimelineChange = jest.fn();
const defaultProps = {
const props = {
isDisabled: false,
onTimelineChange,
};

describe('Insert timeline popover ', () => {
afterEach(() => {
jest.clearAllMocks();
});

it('should insert a timeline when passed in the router state', () => {
mount(<InsertTimelinePopoverComponent {...defaultProps} />);
expect(mockDispatch.mock.calls[0][0]).toEqual({
payload: { id: 'timeline-id', show: false },
type: 'x-pack/security_solution/local/timeline/SHOW_TIMELINE',
});
expect(onTimelineChange).toBeCalledWith(
'Timeline title',
'34578-3497-5893-47589-34759',
undefined
);
expect(mockDispatch.mock.calls[1][0]).toEqual({
payload: null,
type: 'x-pack/security_solution/local/timeline/SET_INSERT_TIMELINE',
});
});
it('should do nothing when router state', () => {
jest.spyOn(routeData, 'useLocation').mockReturnValue(mockLocation);
mount(<InsertTimelinePopoverComponent {...defaultProps} />);
expect(mockDispatch).toHaveBeenCalledTimes(0);
expect(onTimelineChange).toHaveBeenCalledTimes(0);
it('it renders', () => {
const wrapper = mount(<InsertTimelinePopoverComponent {...props} />);
expect(wrapper.find('[data-test-subj="insert-timeline-popover"]').exists()).toBeTruthy();
});
});
Loading