diff --git a/x-pack/legacy/plugins/siem/cypress/README.md b/x-pack/legacy/plugins/siem/cypress/README.md index c9e0d4e18f78..a57fe0d361b8 100644 --- a/x-pack/legacy/plugins/siem/cypress/README.md +++ b/x-pack/legacy/plugins/siem/cypress/README.md @@ -22,29 +22,29 @@ automatically when you submit a PR. Smoke Tests are located in `siem/cypress/integration/smoke_tests` -## Test Helpers +## Structure -_Test helpers_ are functions that may be re-used across tests. +### Tasks -- Reusable code and CSS selectors should be added to -`siem/cypress/integration/lib`, as described below. +_Tasks_ are functions that my be re-used across tests. Inside the _tasks_ folder there are some other folders that represents +the page to which we will perform the actions. For each folder we are going to create a file for each one of the sections that + has the page. -### Reusable Test Helper Functions and CSS Selectors +i.e. +- tasks + - hosts + - events.ts -The `cypress/integration/lib` directory contains code intended to be re-used -across many different tests. Add reusable test helper functions and CSS -selectors to directories under `cypress/integration/lib`. +### Screens -- Files named `helpers.ts` (e.g. `siem/cypress/integration/lib/login/helpers.ts`) -contain functions (e.g. `login`) that may be imported and invoked from multiple tests. +In _screens_ folder we are going to find all the elements we want to interact in our tests. Inside _screens_ fonder there +are some other folders that represents the page that contains the elements the tests are going to interact with. For each +folder we are going to create a file for each one of the sections that the page has. -- Files named `selectors.ts` export CSS selectors for re-use. For example, -`siem/cypress/integration/lib/login/selectors.ts` exports the following selector -that matches the Username text area in the Kibana login page: - -```sh -export const USERNAME = '[data-test-subj="loginUsername"]'; -``` +i.e. +- tasks + - hosts + - events.ts ## Mock Data diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/events_viewer/events_viewer.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/events_viewer/events_viewer.spec.ts index 1450ee8dc8ab..bf141a9f0a0b 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/events_viewer/events_viewer.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/events_viewer/events_viewer.spec.ts @@ -9,25 +9,31 @@ import { FIELDS_BROWSER_CONTAINER, FIELDS_BROWSER_SELECTED_CATEGORY_TITLE, FIELDS_BROWSER_TITLE, -} from '../../lib/fields_browser/selectors'; + FIELDS_BROWSER_CHECKBOX, +} from '../../../screens/hosts/fields_browser'; import { HOSTS_PAGE } from '../../lib/urls'; -import { loginAndWaitForPage, DEFAULT_TIMEOUT } from '../../lib/util/helpers'; +import { loginAndWaitForPage } from '../../../tasks/login'; +import { openEventsViewerFieldsBrowser, filterSearchBar } from '../../lib/events_viewer/helpers'; +import { closeFieldsBrowser } from '../../../tasks/hosts/fields_browsers'; +import { openEvents } from '../../../tasks/hosts/main'; import { - clickOutsideFieldsBrowser, - openEventsViewerFieldsBrowser, - filterSearchBar, -} from '../../lib/events_viewer/helpers'; + closeModal, + opensInspectQueryModal, + waitsForEventsToBeLoaded, + addsHostGeoCityNameToHeader, + addsHostGeoCountryNameToHeader, + resetFields, +} from '../../../tasks/hosts/events'; + import { - CLOSE_MODAL, - EVENTS_VIEWER_PANEL, HEADER_SUBTITLE, INSPECT_MODAL, - INSPECT_QUERY, LOAD_MORE, LOCAL_EVENTS_COUNT, -} from '../../lib/events_viewer/selectors'; -import { SERVER_SIDE_EVENT_COUNT } from '../../lib/timeline/selectors'; -import { clickEventsTab } from '../../lib/hosts/helpers'; + HOST_GEO_CITY_NAME_HEADER, + HOST_GEO_COUNTRY_NAME_HEADER, +} from '../../../screens/hosts/events'; +import { DEFAULT_TIMEOUT } from '../../lib/util/helpers'; const defaultHeadersInDefaultEcsCategory = [ { id: '@timestamp' }, @@ -43,7 +49,7 @@ describe('Events Viewer', () => { context('Fields rendering', () => { before(() => { loginAndWaitForPage(HOSTS_PAGE); - clickEventsTab(); + openEvents(); }); beforeEach(() => { @@ -51,7 +57,7 @@ describe('Events Viewer', () => { }); afterEach(() => { - clickOutsideFieldsBrowser(); + closeFieldsBrowser(); cy.get(FIELDS_BROWSER_CONTAINER).should('not.exist'); }); @@ -69,7 +75,7 @@ describe('Events Viewer', () => { it('displays a checked checkbox for all of the default events viewer columns that are also in the default ECS category', () => { defaultHeadersInDefaultEcsCategory.forEach(header => - cy.get(`[data-test-subj="field-${header.id}-checkbox"]`).should('be.checked') + cy.get(FIELDS_BROWSER_CHECKBOX(header.id)).should('be.checked') ); }); }); @@ -77,26 +83,17 @@ describe('Events Viewer', () => { context('Events viewer query modal', () => { before(() => { loginAndWaitForPage(HOSTS_PAGE); - clickEventsTab(); + openEvents(); }); after(() => { - cy.get(CLOSE_MODAL).click(); + closeModal(); cy.get(INSPECT_MODAL, { timeout: DEFAULT_TIMEOUT }).should('not.exist'); }); it('launches the inspect query modal when the inspect button is clicked', () => { - // wait for data to load - cy.get(SERVER_SIDE_EVENT_COUNT, { timeout: DEFAULT_TIMEOUT }) - .should('exist') - .invoke('text', { timeout: DEFAULT_TIMEOUT }) - .should('not.equal', '0'); - - cy.get(INSPECT_QUERY, { timeout: DEFAULT_TIMEOUT }) - .should('exist') - .trigger('mousemove', { force: true }) - .click({ force: true }); - + waitsForEventsToBeLoaded(); + opensInspectQueryModal(); cy.get(INSPECT_MODAL, { timeout: DEFAULT_TIMEOUT }).should('exist'); }); }); @@ -104,7 +101,7 @@ describe('Events Viewer', () => { context('Events viewer fields behaviour', () => { before(() => { loginAndWaitForPage(HOSTS_PAGE); - clickEventsTab(); + openEvents(); }); beforeEach(() => { @@ -113,89 +110,55 @@ describe('Events Viewer', () => { it('adds a field to the events viewer when the user clicks the checkbox', () => { const filterInput = 'host.geo.c'; - const toggleField = 'host.geo.city_name'; filterFieldsBrowser(filterInput); - - cy.get(`${EVENTS_VIEWER_PANEL} [data-test-subj="header-text-${toggleField}"]`).should( - 'not.exist' - ); - - cy.get(`${EVENTS_VIEWER_PANEL} [data-test-subj="field-${toggleField}-checkbox"]`).check({ - force: true, - }); - - clickOutsideFieldsBrowser(); - - cy.get(`${EVENTS_VIEWER_PANEL} [data-test-subj="header-text-${toggleField}"]`).should( - 'exist' - ); + cy.get(HOST_GEO_CITY_NAME_HEADER).should('not.exist'); + addsHostGeoCityNameToHeader(); + closeFieldsBrowser(); + cy.get(HOST_GEO_CITY_NAME_HEADER).should('exist'); }); it('resets all fields in the events viewer when `Reset Fields` is clicked', () => { const filterInput = 'host.geo.c'; - const toggleField = 'host.geo.country_name'; filterFieldsBrowser(filterInput); - - cy.get(`${EVENTS_VIEWER_PANEL} [data-test-subj="header-text-${toggleField}"]`).should( - 'not.exist' - ); - - cy.get(`${EVENTS_VIEWER_PANEL} [data-test-subj="field-${toggleField}-checkbox"]`).check({ - force: true, - }); - - cy.get(`${EVENTS_VIEWER_PANEL} [data-test-subj="reset-fields"]`).click({ force: true }); - - cy.get(`${EVENTS_VIEWER_PANEL} [data-test-subj="header-text-${toggleField}"]`).should( - 'not.exist' - ); + cy.get(HOST_GEO_COUNTRY_NAME_HEADER).should('not.exist'); + addsHostGeoCountryNameToHeader(); + resetFields(); + cy.get(HOST_GEO_COUNTRY_NAME_HEADER).should('not.exist'); }); }); context('Events behaviour', () => { before(() => { loginAndWaitForPage(HOSTS_PAGE); - clickEventsTab(); + openEvents(); }); it('filters the events by applying filter criteria from the search bar at the top of the page', () => { const filterInput = '4bf34c1c-eaa9-46de-8921-67a4ccc49829'; // this will never match real data - + waitsForEventsToBeLoaded(); cy.get(HEADER_SUBTITLE) .invoke('text') - .then(text1 => { - cy.get(HEADER_SUBTITLE) - .invoke('text', { timeout: DEFAULT_TIMEOUT }) - .should('not.equal', 'Showing: 0 events'); - + .then(initialNumberOfEvents => { filterSearchBar(filterInput); - cy.get(HEADER_SUBTITLE) .invoke('text') - .should(text2 => { - expect(text1).not.to.eq(text2); - }); + .should('not.equal', initialNumberOfEvents); }); }); it('loads more events when the load more button is clicked', () => { - cy.get(LOCAL_EVENTS_COUNT, { timeout: DEFAULT_TIMEOUT }) + const defaultNumberOfLoadedEvents = '25'; + cy.get(LOCAL_EVENTS_COUNT) .invoke('text') - .then(text1 => { - cy.get(LOCAL_EVENTS_COUNT) - .invoke('text') - .should('equal', '25'); + .should('equal', defaultNumberOfLoadedEvents); - cy.get(LOAD_MORE).click({ force: true }); + cy.get(LOAD_MORE).click({ force: true }); - cy.get(LOCAL_EVENTS_COUNT) - .invoke('text') - .should(text2 => { - expect(text1).not.to.eq(text2); - }); - }); + cy.get(LOCAL_EVENTS_COUNT) + .invoke('text') + .should('not.equal', defaultNumberOfLoadedEvents); }); }); }); diff --git a/x-pack/legacy/plugins/siem/cypress/screens/hosts/events.ts b/x-pack/legacy/plugins/siem/cypress/screens/hosts/events.ts new file mode 100644 index 000000000000..034c1453fc97 --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/screens/hosts/events.ts @@ -0,0 +1,36 @@ +/* + * 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. + */ + +export const EVENTS_VIEWER_PANEL = '[data-test-subj="events-viewer-panel"]'; + +export const CLOSE_MODAL = '[data-test-subj="modal-inspect-close"]'; + +export const HEADER_SUBTITLE = `${EVENTS_VIEWER_PANEL} [data-test-subj="header-panel-subtitle"]`; + +export const INSPECT_MODAL = '[data-test-subj="modal-inspect-euiModal"]'; + +export const INSPECT_QUERY = `${EVENTS_VIEWER_PANEL} [data-test-subj="inspect-icon-button"]`; + +export const LOAD_MORE = `${EVENTS_VIEWER_PANEL} [data-test-subj="TimelineMoreButton"]`; + +export const LOCAL_EVENTS_COUNT = `${EVENTS_VIEWER_PANEL} [data-test-subj="local-events-count"]`; + +export const SERVER_SIDE_EVENT_COUNT = '[data-test-subj="server-side-event-count"]'; + +export const HOST_GEO_CITY_NAME_HEADER = '[data-test-subj="header-text-host.geo.city_name"]'; + +export const HOST_GEO_CITY_NAME_CHECKBOX = '[data-test-subj="field-host.geo.city_name-checkbox"]'; + +export const HOST_GEO_COUNTRY_NAME_HEADER = '[data-test-subj="header-text-host.geo.country_name"]'; + +export const HOST_GEO_COUNTRY_NAME_CHECKBOX = + '[data-test-subj="field-host.geo.country_name-checkbox"]'; + +export const FIELDS_BROWSER_CONTAINER = '[data-test-subj="fields-browser-container"]'; + +export const EVENTS_VIEWER_FIELDS_BUTTON = `${EVENTS_VIEWER_PANEL} [data-test-subj="show-field-browser-gear"]`; + +export const RESET_FIELDS = `${EVENTS_VIEWER_PANEL} [data-test-subj="reset-fields"]`; diff --git a/x-pack/legacy/plugins/siem/cypress/screens/hosts/fields_browser.ts b/x-pack/legacy/plugins/siem/cypress/screens/hosts/fields_browser.ts new file mode 100644 index 000000000000..f4da73ba5e5f --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/screens/hosts/fields_browser.ts @@ -0,0 +1,22 @@ +/* + * 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. + */ + +/** Clicking this button in the timeline opens the Fields browser */ +export const TIMELINE_FIELDS_BUTTON = + '[data-test-subj="timeline"] [data-test-subj="show-field-browser"]'; + +/** The title displayed in the fields browser (i.e. Customize Columns) */ +export const FIELDS_BROWSER_TITLE = '[data-test-subj="field-browser-title"]'; + +/** Contains the body of the fields browser */ +export const FIELDS_BROWSER_CONTAINER = '[data-test-subj="fields-browser-container"]'; + +/** The title of the selected category in the right-hand side of the fields browser */ +export const FIELDS_BROWSER_SELECTED_CATEGORY_TITLE = '[data-test-subj="selected-category-title"]'; + +export const FIELDS_BROWSER_CHECKBOX = (id: string) => { + return `[data-test-subj="field-${id}-checkbox`; +}; diff --git a/x-pack/legacy/plugins/siem/cypress/screens/hosts/main.ts b/x-pack/legacy/plugins/siem/cypress/screens/hosts/main.ts new file mode 100644 index 000000000000..e80ecdac272c --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/screens/hosts/main.ts @@ -0,0 +1,9 @@ +/* + * 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. + */ + +export const EVENTS_TAB = '[data-test-subj="navigation-events"]'; + +export const KQL_SEARCH_BAR = '[data-test-subj="queryInput"]'; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/events.ts b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/events.ts new file mode 100644 index 000000000000..d84d62f82d2e --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/events.ts @@ -0,0 +1,66 @@ +/* + * 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 { DEFAULT_TIMEOUT } from '../../integration/lib/util/helpers'; +import { + EVENTS_VIEWER_FIELDS_BUTTON, + CLOSE_MODAL, + INSPECT_QUERY, + SERVER_SIDE_EVENT_COUNT, + HOST_GEO_CITY_NAME_CHECKBOX, + HOST_GEO_COUNTRY_NAME_CHECKBOX, + FIELDS_BROWSER_CONTAINER, + RESET_FIELDS, + LOAD_MORE, +} from '../../screens/hosts/events'; + +export const closeModal = () => { + cy.get(CLOSE_MODAL, { timeout: DEFAULT_TIMEOUT }).click(); +}; + +export const opensInspectQueryModal = () => { + cy.get(INSPECT_QUERY, { timeout: DEFAULT_TIMEOUT }) + .should('exist') + .trigger('mousemove', { force: true }) + .click({ force: true }); +}; + +export const waitsForEventsToBeLoaded = () => { + cy.get(SERVER_SIDE_EVENT_COUNT, { timeout: DEFAULT_TIMEOUT }) + .should('exist') + .invoke('text', { timeout: DEFAULT_TIMEOUT }) + .should('not.equal', '0'); +}; + +export const addsHostGeoCityNameToHeader = () => { + cy.get(HOST_GEO_CITY_NAME_CHECKBOX).check({ + force: true, + }); +}; + +export const addsHostGeoCountryNameToHeader = () => { + cy.get(HOST_GEO_COUNTRY_NAME_CHECKBOX).check({ + force: true, + }); +}; + +export const resetFields = () => { + cy.get(RESET_FIELDS).click({ force: true }); +}; + +export const openEventsViewerFieldsBrowser = () => { + cy.get(EVENTS_VIEWER_FIELDS_BUTTON, { timeout: DEFAULT_TIMEOUT }).click({ force: true }); + + cy.get(SERVER_SIDE_EVENT_COUNT, { timeout: DEFAULT_TIMEOUT }) + .invoke('text') + .should('not.equal', '0'); + + cy.get(FIELDS_BROWSER_CONTAINER).should('exist'); +}; + +export const loadMoreEvents = () => { + cy.get(LOAD_MORE).click({ force: true }); +}; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/fields_browsers.ts b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/fields_browsers.ts new file mode 100644 index 000000000000..ae3ed2010a0a --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/fields_browsers.ts @@ -0,0 +1,12 @@ +/* + * 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 { DEFAULT_TIMEOUT } from '../../integration/lib/util/helpers'; +import { KQL_SEARCH_BAR } from '../../screens/hosts/main'; + +export const closeFieldsBrowser = () => { + cy.get(KQL_SEARCH_BAR, { timeout: DEFAULT_TIMEOUT }).click(); +}; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/main.ts b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/main.ts new file mode 100644 index 000000000000..d95ae837a3de --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/main.ts @@ -0,0 +1,13 @@ +/* + * 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 { DEFAULT_TIMEOUT } from '../../integration/lib/util/helpers'; + +import { EVENTS_TAB } from '../../screens/hosts/main'; + +/** Clicks the Events tab on the hosts page */ +export const openEvents = () => + cy.get(EVENTS_TAB, { timeout: DEFAULT_TIMEOUT }).click({ force: true }); diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/login.ts b/x-pack/legacy/plugins/siem/cypress/tasks/login.ts new file mode 100644 index 000000000000..d5bf1178d5bd --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/tasks/login.ts @@ -0,0 +1,24 @@ +/* + * 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 { login } from '../integration/lib/login/helpers'; + +/** The default time in ms to wait for a Cypress command to complete */ +export const DEFAULT_TIMEOUT = 30 * 1000; + +/** + * Authenticates with Kibana, visits the specified `url`, and waits for the + * Kibana logo to be displayed before continuing + */ +export const loginAndWaitForPage = (url: string) => { + login(); + + cy.visit(`${Cypress.config().baseUrl}${url}`); + + cy.viewport('macbook-15'); + + cy.contains('a', 'SIEM', { timeout: DEFAULT_TIMEOUT }); +};