From ed5f9110c5ddb10ec3c454ae4137fb1c1e07d3f1 Mon Sep 17 00:00:00 2001 From: Michael Olorunnisola Date: Tue, 16 Jan 2024 07:18:26 -0500 Subject: [PATCH 1/2] [Investigations] - Unskip and refactor discover state tests (#173308) Dependent on: https://github.com/elastic/kibana/pull/173015 - fixes https://github.com/elastic/kibana/issues/165663 - fixes https://github.com/elastic/kibana/issues/165747 [Flaky Tests (100/100)](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/4870) --------- Co-authored-by: Jan Monschke (cherry picked from commit 1172c0ec09eb72f478b7c0cb7afc6016fabdc400) # Conflicts: # x-pack/test/security_solution_cypress/cypress/tasks/common.ts --- .../use_discover_in_timeline_actions.tsx | 6 +- .../discover_timeline_state_integration.cy.ts | 142 +++++++----------- .../cypress/tasks/common.ts | 11 ++ .../cypress/tasks/discover.ts | 2 +- .../cypress/tasks/stack_management.ts | 13 +- .../cypress/tasks/timeline.ts | 30 ++-- .../cypress/tasks/timelines.ts | 8 +- 7 files changed, 104 insertions(+), 108 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.tsx b/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.tsx index 0196e6a06c834..98470dcacaea0 100644 --- a/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.tsx +++ b/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.tsx @@ -205,6 +205,10 @@ export const useDiscoverInTimelineActions = ( } else { // If no saved search exists. Create a new saved search instance and associate it with the timeline. try { + // Make sure we're not creating a saved search while a previous creation call is in progress + if (status !== 'idle') { + return; + } dispatch( timelineActions.startTimelineSaving({ id: TimelineId.active, @@ -218,7 +222,7 @@ export const useDiscoverInTimelineActions = ( const responseIsEmpty = !response || !response?.id; if (responseIsEmpty) { throw new Error('Response is empty'); - } else if (!savedSearchId && !responseIsEmpty && status !== 'loading') { + } else if (!savedSearchId && !responseIsEmpty) { dispatch( timelineActions.updateSavedSearchId({ id: TimelineId.active, 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 351cb601c481e..903ca0c087ed3 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 @@ -6,9 +6,11 @@ */ import { visitWithTimeRange } from '../../../../tasks/navigation'; -import { TIMELINE_TITLE } from '../../../../screens/timeline'; import { BASIC_TABLE_LOADING } from '../../../../screens/common'; -import { goToSavedObjectSettings } from '../../../../tasks/stack_management'; +import { + clickSavedObjectTagsFilter, + goToSavedObjectSettings, +} from '../../../../tasks/stack_management'; import { navigateFromKibanaCollapsibleTo, openKibanaNavigation, @@ -31,9 +33,9 @@ import { import { updateDateRangeInLocalDatePickers } from '../../../../tasks/date_picker'; import { login } from '../../../../tasks/login'; import { - addDescriptionToTimeline, addNameToTimelineAndSave, createNewTimeline, + createTimelineOptionsPopoverBottomBar, goToEsqlTab, openTimelineById, openTimelineFromSettings, @@ -43,15 +45,10 @@ import { STACK_MANAGEMENT_PAGE } from '../../../../screens/kibana_navigation'; import { GET_SAVED_OBJECTS_TAGS_OPTION, SAVED_OBJECTS_ROW_TITLES, - SAVED_OBJECTS_TAGS_FILTER, } from '../../../../screens/common/stack_management'; const INITIAL_START_DATE = 'Jan 18, 2021 @ 20:33:29.186'; const INITIAL_END_DATE = 'Jan 19, 2024 @ 20:33:29.186'; -const SAVED_SEARCH_UPDATE_REQ = 'SAVED_SEARCH_UPDATE_REQ'; -const SAVED_SEARCH_UPDATE_WITH_DESCRIPTION = 'SAVED_SEARCH_UPDATE_WITH_DESCRIPTION'; -const SAVED_SEARCH_CREATE_REQ = 'SAVED_SEARCH_CREATE_REQ'; -const SAVED_SEARCH_GET_REQ = 'SAVED_SEARCH_GET_REQ'; const TIMELINE_REQ_WITH_SAVED_SEARCH = 'TIMELINE_REQ_WITH_SAVED_SEARCH'; const TIMELINE_PATCH_REQ = 'TIMELINE_PATCH_REQ'; @@ -59,59 +56,37 @@ const TIMELINE_RESPONSE_SAVED_OBJECT_ID_PATH = 'response.body.data.persistTimeline.timeline.savedObjectId'; const esqlQuery = 'from auditbeat-* | where ecs.version == "8.0.0"'; -// FLAKY: https://github.com/elastic/kibana/issues/168745 -describe.skip( +const handleIntercepts = () => { + cy.intercept('PATCH', '/api/timeline', (req) => { + if (req.body.hasOwnProperty('timeline') && req.body.timeline.savedSearchId === null) { + req.alias = TIMELINE_PATCH_REQ; + } + }); + cy.intercept('PATCH', '/api/timeline', (req) => { + if (req.body.hasOwnProperty('timeline') && req.body.timeline.savedSearchId !== null) { + req.alias = TIMELINE_REQ_WITH_SAVED_SEARCH; + } + }); +}; + +describe( 'Discover Timeline State Integration', { tags: ['@ess', '@brokenInServerless'], - // ESQL and test involving STACK_MANAGEMENT_PAGE are broken in serverless }, () => { beforeEach(() => { - cy.intercept('PATCH', '/api/timeline', (req) => { - if (req.body.hasOwnProperty('timeline') && req.body.timeline.savedSearchId === null) { - req.alias = TIMELINE_PATCH_REQ; - } - }); - cy.intercept('PATCH', '/api/timeline', (req) => { - if (req.body.hasOwnProperty('timeline') && req.body.timeline.savedSearchId !== null) { - req.alias = TIMELINE_REQ_WITH_SAVED_SEARCH; - } - }); - cy.intercept('POST', '/api/content_management/rpc/get', (req) => { - if (req.body.hasOwnProperty('contentTypeId') && req.body.contentTypeId === 'search') { - req.alias = SAVED_SEARCH_GET_REQ; - } - }); - cy.intercept('POST', '/api/content_management/rpc/create', (req) => { - if (req.body.hasOwnProperty('contentTypeId') && req.body.contentTypeId === 'search') { - req.alias = SAVED_SEARCH_CREATE_REQ; - } - }); - - cy.intercept('POST', '/api/content_management/rpc/update', (req) => { - if (req.body.hasOwnProperty('contentTypeId') && req.body.contentTypeId === 'search') { - req.alias = SAVED_SEARCH_UPDATE_REQ; - } - }); - cy.intercept('POST', '/api/content_management/rpc/update', (req) => { - if ( - req.body.hasOwnProperty('data') && - req.body.data.hasOwnProperty('description') && - req.body.data.description.length > 0 - ) { - req.alias = SAVED_SEARCH_UPDATE_WITH_DESCRIPTION; - } - }); login(); visitWithTimeRange(ALERTS_URL); - createNewTimeline(); + createTimelineOptionsPopoverBottomBar(); goToEsqlTab(); updateDateRangeInLocalDatePickers(DISCOVER_CONTAINER, INITIAL_START_DATE, INITIAL_END_DATE); + handleIntercepts(); }); - context('save/restore', () => { - it('should be able create an empty timeline with default discover state', () => { + + describe('ESQL tab state', () => { + it('should be able create an empty timeline with default esql tab state', () => { addNameToTimelineAndSave('Timerange timeline'); createNewTimeline(); goToEsqlTab(); @@ -120,7 +95,7 @@ describe.skip( `Last 15 minutes` ); }); - it('should save/restore discover dataview/timerange/filter/query/columns when saving/resoring timeline', () => { + it('should save/restore esql tab dataview/timerange/filter/query/columns when saving/resoring timeline', () => { const timelineSuffix = Date.now(); const timelineName = `DataView timeline-${timelineSuffix}`; const column1 = 'event.category'; @@ -151,7 +126,7 @@ describe.skip( ); }); }); - it('should save/restore discover dataview/timerange/filter/query/columns when timeline is opened via url', () => { + it('should save/restore esql tab dataview/timerange/filter/query/columns when timeline is opened via url', () => { const timelineSuffix = Date.now(); const timelineName = `DataView timeline-${timelineSuffix}`; const column1 = 'event.category'; @@ -177,7 +152,7 @@ describe.skip( ); }); }); - it('should save/restore discover ES|QL when saving timeline', () => { + it('should save/restore esql tab ES|QL when saving timeline', () => { const timelineSuffix = Date.now(); const timelineName = `ES|QL timeline-${timelineSuffix}`; addNameToTimelineAndSave(timelineName); @@ -196,60 +171,51 @@ describe.skip( }); }); }); - /* - * skipping because it is @brokenInServerless and this cypress tag was somehow not working - * so skipping this test both in ess and serverless. - * - * Raised issue: https://github.com/elastic/kibana/issues/165913 - * - * */ - context.skip('saved search tags', () => { - it('should save discover saved search with `Security Solution` tag', () => { + + describe('Discover saved search state for ESQL tab', () => { + it('should save esql tab saved search with `Security Solution` tag', () => { const timelineSuffix = Date.now(); const timelineName = `SavedObject timeline-${timelineSuffix}`; addDiscoverEsqlQuery(esqlQuery); addNameToTimelineAndSave(timelineName); cy.wait(`@${TIMELINE_REQ_WITH_SAVED_SEARCH}`); + cy.get(LOADING_INDICATOR).should('not.exist'); openKibanaNavigation(); navigateFromKibanaCollapsibleTo(STACK_MANAGEMENT_PAGE); cy.get(LOADING_INDICATOR).should('not.exist'); goToSavedObjectSettings(); cy.get(LOADING_INDICATOR).should('not.exist'); - cy.get(SAVED_OBJECTS_TAGS_FILTER).trigger('click'); + clickSavedObjectTagsFilter(); cy.get(GET_SAVED_OBJECTS_TAGS_OPTION('Security_Solution')).trigger('click'); cy.get(BASIC_TABLE_LOADING).should('not.exist'); cy.get(SAVED_OBJECTS_ROW_TITLES).should( 'contain.text', - `Saved Search for timeline - ${timelineName}` + `Saved search for timeline - ${timelineName}` ); }); - }); - context('saved search', () => { + it('should rename the saved search on timeline rename', () => { - const timelineSuffix = Date.now(); - const timelineName = `Rename timeline-${timelineSuffix}`; + const initialTimelineSuffix = Date.now(); + const initialTimelineName = `Timeline-${initialTimelineSuffix}`; addDiscoverEsqlQuery(esqlQuery); - - addNameToTimelineAndSave(timelineName); - cy.wait(`@${TIMELINE_PATCH_REQ}`) - .its(TIMELINE_RESPONSE_SAVED_OBJECT_ID_PATH) - .then((timelineId) => { - cy.wait(`@${SAVED_SEARCH_UPDATE_REQ}`); - cy.wait(`@${TIMELINE_REQ_WITH_SAVED_SEARCH}`); - // create an empty timeline - createNewTimeline(); - // switch to old timeline - openTimelineFromSettings(); - openTimelineById(timelineId); - cy.get(TIMELINE_TITLE).should('have.text', timelineName); - const timelineDesc = 'Timeline Description with Saved Seach'; - addDescriptionToTimeline(timelineDesc); - cy.wait(`@${SAVED_SEARCH_UPDATE_WITH_DESCRIPTION}`, { - timeout: 30000, - }).then((interception) => { - expect(interception.request.body.data.description).eq(timelineDesc); - }); - }); + addNameToTimelineAndSave(initialTimelineName); + cy.get(LOADING_INDICATOR).should('not.exist'); + const timelineSuffix = Date.now(); + const renamedTimelineName = `Rename timeline-${timelineSuffix}`; + addNameToTimelineAndSave(renamedTimelineName); + cy.wait(`@${TIMELINE_REQ_WITH_SAVED_SEARCH}`); + openKibanaNavigation(); + navigateFromKibanaCollapsibleTo(STACK_MANAGEMENT_PAGE); + cy.get(LOADING_INDICATOR).should('not.exist'); + goToSavedObjectSettings(); + cy.get(LOADING_INDICATOR).should('not.exist'); + clickSavedObjectTagsFilter(); + cy.get(GET_SAVED_OBJECTS_TAGS_OPTION('Security_Solution')).trigger('click'); + cy.get(BASIC_TABLE_LOADING).should('not.exist'); + cy.get(SAVED_OBJECTS_ROW_TITLES).should( + 'contain.text', + `Saved search for timeline - ${renamedTimelineName}` + ); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/common.ts b/x-pack/test/security_solution_cypress/cypress/tasks/common.ts index b7d0062cd5d02..ff0bbac6866cd 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/common.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/common.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { recurse } from 'cypress-recurse'; import { KIBANA_LOADING_ICON } from '../screens/security_header'; import { EUI_BASIC_TABLE_LOADING } from '../screens/common/controls'; @@ -81,3 +82,13 @@ export const waitForTableToLoad = () => { cy.get(EUI_BASIC_TABLE_LOADING).should('exist'); cy.get(EUI_BASIC_TABLE_LOADING).should('not.exist'); }; + +export const waitForTabToBeLoaded = (tabId: string) => { + recurse( + () => cy.get(tabId).click(), + ($el) => expect($el).to.have.class('euiTab-isSelected'), + { + delay: 500, + } + ); +}; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/discover.ts b/x-pack/test/security_solution_cypress/cypress/tasks/discover.ts index 75ecbcdb4503d..39ddc71eb6e41 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/discover.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/discover.ts @@ -60,7 +60,7 @@ export const addDiscoverEsqlQuery = (esqlQuery: string) => { selectCurrentDiscoverEsqlQuery(DISCOVER_ESQL_EDITABLE_INPUT); cy.get(DISCOVER_ESQL_EDITABLE_INPUT).type(`${esqlQuery}`); cy.get(DISCOVER_ESQL_EDITABLE_INPUT).blur(); - cy.get(GET_LOCAL_SEARCH_BAR_SUBMIT_BUTTON(DISCOVER_CONTAINER)).realClick(); + cy.get(GET_LOCAL_SEARCH_BAR_SUBMIT_BUTTON(DISCOVER_CONTAINER)).click(); }; export const convertNBSPToSP = (str: string) => { diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/stack_management.ts b/x-pack/test/security_solution_cypress/cypress/tasks/stack_management.ts index 4c53443c13ae1..9bc0f6360c88e 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/stack_management.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/stack_management.ts @@ -5,10 +5,15 @@ * 2.0. */ -import { SAVED_OBJECTS_SETTINGS } from '../screens/common/stack_management'; +import { + SAVED_OBJECTS_SETTINGS, + SAVED_OBJECTS_TAGS_FILTER, +} from '../screens/common/stack_management'; export const goToSavedObjectSettings = () => { - cy.get(SAVED_OBJECTS_SETTINGS).scrollIntoView(); - cy.get(SAVED_OBJECTS_SETTINGS).should('be.visible').focus(); - cy.get(SAVED_OBJECTS_SETTINGS).should('be.visible').click(); + cy.get(SAVED_OBJECTS_SETTINGS).click(); +}; + +export const clickSavedObjectTagsFilter = () => { + cy.get(SAVED_OBJECTS_TAGS_FILTER).trigger('click'); }; 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 3b77c22b862bb..6fb525e1e98a1 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts @@ -307,7 +307,7 @@ export const attachTimelineToExistingCase = () => { const clickIdHoverActionOverflowButton = () => { cy.get(ID_HOVER_ACTION_OVERFLOW_BTN).should('exist'); - cy.get(ID_HOVER_ACTION_OVERFLOW_BTN).click({ force: true }); + cy.get(ID_HOVER_ACTION_OVERFLOW_BTN).click(); }; export const clickIdToggleField = () => { @@ -339,7 +339,19 @@ export const createNewTimeline = () => { }; export const openCreateTimelineOptionsPopover = () => { - cy.get(NEW_TIMELINE_ACTION).filter(':visible').should('be.visible').click(); + cy.get(NEW_TIMELINE_ACTION).filter(':visible').click(); +}; + +export const createTimelineOptionsPopoverBottomBar = () => { + recurse( + () => { + cy.get(TIMELINE_SETTINGS_ICON).filter(':visible').click(); + return cy.get(CREATE_NEW_TIMELINE).eq(0); + }, + (sub) => sub.is(':visible') + ); + + cy.get(CREATE_NEW_TIMELINE).eq(0).click(); }; export const createTimelineOptionsPopoverBottomBar = () => { @@ -357,13 +369,13 @@ export const createTimelineOptionsPopoverBottomBar = () => { export const createTimelineTemplateOptionsPopoverBottomBar = () => { recurse( () => { - cy.get(TIMELINE_SETTINGS_ICON).filter(':visible').should('be.visible').click(); + cy.get(TIMELINE_SETTINGS_ICON).filter(':visible').click(); return cy.get(CREATE_NEW_TIMELINE_TEMPLATE).eq(0); }, (sub) => sub.is(':visible') ); - cy.get(CREATE_NEW_TIMELINE_TEMPLATE).eq(0).should('be.visible').click(); + cy.get(CREATE_NEW_TIMELINE_TEMPLATE).eq(0).click(); }; export const closeCreateTimelineOptionsPopover = () => { @@ -385,7 +397,7 @@ export const executeTimelineSearch = (query: string) => { }; export const expandFirstTimelineEventDetails = () => { - cy.get(TOGGLE_TIMELINE_EXPAND_EVENT).first().click({ force: true }); + cy.get(TOGGLE_TIMELINE_EXPAND_EVENT).first().click(); }; /** @@ -425,7 +437,7 @@ export const openTimelineFieldsBrowser = () => { export const openTimelineInspectButton = () => { cy.get(TIMELINE_INSPECT_BUTTON).should('not.be.disabled'); - cy.get(TIMELINE_INSPECT_BUTTON).click({ force: true }); + cy.get(TIMELINE_INSPECT_BUTTON).click(); }; export const openTimelineFromSettings = () => { @@ -473,7 +485,7 @@ export const populateTimeline = () => { const clickTimestampHoverActionOverflowButton = () => { cy.get(TIMESTAMP_HOVER_ACTION_OVERFLOW_BTN).should('exist'); - cy.get(TIMESTAMP_HOVER_ACTION_OVERFLOW_BTN).click({ force: true }); + cy.get(TIMESTAMP_HOVER_ACTION_OVERFLOW_BTN).click(); }; export const clickTimestampToggleField = () => { @@ -481,7 +493,7 @@ export const clickTimestampToggleField = () => { cy.get(TIMESTAMP_TOGGLE_FIELD).should('exist'); - cy.get(TIMESTAMP_TOGGLE_FIELD).click({ force: true }); + cy.get(TIMESTAMP_TOGGLE_FIELD).click(); }; export const removeColumn = (columnName: string) => { @@ -492,7 +504,7 @@ export const removeColumn = (columnName: string) => { }; export const resetFields = () => { - cy.get(RESET_FIELDS).click({ force: true }); + cy.get(RESET_FIELDS).click(); }; export const selectCase = (caseId: string) => { diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/timelines.ts b/x-pack/test/security_solution_cypress/cypress/tasks/timelines.ts index 355bf7447e8a2..19d4be697ff10 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/timelines.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/timelines.ts @@ -28,17 +28,15 @@ export const expandNotes = () => { export const importTimeline = (timeline: string) => { cy.get(IMPORT_TIMELINE_BTN).click(); - cy.get(INPUT_FILE).click({ force: true }); + cy.get(INPUT_FILE).click(); cy.get(INPUT_FILE).attachFile(timeline); cy.get(INPUT_FILE).trigger('change'); - cy.get(IMPORT_BTN).last().click({ force: true }); + cy.get(IMPORT_BTN).last().click(); cy.get(INPUT_FILE).should('not.exist'); }; export const openTimeline = (id?: string) => { - cy.get(id ? TIMELINE(id) : TIMELINE_NAME) - .should('be.visible') - .click(); + cy.get(id ? TIMELINE(id) : TIMELINE_NAME).click(); }; export const waitForTimelinesPanelToBeLoaded = () => { From 70004900d05bbe12c0650ee60a82b1f318e2ec1d Mon Sep 17 00:00:00 2001 From: Jan Monschke Date: Tue, 16 Jan 2024 14:55:13 +0100 Subject: [PATCH 2/2] remove duplicate createTimelineOptionsPopoverBottomBar --- .../cypress/tasks/timeline.ts | 12 ------------ 1 file changed, 12 deletions(-) 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 6fb525e1e98a1..a0672ad4a73a4 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts @@ -354,18 +354,6 @@ export const createTimelineOptionsPopoverBottomBar = () => { cy.get(CREATE_NEW_TIMELINE).eq(0).click(); }; -export const createTimelineOptionsPopoverBottomBar = () => { - recurse( - () => { - cy.get(TIMELINE_SETTINGS_ICON).filter(':visible').should('be.visible').click(); - return cy.get(CREATE_NEW_TIMELINE).eq(0); - }, - (sub) => sub.is(':visible') - ); - - cy.get(CREATE_NEW_TIMELINE).eq(0).should('be.visible').click(); -}; - export const createTimelineTemplateOptionsPopoverBottomBar = () => { recurse( () => {