Skip to content

Commit

Permalink
[Security Solution] Add tests for import / export timelines (#75537)
Browse files Browse the repository at this point in the history
* add intergration test for install prebuilt timelines

* add integration tests

* add functional test for export timeline

* clean up

* asserts the content of the exported timeline

* update selector

* update selector

* reuses the timeline id from the expected exported file

Co-authored-by: Elastic Machine <[email protected]>
Co-authored-by: Gloria Hornero <[email protected]>
  • Loading branch information
3 people authored Sep 1, 2020
1 parent ec1c158 commit e7966e9
Show file tree
Hide file tree
Showing 10 changed files with 605 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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 { exportTimeline, waitForTimelinesPanelToBeLoaded } from '../tasks/timeline';
import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver';
import { loginAndWaitForPageWithoutDateRange } from '../tasks/login';

import { TIMELINES_URL } from '../urls/navigation';

const EXPECTED_EXPORTED_TIMELINE_PATH = 'cypress/test_files/expected_timelines_export.ndjson';

describe('Export timelines', () => {
before(() => {
esArchiverLoad('timeline');
cy.server();
cy.route('POST', '**api/timeline/_export?file_name=timelines_export.ndjson*').as('export');
});

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

it('Exports a custom timeline', () => {
loginAndWaitForPageWithoutDateRange(TIMELINES_URL);
waitForTimelinesPanelToBeLoaded();

cy.readFile(EXPECTED_EXPORTED_TIMELINE_PATH).then(($expectedExportedJson) => {
const parsedJson = JSON.parse($expectedExportedJson);
const timelineId = parsedJson.savedObjectId;
exportTimeline(timelineId);

cy.wait('@export').then((response) => {
cy.wrap(response.status).should('eql', 200);
cy.wrap(response.xhr.responseText).should('eql', $expectedExportedJson);
});
});
});
});
10 changes: 10 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,13 +4,17 @@
* you may not use this file except in compliance with the Elastic License.
*/

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

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

export const CREATE_NEW_TIMELINE = '[data-test-subj="timeline-new"]';

export const DRAGGABLE_HEADER =
'[data-test-subj="events-viewer-panel"] [data-test-subj="headers-group"] [data-test-subj="draggable-header"]';

export const EXPORT_TIMELINE_ACTION = '[data-test-subj="export-timeline-action"]';

export const HEADER = '[data-test-subj="header"]';

export const HEADERS_GROUP = '[data-test-subj="headers-group"]';
Expand Down Expand Up @@ -41,6 +45,10 @@ export const TIMELINE = (id: string) => {

export const TIMELINE_CHANGES_IN_PROGRESS = '[data-test-subj="timeline"] .euiProgress';

export const TIMELINE_CHECKBOX = (id: string) => {
return `[data-test-subj="checkboxSelectRow-${id}"]`;
};

export const TIMELINE_COLUMN_SPINNER = '[data-test-subj="timeline-loading-spinner"]';

export const TIMELINE_DATA_PROVIDERS = '[data-test-subj="dataProviders"]';
Expand Down Expand Up @@ -70,6 +78,8 @@ export const TIMELINE_SETTINGS_ICON = '[data-test-subj="settings-gear"]';

export const TIMELINE_TITLE = '[data-test-subj="timeline-title"]';

export const TIMELINES_TABLE = '[data-test-subj="timelines-table"]';

export const TIMESTAMP_HEADER_FIELD = '[data-test-subj="header-text-@timestamp"]';

export const TIMESTAMP_TOGGLE_FIELD = '[data-test-subj="toggle-field-@timestamp"]';
Expand Down
14 changes: 14 additions & 0 deletions x-pack/plugins/security_solution/cypress/tasks/timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
*/

import {
BULK_ACTIONS,
CLOSE_TIMELINE_BTN,
CREATE_NEW_TIMELINE,
EXPORT_TIMELINE_ACTION,
TIMELINE_CHECKBOX,
HEADER,
ID_FIELD,
ID_HEADER_FIELD,
Expand All @@ -20,6 +23,7 @@ import {
TIMELINE_INSPECT_BUTTON,
TIMELINE_SETTINGS_ICON,
TIMELINE_TITLE,
TIMELINES_TABLE,
TIMESTAMP_TOGGLE_FIELD,
TOGGLE_TIMELINE_EXPAND_EVENT,
REMOVE_COLUMN,
Expand Down Expand Up @@ -66,6 +70,12 @@ export const expandFirstTimelineEventDetails = () => {
cy.get(TOGGLE_TIMELINE_EXPAND_EVENT).first().click({ force: true });
};

export const exportTimeline = (timelineId: string) => {
cy.get(TIMELINE_CHECKBOX(timelineId)).click({ force: true });
cy.get(BULK_ACTIONS).click({ force: true });
cy.get(EXPORT_TIMELINE_ACTION).click();
};

export const openTimelineFieldsBrowser = () => {
cy.get(TIMELINE_FIELDS_BUTTON).click({ force: true });
};
Expand Down Expand Up @@ -122,6 +132,10 @@ export const resetFields = () => {
cy.get(RESET_FIELDS).click({ force: true });
};

export const waitForTimelinesPanelToBeLoaded = () => {
cy.get(TIMELINES_TABLE).should('exist');
};

export const waitForTimelineChanges = () => {
cy.get(TIMELINE_CHANGES_IN_PROGRESS).should('exist');
cy.get(TIMELINE_CHANGES_IN_PROGRESS).should('not.exist');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"savedObjectId":"0162c130-78be-11ea-9718-118a926974a4","version":"WzcsMV0=","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"}],"created":1586256805054,"createdBy":"elastic","dataProviders":[],"dateRange":{"end":1586256837669,"start":1546343624710},"description":"description","eventType":"all","filters":[],"kqlMode":"filter","kqlQuery":{"filterQuery":{"kuery":{"expression":"host.name:*","kind":"kuery"},"serializedQuery":"{\"bool\":{\"should\":[{\"exists\":{\"field\":\"host.name\"}}],\"minimum_should_match\":1}}"}},"savedQueryId":null,"sort":{"columnId":"@timestamp","sortDirection":"desc"},"title":"SIEM test","updated":1586256839298,"updatedBy":"elastic","timelineType":"default","eventNotes":[],"globalNotes":[],"pinnedEventIds":[]}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { EuiIcon, EuiLink, IconSize, IconType } from '@elastic/eui';
import { LinkAnchorProps } from '@elastic/eui/src/components/link/link';
import React from 'react';
import React, { ReactNode } from 'react';
import styled, { css } from 'styled-components';

interface LinkProps {
Expand Down Expand Up @@ -47,7 +47,7 @@ export const Link = styled(({ iconSide, children, ...rest }) => (
Link.displayName = 'Link';

export interface LinkIconProps extends LinkProps {
children: string;
children: string | ReactNode;
iconSize?: IconSize;
iconType: IconType;
dataTestSubj?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ export const OpenTimeline = React.memo<OpenTimelineProps>(
popoverContent={getBatchItemsPopoverContent}
data-test-subj="utility-bar-action"
>
{i18n.BATCH_ACTIONS}
<span data-test-subj="utility-bar-action-button">{i18n.BATCH_ACTIONS}</span>
</UtilityBarAction>
</>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default ({ getService }: FtrProviderContext): void => {
await deleteAllTimelines(es);
});

it('should contain two output keys of rules_installed and rules_updated', async () => {
it('should contain rules_installed, rules_updated, timelines_installed, and timelines_updated', async () => {
const { body } = await supertest
.put(DETECTION_ENGINE_PREPACKAGED_URL)
.set('kbn-xsrf', 'true')
Expand All @@ -74,6 +74,16 @@ export default ({ getService }: FtrProviderContext): void => {
expect(body.rules_installed).to.be.greaterThan(0);
});

it('should create the prepackaged timelines and return a count greater than zero', async () => {
const { body } = await supertest
.put(DETECTION_ENGINE_PREPACKAGED_URL)
.set('kbn-xsrf', 'true')
.send()
.expect(200);

expect(body.timelines_installed).to.be.greaterThan(0);
});

it('should create the prepackaged rules that the rules_updated is of size zero', async () => {
const { body } = await supertest
.put(DETECTION_ENGINE_PREPACKAGED_URL)
Expand All @@ -84,6 +94,16 @@ export default ({ getService }: FtrProviderContext): void => {
expect(body.rules_updated).to.eql(0);
});

it('should create the prepackaged timelines and the timelines_updated is of size zero', async () => {
const { body } = await supertest
.put(DETECTION_ENGINE_PREPACKAGED_URL)
.set('kbn-xsrf', 'true')
.send()
.expect(200);

expect(body.timelines_updated).to.eql(0);
});

it('should be possible to call the API twice and the second time the number of rules installed should be zero', async () => {
await supertest
.put(DETECTION_ENGINE_PREPACKAGED_URL)
Expand All @@ -109,6 +129,30 @@ export default ({ getService }: FtrProviderContext): void => {

expect(body.rules_installed).to.eql(0);
});

it('should be possible to call the API twice and the second time the number of timelines installed should be zero', async () => {
await supertest
.put(DETECTION_ENGINE_PREPACKAGED_URL)
.set('kbn-xsrf', 'true')
.send()
.expect(200);

await waitFor(async () => {
const { body } = await supertest
.get(`${DETECTION_ENGINE_PREPACKAGED_URL}/_status`)
.set('kbn-xsrf', 'true')
.expect(200);
return body.timelines_not_installed === 0;
});

const { body } = await supertest
.put(DETECTION_ENGINE_PREPACKAGED_URL)
.set('kbn-xsrf', 'true')
.send()
.expect(200);

expect(body.timelines_installed).to.eql(0);
});
});
});
};
Loading

0 comments on commit e7966e9

Please sign in to comment.