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 Solution] Add tests for import / export timelines #75537

Merged
merged 17 commits into from
Sep 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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