From 818b9bbc35fb6f6c30bbebd7f333182af08ace8b Mon Sep 17 00:00:00 2001 From: Jan Monschke Date: Wed, 17 Jan 2024 13:10:13 +0100 Subject: [PATCH] [8.12] [Investigations] - Unskip and refactor discover state tests (#173308) (#174914) # Backport This will backport the following commits from `main` to `8.12`: - [[Investigations] - Unskip and refactor discover state tests (#173308)](https://github.com/elastic/kibana/pull/173308) ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) --------- Co-authored-by: Michael Olorunnisola --- .../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 | 22 +-- .../cypress/tasks/timelines.ts | 8 +- 7 files changed, 94 insertions(+), 110 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..a0672ad4a73a4 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,31 +339,31 @@ 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').should('be.visible').click(); + 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).should('be.visible').click(); + cy.get(CREATE_NEW_TIMELINE).eq(0).click(); }; 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 +385,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 +425,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 +473,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 +481,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 +492,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 = () => {