diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_update_timeline.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_update_timeline.test.tsx index 10088446c045c..b2c990b12eced 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_update_timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_update_timeline.test.tsx @@ -116,7 +116,7 @@ describe('dispatchUpdateTimeline', () => { ...mockTimelineModel, version: null, updated: undefined, - changed: undefined, + changed: true, }, }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_update_timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_update_timeline.tsx index 2d380e700f7d3..2927b2365221d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_update_timeline.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_update_timeline.tsx @@ -53,7 +53,9 @@ export const useUpdateTimeline = () => { }: UpdateTimeline) => { let _timeline = timeline; if (duplicate) { - _timeline = { ...timeline, updated: undefined, changed: undefined, version: null }; + // Reset the `updated` and `version` fields because a duplicated timeline has not been saved yet. + // The `changed` field is set to true because the duplicated timeline needs to be saved. + _timeline = { ...timeline, updated: undefined, changed: true, version: null }; } if (!isEmpty(_timeline.indexNames)) { dispatch( diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/unsaved_timeline.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/unsaved_timeline.cy.ts index 1c46f8491f986..0d938a00ac4f3 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/unsaved_timeline.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/unsaved_timeline.cy.ts @@ -18,7 +18,7 @@ import { openKibanaNavigation, } from '../../../tasks/kibana_navigation'; import { login } from '../../../tasks/login'; -import { visitWithTimeRange } from '../../../tasks/navigation'; +import { visit, visitWithTimeRange } from '../../../tasks/navigation'; import { closeTimelineUsingCloseButton, openTimelineUsingToggle, @@ -32,13 +32,14 @@ import { } from '../../../tasks/serverless/navigation'; import { addNameToTimelineAndSave, + closeTimeline, createNewTimeline, + duplicateFirstTimeline, populateTimeline, } from '../../../tasks/timeline'; -import { EXPLORE_URL, hostsUrl, MANAGE_URL } from '../../../urls/navigation'; +import { EXPLORE_URL, hostsUrl, MANAGE_URL, TIMELINES_URL } from '../../../urls/navigation'; -// FLAKY: https://github.com/elastic/kibana/issues/174068 -describe.skip('[ESS] Save Timeline Prompts', { tags: ['@ess'] }, () => { +describe('[ESS] Save Timeline Prompts', { tags: ['@ess'] }, () => { beforeEach(() => { login(); visitWithTimeRange(hostsUrl('allHosts')); @@ -119,6 +120,17 @@ describe.skip('[ESS] Save Timeline Prompts', { tags: ['@ess'] }, () => { cy.get(TIMELINE_SAVE_MODAL).should('not.exist'); cy.url().should('not.contain', MANAGE_URL); }); + + it('should prompt when a timeline is duplicated but not saved', () => { + addNameToTimelineAndSave('Original'); + closeTimeline(); + visit(TIMELINES_URL); + duplicateFirstTimeline(); + closeTimeline(); + openKibanaNavigation(); + navigateFromKibanaCollapsibleTo(MANAGE_PAGE); + cy.get(APP_LEAVE_CONFIRM_MODAL).should('be.visible'); + }); }); // In serverless it is not possible to use the navigation menu without closing the timeline @@ -201,4 +213,14 @@ describe('[serverless] Save Timeline Prompts', { tags: ['@serverless'] }, () => navigateToExploreUsingBreadcrumb(); // explore has timelines disabled cy.get(APP_LEAVE_CONFIRM_MODAL).should('not.exist'); }); + + it('should prompt when a timeline is duplicated but not saved', () => { + addNameToTimelineAndSave('Original'); + closeTimeline(); + visit(TIMELINES_URL); + duplicateFirstTimeline(); + closeTimeline(); + navigateToExplorePageInServerless(); // security page with timelines disabled + cy.get(APP_LEAVE_CONFIRM_MODAL).should('be.visible'); + }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/screens/timelines.ts b/x-pack/test/security_solution_cypress/cypress/screens/timelines.ts index f0435a0fa4950..df5c975b21290 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/timelines.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/timelines.ts @@ -38,6 +38,8 @@ export const TIMELINES_PINNED_EVENT_COUNT = '[data-test-subj="pinned-event-count export const TIMELINES_TABLE = '[data-test-subj="timelines-table"]'; +export const DUPLICATE_TIMELINE = '[data-test-subj="open-duplicate"]'; + export const TIMELINES_USERNAME = '[data-test-subj="username"]'; export const REFRESH_BUTTON = '[data-test-subj="refreshButton-linkIcon"]'; 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 5866011ec1cbf..56a1214a90529 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts @@ -91,15 +91,22 @@ import { QUERY_EVENT_COUNT, TIMELINE_ENABLE_DISABLE_ALL_ROW_RENDERER, TIMELINE_DISCOVER_FIELDS_BUTTON, + TIMELINE_TITLE, } from '../screens/timeline'; -import { REFRESH_BUTTON, TIMELINE, TIMELINES_TAB_TEMPLATE } from '../screens/timelines'; +import { + DUPLICATE_TIMELINE, + REFRESH_BUTTON, + TIMELINE, + TIMELINES_TABLE, + TIMELINES_TAB_TEMPLATE, +} from '../screens/timelines'; import { waitForTabToBeLoaded } from './common'; import { closeFieldsBrowser, filterFieldsBrowser } from './fields_browser'; import { TIMELINE_CONTEXT_MENU_BTN } from '../screens/alerts'; import { LOADING_INDICATOR } from '../screens/security_header'; -import { TOASTER } from '../screens/alerts_detection_rules'; +import { COLLAPSED_ACTION_BTN, TOASTER } from '../screens/alerts_detection_rules'; import { RUNTIME_FIELD_INPUT, SAVE_FIELD_BUTTON } from '../screens/create_runtime_field'; const hostExistsQuery = 'host.name: *'; @@ -150,6 +157,14 @@ export const addNameAndDescriptionToTimeline = ( cy.get(TIMELINE_TITLE_INPUT).should('not.exist'); }; +export const duplicateFirstTimeline = () => { + cy.get(TIMELINES_TABLE).within(() => { + cy.get(COLLAPSED_ACTION_BTN).first().click(); + }); + cy.get(DUPLICATE_TIMELINE).click(); + cy.get(TIMELINE_TITLE).should('be.visible'); +}; + export const goToNotesTab = () => { cy.get(NOTES_TAB_BUTTON).click(); cy.get(NOTES_TEXT_AREA).should('be.visible');