From 9746baba57030e1f906b80cff375bbb7038aa4ec Mon Sep 17 00:00:00 2001 From: Michael Olorunnisola Date: Wed, 17 Jan 2024 11:14:52 -0500 Subject: [PATCH] [Investigations] Unskip dataproviders test (#173183) ## Summary This PR unskips the tests for the timeline query builder dataProviders. In this unskip a few things were tackled: 1. dataProviders were unskipped and the utilized tasks updated to work with the new timeline ux 2. Two new tasks were added: `createTimelineOptionsPopoverBottomBar` and `openTimelineByIdFromOpenTimelineModal` 3. All `force:true` were removed from the timeline tasks file 4. The `open_timeline`, `sourcerer_timeline`, and `query_tab` tests were fixed against the new timeline ux as `force:true` was masking a bug in how the cypress tests was written. Resolves the following issues: https://github.com/elastic/kibana/issues/163622 https://github.com/elastic/kibana/issues/164069 https://github.com/elastic/kibana/issues/169396 https://github.com/elastic/kibana/issues/156797 [Flaky Tests - Dec. 20](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/4662) --------- Co-authored-by: Jan Monschke --- .../sourcerer/sourcerer_timeline.cy.ts | 8 +- .../timelines/data_providers.cy.ts | 39 ++++------ .../discover_timeline_state_integration.cy.ts | 30 ++++---- .../investigations/timelines/query_tab.cy.ts | 77 ++++++++----------- .../cypress/screens/timeline.ts | 2 + .../cypress/tasks/timeline.ts | 36 +++++---- 6 files changed, 88 insertions(+), 104 deletions(-) diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/sourcerer/sourcerer_timeline.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/sourcerer/sourcerer_timeline.cy.ts index d8e1ed1ea3321..e6f26a744ad9e 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/sourcerer/sourcerer_timeline.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/sourcerer/sourcerer_timeline.cy.ts @@ -10,6 +10,7 @@ import { DEFAULT_INDEX_PATTERN, } from '@kbn/security-solution-plugin/common/constants'; +import { deleteTimelines } from '../../../../tasks/api_calls/common'; import { login } from '../../../../tasks/login'; import { visitWithTimeRange } from '../../../../tasks/navigation'; @@ -92,18 +93,15 @@ describe('Timeline scope', { tags: ['@ess', '@serverless', '@brokenInServerless' }); }); describe('Alerts checkbox', () => { - before(() => { + beforeEach(() => { login(); + deleteTimelines(); createTimeline(getTimeline()).then((response) => cy.wrap(response.body.data.persistTimeline.timeline.savedObjectId).as('timelineId') ); createTimeline(getTimelineModifiedSourcerer()).then((response) => cy.wrap(response.body.data.persistTimeline.timeline.savedObjectId).as('auditbeatTimelineId') ); - }); - - beforeEach(() => { - login(); visitWithTimeRange(TIMELINES_URL); refreshUntilAlertsIndexExists(); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/data_providers.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/data_providers.cy.ts index c226eb680e8d8..735c195988667 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/data_providers.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/data_providers.cy.ts @@ -22,32 +22,27 @@ import { updateDataProviderbyDraggingField, addNameAndDescriptionToTimeline, populateTimeline, - createNewTimeline, + createTimelineOptionsPopoverBottomBar, updateDataProviderByFieldHoverAction, saveTimeline, } from '../../../tasks/timeline'; import { getTimeline } from '../../../objects/timeline'; import { hostsUrl } from '../../../urls/navigation'; -import { scrollToBottom } from '../../../tasks/common'; -// Failing in serverless -// FLAKY: https://github.com/elastic/kibana/issues/169396 -describe.skip('timeline data providers', { tags: ['@ess', '@serverless'] }, () => { +describe('Timeline data providers', { tags: ['@ess', '@serverless'] }, () => { beforeEach(() => { login(); visitWithTimeRange(hostsUrl('allHosts')); waitForAllHostsToBeLoaded(); - scrollToBottom(); - createNewTimeline(); + createTimelineOptionsPopoverBottomBar(); addNameAndDescriptionToTimeline(getTimeline()); populateTimeline(); }); - it('displays the data provider action menu when Enter is pressed', () => { + it('should display the data provider action menu when Enter is pressed', () => { addDataProvider({ field: 'host.name', operator: 'exists' }); cy.get(TIMELINE_DATA_PROVIDERS_ACTION_MENU).should('not.exist'); - cy.get(`${TIMELINE_FLYOUT_HEADER} ${TIMELINE_DROPPED_DATA_PROVIDERS}`).focus(); cy.get(`${TIMELINE_FLYOUT_HEADER} ${TIMELINE_DROPPED_DATA_PROVIDERS}`) .first() .parent() @@ -56,22 +51,18 @@ describe.skip('timeline data providers', { tags: ['@ess', '@serverless'] }, () = cy.get(TIMELINE_DATA_PROVIDERS_ACTION_MENU).should('exist'); }); - it( - 'persists timeline when data provider is updated by dragging a field from data grid', - { tags: ['@brokenInServerless'] }, - () => { - updateDataProviderbyDraggingField('host.name', 0); - saveTimeline(); - cy.reload(); - cy.get(`${GET_TIMELINE_GRID_CELL('host.name')}`) - .first() - .then((hostname) => { - cy.get(TIMELINE_DATA_PROVIDERS_CONTAINER).contains(`host.name: "${hostname.text()}"`); - }); - } - ); + it('should persist timeline when data provider is updated by dragging a field from data grid', () => { + updateDataProviderbyDraggingField('host.name', 0); + saveTimeline(); + cy.reload(); + cy.get(`${GET_TIMELINE_GRID_CELL('host.name')}`) + .first() + .then((hostname) => { + cy.get(TIMELINE_DATA_PROVIDERS_CONTAINER).contains(`host.name: "${hostname.text()}"`); + }); + }); - it('persists timeline when a field is added by hover action "Add To Timeline" in data provider ', () => { + it('should persist timeline when a field is added by hover action "Add To Timeline" in data provider ', () => { addDataProvider({ field: 'host.name', operator: 'exists' }); saveTimeline(); updateDataProviderByFieldHoverAction('host.name', 0); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/esql/discover_timeline_state_integration.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/esql/discover_timeline_state_integration.cy.ts index 903ca0c087ed3..95151a2b66d71 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/esql/discover_timeline_state_integration.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/esql/discover_timeline_state_integration.cy.ts @@ -114,16 +114,17 @@ describe( createNewTimeline(); // switch to old timeline openTimelineFromSettings(); - openTimelineById(timelineId); - cy.get(LOADING_INDICATOR).should('not.exist'); - goToEsqlTab(); - verifyDiscoverEsqlQuery(esqlQuery); - cy.get(GET_DISCOVER_DATA_GRID_CELL_HEADER(column1)).should('exist'); - cy.get(GET_DISCOVER_DATA_GRID_CELL_HEADER(column2)).should('exist'); - cy.get(GET_LOCAL_DATE_PICKER_START_DATE_POPOVER_BUTTON(DISCOVER_CONTAINER)).should( - 'have.text', - INITIAL_START_DATE - ); + openTimelineById(timelineId).then(() => { + cy.get(LOADING_INDICATOR).should('not.exist'); + goToEsqlTab(); + verifyDiscoverEsqlQuery(esqlQuery); + cy.get(GET_DISCOVER_DATA_GRID_CELL_HEADER(column1)).should('exist'); + cy.get(GET_DISCOVER_DATA_GRID_CELL_HEADER(column2)).should('exist'); + cy.get(GET_LOCAL_DATE_PICKER_START_DATE_POPOVER_BUTTON(DISCOVER_CONTAINER)).should( + 'have.text', + INITIAL_START_DATE + ); + }); }); }); it('should save/restore esql tab dataview/timerange/filter/query/columns when timeline is opened via url', () => { @@ -164,10 +165,11 @@ describe( createNewTimeline(); // switch to old timeline openTimelineFromSettings(); - openTimelineById(timelineId); - cy.get(LOADING_INDICATOR).should('not.exist'); - goToEsqlTab(); - cy.get(DISCOVER_DATA_VIEW_SWITCHER.BTN).should('not.exist'); + openTimelineById(timelineId).then(() => { + cy.get(LOADING_INDICATOR).should('not.exist'); + goToEsqlTab(); + cy.get(DISCOVER_DATA_VIEW_SWITCHER.BTN).should('not.exist'); + }); }); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/query_tab.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/query_tab.cy.ts index b5b610a387ffa..acd3f4eb2176e 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/query_tab.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/query_tab.cy.ts @@ -14,74 +14,57 @@ import { TIMELINE_QUERY, NOTE_CARD_CONTENT, } from '../../../screens/timeline'; +import { deleteTimelines } from '../../../tasks/api_calls/common'; +import { addNoteToTimeline } from '../../../tasks/api_calls/notes'; import { createTimeline } from '../../../tasks/api_calls/timelines'; import { login } from '../../../tasks/login'; import { visit } from '../../../tasks/navigation'; import { addFilter, + addNoteToFirstRowEvent, openTimelineById, pinFirstEvent, - refreshTimelinesUntilTimeLinePresent, } from '../../../tasks/timeline'; import { TIMELINES_URL } from '../../../urls/navigation'; -describe.skip('Timeline query tab', { tags: ['@ess', '@serverless'] }, () => { - before(() => { +const defaultTimeline = getTimeline(); + +describe('Timeline query tab', { tags: ['@ess', '@serverless'] }, () => { + beforeEach(() => { login(); visit(TIMELINES_URL); - createTimeline(getTimeline()) + deleteTimelines(); + createTimeline(defaultTimeline) .then((response) => response.body.data.persistTimeline.timeline.savedObjectId) .then((timelineId: string) => { - refreshTimelinesUntilTimeLinePresent(timelineId) - // This cy.wait is here because we cannot do a pipe on a timeline as that will introduce multiple URL - // request responses and indeterminism since on clicks to activates URL's. - .then(() => cy.wrap(timelineId).as('timelineId')) - // eslint-disable-next-line cypress/no-unnecessary-waiting - .then(() => cy.wait(1000)) - // TO-DO: Issue 163398 - // .then(() => - // addNoteToTimeline(getTimeline().notes, timelineId).should((response) => - // expect(response.status).to.equal(200) - // ) - // ) - .then(() => openTimelineById(timelineId)) - .then(() => pinFirstEvent()) - // TO-DO: Issue 163398 - // .then(() => persistNoteToFirstEvent('event note')) - .then(() => addFilter(getTimeline().filter)); + cy.wrap(timelineId).as('timelineId'); + addNoteToTimeline(defaultTimeline.notes, timelineId); + openTimelineById(timelineId); + pinFirstEvent(); + addFilter(defaultTimeline.filter); }); }); - describe('Query tab', () => { - beforeEach(function () { - login(); - visit(TIMELINES_URL); - openTimelineById(this.timelineId).then(() => addFilter(getTimeline().filter)); - }); - - it('should contain the right query', () => { - cy.get(TIMELINE_QUERY).should('have.text', `${getTimeline().query}`); - }); - - // TO-DO: Issue 163398 - it.skip('should be able to add event note', () => { - cy.get(NOTE_CARD_CONTENT).should('contain', 'event note'); - }); + it('should display the right query and filters', () => { + cy.get(TIMELINE_QUERY).should('have.text', `${defaultTimeline.query}`); + cy.get(TIMELINE_FILTER(defaultTimeline.filter)).should('exist'); + }); - it('should display timeline filter', () => { - cy.get(TIMELINE_FILTER(getTimeline().filter)).should('exist'); - }); + it('should be able to add event note', () => { + const note = 'event note'; + addNoteToFirstRowEvent(note); + cy.get(NOTE_CARD_CONTENT).should('contain', 'event note'); + }); - it('should display pinned events', () => { - cy.get(PIN_EVENT) - .should('have.attr', 'aria-label') - .and('match', /Unpin the event in row 2/); - }); + it('should display pinned events', () => { + cy.get(PIN_EVENT) + .should('have.attr', 'aria-label') + .and('match', /Unpin the event in row 2/); + }); - it('should have an unlock icon', { tags: '@brokenInServerless' }, () => { - cy.get(UNLOCKED_ICON).should('be.visible'); - }); + it('should have an unlock icon', { tags: '@brokenInServerless' }, () => { + cy.get(UNLOCKED_ICON).should('be.visible'); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts b/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts index 69d3d4020b37f..6a87547a0c938 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts @@ -54,6 +54,8 @@ export const LOCKED_ICON = '[data-test-subj="timeline-date-picker-lock-button"]' export const UNLOCKED_ICON = '[data-test-subj="timeline-date-picker-unlock-button"]'; +export const ROW_ADD_NOTES_BUTTON = '[data-test-subj="timeline-notes-button-small"]'; + export const NOTE_CARD_CONTENT = '[data-test-subj="notes"]'; export const NOTE_DESCRIPTION = '[data-test-subj="note-preview-description"]'; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts b/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts index 40ee5c4f29961..4bf4bbfa038c7 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts @@ -10,7 +10,6 @@ import type { Timeline, TimelineFilter } from '../objects/timeline'; import { ALL_CASES_CREATE_NEW_CASE_TABLE_BTN } from '../screens/all_cases'; import { FIELDS_BROWSER_CHECKBOX } from '../screens/fields_browser'; -import { LOADING_INDICATOR } from '../screens/security_header'; import { EQL_QUERY_VALIDATION_SPINNER } from '../screens/create_new_rule'; import { @@ -65,7 +64,6 @@ import { TIMELINE_COLLAPSED_ITEMS_BTN, TIMELINE_TAB_CONTENT_EQL, TIMESTAMP_HOVER_ACTION_OVERFLOW_BTN, - TIMELINE_DATA_PROVIDER_FIELD_INPUT, ACTIVE_TIMELINE_BOTTOM_BAR, EMPTY_DATA_PROVIDER_AREA, EMPTY_DROPPABLE_DATA_PROVIDER_GROUP, @@ -84,6 +82,8 @@ import { TIMELINE_SEARCH_OR_FILTER, TIMELINE_KQLMODE_FILTER, TIMELINE_KQLMODE_SEARCH, + TIMELINE_DATA_PROVIDERS_CONTAINER, + ROW_ADD_NOTES_BUTTON, TIMELINE_PANEL, } from '../screens/timeline'; @@ -173,7 +173,6 @@ export const addNotesToTimeline = (notes: string) => { .then((notesCount) => { cy.get(NOTES_TEXT_AREA).type(notes, { parseSpecialCharSequences: false, - force: true, }); cy.get(ADD_NOTE_BUTTON).click(); @@ -183,6 +182,15 @@ export const addNotesToTimeline = (notes: string) => { }); }; +export const addNoteToFirstRowEvent = (notes: string) => { + cy.get(ROW_ADD_NOTES_BUTTON).first().click(); + cy.get(NOTES_TEXT_AREA).type(notes, { + parseSpecialCharSequences: false, + }); + + cy.get(ADD_NOTE_BUTTON).click(); +}; + export const addEqlToTimeline = (eql: string) => { goToCorrelationTab().then(() => { cy.get(TIMELINE_CORRELATION_INPUT).type(eql); @@ -217,19 +225,21 @@ export const changeTimelineQueryLanguage = (language: 'kuery' | 'lucene') => { }; export const addDataProvider = (filter: TimelineFilter): Cypress.Chainable> => { + cy.get(TOGGLE_DATA_PROVIDER_BTN).click(); + cy.get(TIMELINE_DATA_PROVIDERS_CONTAINER).should('be.visible'); // Cypress doesn't properly wait for the data provider to finish expanding, so we wait for the animation to finish. cy.get(TIMELINE_ADD_FIELD_BUTTON).click(); - cy.get(LOADING_INDICATOR).should('not.exist'); - cy.get('[data-popover-open]').should('exist'); - cy.get(TIMELINE_DATA_PROVIDER_FIELD).click(); - cy.get(TIMELINE_DATA_PROVIDER_FIELD) - .find(TIMELINE_DATA_PROVIDER_FIELD_INPUT) - .should('have.focus'); // make sure the focus is ready before start typing cy.get(TIMELINE_DATA_PROVIDER_FIELD) .find(COMBO_BOX_INPUT) .type(`${filter.field}{downarrow}{enter}`); + + cy.get(TIMELINE_DATA_PROVIDER_OPERATOR) + .find(`${COMBO_BOX_INPUT} input`) + .type(`{selectall}{backspace}{selectall}{backspace}`); + cy.get(TIMELINE_DATA_PROVIDER_OPERATOR) .find(COMBO_BOX_INPUT) .type(`${filter.operator}{downarrow}{enter}`); + if (filter.operator !== 'exists') { cy.get(TIMELINE_DATA_PROVIDER_VALUE).type(`${filter.value}{enter}`); } @@ -260,7 +270,7 @@ export const updateDataProviderbyDraggingField = (fieldName: string, rowNumber: export const updateDataProviderByFieldHoverAction = (fieldName: string, rowNumber: number) => { const fieldSelector = GET_TIMELINE_GRID_CELL(fieldName); - cy.get(fieldSelector).eq(rowNumber).trigger('mouseover', { force: true }); + cy.get(fieldSelector).eq(rowNumber).trigger('mouseover'); cy.get(HOVER_ACTIONS.ADD_TO_TIMELINE).should('be.visible'); recurse( () => { @@ -295,9 +305,7 @@ export const clickIdToggleField = () => { clickIdHoverActionOverflowButton(); cy.get(ID_HEADER_FIELD).should('not.exist'); - cy.get(ID_TOGGLE_FIELD).click({ - force: true, - }); + cy.get(ID_TOGGLE_FIELD).click(); }; export const closeTimeline = () => { @@ -307,7 +315,7 @@ export const closeTimeline = () => { export const createNewTimeline = () => { cy.get(NEW_TIMELINE_ACTION).click(); - cy.get(CREATE_NEW_TIMELINE).first().click(); + cy.get(CREATE_NEW_TIMELINE).eq(0).click(); }; export const openCreateTimelineOptionsPopover = () => {