From 84014b155d97e872d1f21d406313f0aef3038f35 Mon Sep 17 00:00:00 2001 From: Gerard Soldevila Date: Mon, 25 Sep 2023 09:32:57 +0200 Subject: [PATCH] Fix flakiness on custom time range saved searches (#165454) A bunch of tests on dashboards are customising some of the panels settings and providing custom time ranges: image Currently, the logic is not waiting for the quick toggle animation to complete, before proceeding to select a time range. This can cause a flaky behavior if the logic tries to customize the range before the button is actually available, as seen on [this failed test](https://s3.amazonaws.com/buildkiteartifacts.com/e0f3970e-3a75-4621-919f-e6c773e2bb12/0fda5127-f57f-42fb-8e5a-146b3d535916/018a4c44-5497-48b6-8521-a8a79a2f63b4/018a4c46-0e7a-4b69-9a3d-9c54c27165b0/target/test_failures/018a4c46-0e7a-4b69-9a3d-9c54c27165b0_4fcbc47e71644919129e320eea8bb3bc.html?response-content-type=text%2Fhtml&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIAQPCP3C7LZWZ5UB5F%2F20230901%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230901T094837Z&X-Amz-Expires=600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEB0aCXVzLWVhc3QtMSJGMEQCIGCyKcVLGPUawZubNzZdt5oZNb5v0saiIuPqXwI7rmwlAiAsOj%2Fiep94v%2BYZJtLY3Gw0m%2FmK5mJw2IcIBdNKFXgK%2BCr6Awjm%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8BEAAaDDAzMjM3OTcwNTMwMyIMXOd1Hm6ks%2FNE37V0Ks4DgMUso7syv87hnPcC%2BB1soxvFFnj4JnNZc6ZgkLUe93z99iPFBUsqH%2BRbUTfSbjVOEJYBKGYuvp32xvSWsYNVPXKmcej18LC0yNi%2BBzoG2X%2Bj80g%2BbGMm6YfTncjPhOE0CHHqOWXts9nQ8WpDy8XOl0zfMtuiPjzOXHo9lvw2mgYDZIJIMV72FYB9JGg8FPbLQtD3rysLGNE0VDKgl5LCnYwhY1pwRCRHnVW41QfV0pwK%2FbjNf9HjdK31LQvMY%2FGPuB3M6O2CUZLsvLGfWBeGYHtkqb0hrL9ijO1Uo28ZSS1FytPftEdF0e1kAC9C5zD56HtYm55aktOWtaaC0XPWLdWWGUq%2FKQzhxSCiXK6ovATU3zI3yPNoZs92YBYmIPMOpEI40dCCpksjPwAMCiQd%2F9gMNKP5Qp5CbYd2Khy%2FeXaT8J7HOZCueN63O0j%2FtX1tbwfznhbr74lAcRQjueRYmwboZaGSDZUQ33lSSmyZk1V9WF9eJyt88oHvIx0q9bIjvOlW05DiNKfEFWYwfBywdGuvRU6eGMs1QcDNu33Lb%2BhymudM2JZmQKIjZOcb2l3Fzctp614owH4JcRlmF4%2BIa4xHeBdRlTMysS8bTIsgMK7axacGOqYBzIpC1wgZWJ1kZ0agLWCNaMIdUl%2B4xrr7w%2Fz0843WWMhRrvbJhDTHqk5UclF%2FSROAMe0FH2XEXiQ65ILyUPlrUMels5tfQ3Pp%2FJWPi9NsQJUQ1n9uLN%2BFPDOoMo8Uxg4%2FkG2O7yTkrIdArfA6pWN9I21gFMW%2BFZy9BMYltt5T65ZKOyYAIFGpLhgfBySIBCUMgwR1kusfDhf1%2FRTvtDKD2sJKN5a0IA%3D%3D&X-Amz-SignedHeaders=host&X-Amz-Signature=35fabe908aa7514e4a92de0ed12973af85ccfb439984fc3bdd7ef3bb8fe3419b). (part of this [failed CI build](https://buildkite.com/elastic/kibana-pull-request/builds/155285#018a4c46-0e7a-4b69-9a3d-9c54c27165b0)) The goal of this PR is to add a small waiting period, to make sure the toggle animation has completed, and that the time range controls are visible and clickable. I used the opportunity to cleanup some "await delay millis" calls, reusing existing logic instead. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> (cherry picked from commit b98b6d08cd700c2c892e7d4eaffb97b3c5bbded8) --- .../services/dashboard/add_panel.ts | 3 + .../services/dashboard/panel_settings.ts | 68 +++++++++++++++---- .../apps/dashboard/group2/panel_time_range.ts | 12 ++-- .../drilldowns/explore_data_panel_action.ts | 6 +- .../apps/discover/saved_searches.ts | 4 +- .../apps/lens/open_in_lens/tsvb/dashboard.ts | 8 +-- .../test/functional/apps/uptime/overview.ts | 4 +- .../page_objects/navigational_search.ts | 7 +- .../services/cases/single_case_view.ts | 2 +- .../test/functional/services/data_stream.ts | 7 +- .../infra_source_configuration_form.ts | 10 ++- .../functional/services/uptime/navigation.ts | 2 +- 12 files changed, 91 insertions(+), 42 deletions(-) diff --git a/test/functional/services/dashboard/add_panel.ts b/test/functional/services/dashboard/add_panel.ts index 926e19cd43474..00a91dff87b85 100644 --- a/test/functional/services/dashboard/add_panel.ts +++ b/test/functional/services/dashboard/add_panel.ts @@ -236,6 +236,9 @@ export class DashboardAddPanelService extends FtrService { await this.testSubjects.click(`savedObjectTitle${embeddableName.split(' ').join('-')}`); await this.testSubjects.exists('addObjectToDashboardSuccess'); await this.closeAddPanel(); + + // close "Added successfully" toast + await this.common.clearAllToasts(); return embeddableName; } diff --git a/test/functional/services/dashboard/panel_settings.ts b/test/functional/services/dashboard/panel_settings.ts index a90a51fdc2feb..de75a9c4a7a19 100644 --- a/test/functional/services/dashboard/panel_settings.ts +++ b/test/functional/services/dashboard/panel_settings.ts @@ -8,8 +8,9 @@ import { FtrProviderContext } from '../../ftr_provider_context'; import { CommonlyUsed } from '../../page_objects/time_picker'; +import { WebElementWrapper } from '../lib/web_element_wrapper'; -export function DashboardCustomizePanelProvider({ getService }: FtrProviderContext) { +export function DashboardCustomizePanelProvider({ getService, getPageObject }: FtrProviderContext) { const log = getService('log'); const retry = getService('retry'); const toasts = getService('toasts'); @@ -39,6 +40,48 @@ export function DashboardCustomizePanelProvider({ getService }: FtrProviderConte await testSubjects.missingOrFail(this.TOGGLE_TIME_RANGE_TEST_SUBJ); } + public async findCustomTimeRangeToggleButton(): Promise { + log.debug('findCustomTimeRangeToggleButton'); + let button: WebElementWrapper | undefined; + await retry.waitFor('custom time range toggle button', async () => { + button = await testSubjects.find(this.TOGGLE_TIME_RANGE_TEST_SUBJ); + return Boolean(button); + }); + return button!; + } + + public async enableCustomTimeRange() { + log.debug('enableCustomTimeRange'); + const toggle = await this.findCustomTimeRangeToggleButton(); + + await retry.try(async () => { + if ((await toggle.getAttribute('aria-checked')) === 'false') { + await toggle.click(); + await retry.waitForWithTimeout( + 'custom time range to be enabled', + 1000, + async () => (await toggle.getAttribute('aria-checked')) === 'true' + ); + } + }); + } + + public async disableCustomTimeRange() { + log.debug('disableCustomTimeRange'); + const toggle = await this.findCustomTimeRangeToggleButton(); + + await retry.try(async () => { + if ((await toggle.getAttribute('aria-checked')) === 'true') { + await toggle.click(); + await retry.waitForWithTimeout( + 'custom time range to be disabled', + 1000, + async () => (await toggle.getAttribute('aria-checked')) === 'false' + ); + } + }); + } + public async findFlyout() { log.debug('findFlyout'); return await testSubjects.find(this.FLYOUT_TEST_SUBJ); @@ -50,15 +93,21 @@ export function DashboardCustomizePanelProvider({ getService }: FtrProviderConte return await flyout.findByCssSelector(`[data-test-subj="${testSubject}"]`); } - public async findToggleQuickMenuButton() { - log.debug('findToggleQuickMenuButton'); + public async findDatePickerQuickMenuButton() { + log.debug('findDatePickerQuickMenuButton'); return await this.findFlyoutTestSubject('superDatePickerToggleQuickMenuButton'); } - public async clickToggleQuickMenuButton() { - log.debug('clickToggleQuickMenuButton'); - const button = await this.findToggleQuickMenuButton(); - await button.click(); + public async openDatePickerQuickMenu() { + log.debug('openDatePickerQuickMenu'); + let button: WebElementWrapper | undefined; + await retry.waitFor('superDatePickerToggleQuickMenuButton to be present', async () => { + button = await this.findDatePickerQuickMenuButton(); + return Boolean(button); + }); + if (button) { + await button.click(); + } } public async clickCommonlyUsedTimeRange(time: CommonlyUsed) { @@ -111,10 +160,5 @@ export function DashboardCustomizePanelProvider({ getService }: FtrProviderConte await testSubjects.waitForDeleted('cancelCustomizePanelButton'); }); } - - public async clickToggleShowCustomTimeRange() { - log.debug('clickToggleShowCustomTimeRange'); - await testSubjects.click(this.TOGGLE_TIME_RANGE_TEST_SUBJ); - } })(); } diff --git a/x-pack/test/functional/apps/dashboard/group2/panel_time_range.ts b/x-pack/test/functional/apps/dashboard/group2/panel_time_range.ts index 2295c90d60c65..87ea17c718992 100644 --- a/x-pack/test/functional/apps/dashboard/group2/panel_time_range.ts +++ b/x-pack/test/functional/apps/dashboard/group2/panel_time_range.ts @@ -44,8 +44,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('can add a custom time range to a panel', async () => { await PageObjects.lens.createAndAddLensFromDashboard({}); await dashboardPanelActions.customizePanel(); - await dashboardCustomizePanel.clickToggleShowCustomTimeRange(); - await dashboardCustomizePanel.clickToggleQuickMenuButton(); + await dashboardCustomizePanel.enableCustomTimeRange(); + await dashboardCustomizePanel.openDatePickerQuickMenu(); await dashboardCustomizePanel.clickCommonlyUsedTimeRange('Last_30 days'); await dashboardCustomizePanel.clickSaveButton(); await PageObjects.dashboard.waitForRenderComplete(); @@ -56,7 +56,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('can remove a custom time range from a panel', async () => { await dashboardBadgeActions.clickTimeRangeBadgeAction(); - await dashboardCustomizePanel.clickToggleShowCustomTimeRange(); + await dashboardCustomizePanel.disableCustomTimeRange(); await dashboardCustomizePanel.clickSaveButton(); await PageObjects.dashboard.waitForRenderComplete(); await dashboardBadgeActions.expectMissingTimeRangeBadgeAction(); @@ -68,8 +68,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('can add a custom time range to panel', async () => { await dashboardPanelActions.saveToLibrary('My by reference visualization'); await dashboardPanelActions.customizePanel(); - await dashboardCustomizePanel.clickToggleShowCustomTimeRange(); - await dashboardCustomizePanel.clickToggleQuickMenuButton(); + await dashboardCustomizePanel.enableCustomTimeRange(); + await dashboardCustomizePanel.openDatePickerQuickMenu(); await dashboardCustomizePanel.clickCommonlyUsedTimeRange('Last_30 days'); await dashboardCustomizePanel.clickSaveButton(); await PageObjects.dashboard.waitForRenderComplete(); @@ -80,7 +80,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('can remove a custom time range from a panel', async () => { await dashboardBadgeActions.clickTimeRangeBadgeAction(); - await dashboardCustomizePanel.clickToggleShowCustomTimeRange(); + await dashboardCustomizePanel.disableCustomTimeRange(); await dashboardCustomizePanel.clickSaveButton(); await PageObjects.dashboard.waitForRenderComplete(); await dashboardBadgeActions.expectMissingTimeRangeBadgeAction(); diff --git a/x-pack/test/functional/apps/dashboard/group3/drilldowns/explore_data_panel_action.ts b/x-pack/test/functional/apps/dashboard/group3/drilldowns/explore_data_panel_action.ts index 8e943c2b3104d..cd8d09f7873f6 100644 --- a/x-pack/test/functional/apps/dashboard/group3/drilldowns/explore_data_panel_action.ts +++ b/x-pack/test/functional/apps/dashboard/group3/drilldowns/explore_data_panel_action.ts @@ -46,7 +46,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dashboard.gotoDashboardEditMode(drilldowns.DASHBOARD_WITH_PIE_CHART_NAME); await panelActions.customizePanel(); - await dashboardCustomizePanel.clickToggleShowCustomTimeRange(); + await dashboardCustomizePanel.disableCustomTimeRange(); await dashboardCustomizePanel.clickSaveButton(); await dashboard.saveDashboard('Dashboard with Pie Chart'); }); @@ -80,8 +80,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dashboard.gotoDashboardEditMode(drilldowns.DASHBOARD_WITH_PIE_CHART_NAME); await panelActions.customizePanel(); - await dashboardCustomizePanel.clickToggleShowCustomTimeRange(); - await dashboardCustomizePanel.clickToggleQuickMenuButton(); + await dashboardCustomizePanel.enableCustomTimeRange(); + await dashboardCustomizePanel.openDatePickerQuickMenu(); await dashboardCustomizePanel.clickCommonlyUsedTimeRange('Last_90 days'); await dashboardCustomizePanel.clickSaveButton(); diff --git a/x-pack/test/functional/apps/discover/saved_searches.ts b/x-pack/test/functional/apps/discover/saved_searches.ts index 76b380274f207..d7a2846c30cbb 100644 --- a/x-pack/test/functional/apps/discover/saved_searches.ts +++ b/x-pack/test/functional/apps/discover/saved_searches.ts @@ -54,8 +54,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await dataGrid.getDocCount()).to.be(500); await panelActions.customizePanel(); - await dashboardCustomizePanel.clickToggleShowCustomTimeRange(); - await dashboardCustomizePanel.clickToggleQuickMenuButton(); + await dashboardCustomizePanel.enableCustomTimeRange(); + await dashboardCustomizePanel.openDatePickerQuickMenu(); await dashboardCustomizePanel.clickCommonlyUsedTimeRange('Last_90 days'); await dashboardCustomizePanel.clickSaveButton(); diff --git a/x-pack/test/functional/apps/lens/open_in_lens/tsvb/dashboard.ts b/x-pack/test/functional/apps/lens/open_in_lens/tsvb/dashboard.ts index b6acfcf64a750..9232860012bc9 100644 --- a/x-pack/test/functional/apps/lens/open_in_lens/tsvb/dashboard.ts +++ b/x-pack/test/functional/apps/lens/open_in_lens/tsvb/dashboard.ts @@ -42,8 +42,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await dashboard.waitForRenderComplete(); const originalEmbeddableCount = await canvas.getEmbeddableCount(); await dashboardPanelActions.customizePanel(); - await dashboardCustomizePanel.clickToggleShowCustomTimeRange(); - await dashboardCustomizePanel.clickToggleQuickMenuButton(); + await dashboardCustomizePanel.enableCustomTimeRange(); + await dashboardCustomizePanel.openDatePickerQuickMenu(); await dashboardCustomizePanel.clickCommonlyUsedTimeRange('Last_30 days'); await dashboardCustomizePanel.clickSaveButton(); await dashboard.waitForRenderComplete(); @@ -80,8 +80,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await dashboard.waitForRenderComplete(); const originalEmbeddableCount = await canvas.getEmbeddableCount(); await dashboardPanelActions.customizePanel(); - await dashboardCustomizePanel.clickToggleShowCustomTimeRange(); - await dashboardCustomizePanel.clickToggleQuickMenuButton(); + await dashboardCustomizePanel.enableCustomTimeRange(); + await dashboardCustomizePanel.openDatePickerQuickMenu(); await dashboardCustomizePanel.clickCommonlyUsedTimeRange('Last_30 days'); await dashboardCustomizePanel.clickSaveButton(); await dashboard.waitForRenderComplete(); diff --git a/x-pack/test/functional/apps/uptime/overview.ts b/x-pack/test/functional/apps/uptime/overview.ts index afe19a1ae1938..81e5795533b69 100644 --- a/x-pack/test/functional/apps/uptime/overview.ts +++ b/x-pack/test/functional/apps/uptime/overview.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export const UPTIME_HEARTBEAT_DATA = 'x-pack/test/functional/es_archives/uptime/full_heartbeat'; export default ({ getPageObjects, getService }: FtrProviderContext) => { - const { uptime } = getPageObjects(['uptime']); + const { uptime, common } = getPageObjects(['uptime', 'common']); const retry = getService('retry'); const esArchiver = getService('esArchiver'); @@ -104,7 +104,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); await uptime.setMonitorListPageSize(50); // the pagination parameter should be cleared after a size change - await new Promise((resolve) => setTimeout(resolve, 1000)); + await common.sleep(1000); await retry.try(async () => { await uptime.pageUrlContains('pagination', false); }); diff --git a/x-pack/test/functional/page_objects/navigational_search.ts b/x-pack/test/functional/page_objects/navigational_search.ts index 46fed2814c0df..ae27d6d68a4a5 100644 --- a/x-pack/test/functional/page_objects/navigational_search.ts +++ b/x-pack/test/functional/page_objects/navigational_search.ts @@ -12,11 +12,10 @@ interface SearchResult { label: string; } -const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - export class NavigationalSearchPageObject extends FtrService { private readonly find = this.ctx.getService('find'); private readonly testSubjects = this.ctx.getService('testSubjects'); + private readonly common = this.ctx.getPageObject('common'); async focus() { const field = await this.testSubjects.find('nav-search-input'); @@ -69,7 +68,7 @@ export class NavigationalSearchPageObject extends FtrService { // without heavy flakiness in this situation. // there is NO ui indication of any kind to detect when all the emissions are done, // so we are forced to fallback to awaiting a given amount of time once the first options are displayed. - await delay(waitUntil); + await this.common.sleep(waitUntil); } async getDisplayedResults() { @@ -79,7 +78,7 @@ export class NavigationalSearchPageObject extends FtrService { async isNoResultsPlaceholderDisplayed(checkAfter: number = 3000) { // see comment in `waitForResultsLoaded` - await delay(checkAfter); + await this.common.sleep(checkAfter); return this.testSubjects.exists('nav-search-no-results'); } diff --git a/x-pack/test/functional/services/cases/single_case_view.ts b/x-pack/test/functional/services/cases/single_case_view.ts index 008f89c5b3ee8..27877f592d5dc 100644 --- a/x-pack/test/functional/services/cases/single_case_view.ts +++ b/x-pack/test/functional/services/cases/single_case_view.ts @@ -106,7 +106,7 @@ export function CasesSingleViewServiceProvider({ getService, getPageObject }: Ft '[data-test-subj="euiMarkdownEditorToolbarButton"][aria-label="Visualization"]' ); await addVisualizationButton.moveMouseTo(); - await new Promise((resolve) => setTimeout(resolve, 500)); // give tooltip time to open + await common.sleep(500); // give tooltip time to open }, async assertCaseTitle(expectedTitle: string) { diff --git a/x-pack/test/functional/services/data_stream.ts b/x-pack/test/functional/services/data_stream.ts index 13cc8547b5677..2864be1e0dc2b 100644 --- a/x-pack/test/functional/services/data_stream.ts +++ b/x-pack/test/functional/services/data_stream.ts @@ -8,15 +8,14 @@ import type { MappingProperty } from '@elastic/elasticsearch/lib/api/types'; import type { FtrProviderContext } from '../ftr_provider_context'; -const waitFor = (time: number = 1000) => new Promise((r) => setTimeout(r, time)); - /** * High level interface to operate with Elasticsearch data stream and TSDS. */ -export function DataStreamProvider({ getService }: FtrProviderContext) { +export function DataStreamProvider({ getService, getPageObject }: FtrProviderContext) { const es = getService('es'); const log = getService('log'); const retry = getService('retry'); + const common = getPageObject('common'); const downsampleDefaultOptions = { isStream: true, @@ -65,7 +64,7 @@ export function DataStreamProvider({ getService }: FtrProviderContext) { waitTime / 1000 }s before running the downsampling to avoid a null_pointer_exception` ); - await waitFor(waitTime); + await common.sleep(waitTime); try { log.info(`downsampling "${sourceIndex}" index...`); diff --git a/x-pack/test/functional/services/infra_source_configuration_form.ts b/x-pack/test/functional/services/infra_source_configuration_form.ts index 3f77991ba5041..741d42ac16fda 100644 --- a/x-pack/test/functional/services/infra_source_configuration_form.ts +++ b/x-pack/test/functional/services/infra_source_configuration_form.ts @@ -8,10 +8,14 @@ import { FtrProviderContext } from '../ftr_provider_context'; import { WebElementWrapper } from '../../../../test/functional/services/lib/web_element_wrapper'; -export function InfraSourceConfigurationFormProvider({ getService }: FtrProviderContext) { +export function InfraSourceConfigurationFormProvider({ + getService, + getPageObject, +}: FtrProviderContext) { const retry = getService('retry'); const testSubjects = getService('testSubjects'); const browser = getService('browser'); + const common = getPageObject('common'); return { /** @@ -94,7 +98,7 @@ export function InfraSourceConfigurationFormProvider({ getService }: FtrProvider const movementDifference = destinationIndex - sourceIndex; await moveLogColumnHandle.pressKeys(browser.keys.SPACE); for (let i = 0; i < Math.abs(movementDifference); i++) { - await new Promise((res) => setTimeout(res, KEY_PRESS_DELAY_MS)); + await common.sleep(KEY_PRESS_DELAY_MS); if (movementDifference > 0) { await moveLogColumnHandle.pressKeys(browser.keys.ARROW_DOWN); } else { @@ -102,7 +106,7 @@ export function InfraSourceConfigurationFormProvider({ getService }: FtrProvider } } await moveLogColumnHandle.pressKeys(browser.keys.SPACE); - await new Promise((res) => setTimeout(res, KEY_PRESS_DELAY_MS)); + await common.sleep(KEY_PRESS_DELAY_MS); }, /** diff --git a/x-pack/test/functional/services/uptime/navigation.ts b/x-pack/test/functional/services/uptime/navigation.ts index 57e39f6bf9d0e..8a3898e813c6a 100644 --- a/x-pack/test/functional/services/uptime/navigation.ts +++ b/x-pack/test/functional/services/uptime/navigation.ts @@ -32,7 +32,7 @@ export function UptimeNavigationProvider({ getService, getPageObjects }: FtrProv return { async refreshApp() { await browser.refresh(); - await new Promise((resolve) => setTimeout(resolve, 1000)); + await PageObjects.common.sleep(1000); await PageObjects.header.waitUntilLoadingHasFinished(); },