Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import { expect } from '@playwright/test';
import type { ScoutPage } from '../fixtures/scope/test/scout_page';
import { resolveSelector, type SelectorInput } from '../utils';

export interface ComboBoxInputOptions {
/** When true, uses fill() instead of pressSequentially() for faster input without keystroke simulation. */
useFill?: boolean;
}

// https://eui.elastic.co/docs/components/forms/selection/combo-box/
export class EuiComboBoxWrapper {
private readonly page: ScoutPage;
Expand Down Expand Up @@ -65,17 +70,21 @@ export class EuiComboBoxWrapper {
await this.page.keyboard.press('Escape');
}

private async typeValueInSearch(value: string) {
await this.comboBoxSearchInput.pressSequentially(value, { delay: 50 });
private async typeValueInSearch(value: string, options?: ComboBoxInputOptions) {
if (options?.useFill) {
await this.comboBoxSearchInput.fill(value);
} else {
await this.comboBoxSearchInput.pressSequentially(value, { delay: 50 });
}
}

async selectMultiOption(value: string) {
async selectMultiOption(value: string, options?: ComboBoxInputOptions) {
await this.checkIfAlreadySelected(value);

// put cursor in the comboBox input field
await this.comboBoxMainInput.click();
// type the value with a delay to allow for async option loading
await this.typeValueInSearch(value);
await this.typeValueInSearch(value, options);
// select the option that matches the value
const trimmedValue = value.trim();
await this.page.locator(`.euiFilterSelectItem[title="${trimmedValue}"]`).click();
Expand All @@ -85,7 +94,7 @@ export class EuiComboBoxWrapper {
await this.verifySelectionAndClose(value);
}

async selectMultiOptions(values: string[]) {
async selectMultiOptions(values: string[], options?: ComboBoxInputOptions) {
const selectedOptions = await this.getSelectedMultiOptions();

// Check if any values are already selected before starting UI interactions
Expand All @@ -97,7 +106,7 @@ export class EuiComboBoxWrapper {
await this.comboBoxMainInput.click();

for (const value of values) {
await this.typeValueInSearch(value);
await this.typeValueInSearch(value, options);
const trimmedValue = value.trim();
await this.page.locator(`.euiFilterSelectItem[title="${trimmedValue}"]`).click();
await this.waitForBadgeToBe(value, 'visible');
Expand All @@ -108,11 +117,11 @@ export class EuiComboBoxWrapper {
await this.page.keyboard.press('Escape');
}

async setCustomMultiOption(value: string) {
async setCustomMultiOption(value: string, options?: ComboBoxInputOptions) {
await this.checkIfAlreadySelected(value);

await this.comboBoxMainInput.click();
await this.typeValueInSearch(value);
await this.typeValueInSearch(value, options);
await this.page.keyboard.press('Enter');

await this.waitForBadgeToBe(value, 'visible');
Expand Down Expand Up @@ -146,19 +155,18 @@ export class EuiComboBoxWrapper {
expect(await this.getSelectedMultiOptions()).not.toContain(value);
}

// Select a single option in the comboBox
async selectSingleOption(
value: string,
options: {
optionTestSubj?: string;
optionRoleName?: string;
/** Use for combos backed by slow suggestion APIs so other suites keep default waits. */
optionVisibilityTimeoutMs?: number;
} = {}
} & ComboBoxInputOptions = {}
) {
await this.clear();
await this.comboBoxMainInput.click();
await this.typeValueInSearch(value);
await this.typeValueInSearch(value, options);
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
// Prefer a specific test subj when option text is ambiguous.
const trimmedValue = value.trim();
const optionLocator = options.optionTestSubj
Expand All @@ -182,11 +190,11 @@ export class EuiComboBoxWrapper {
options: {
/** Use when confirming selection may lag after Enter (slow suggestions / CI). */
settleTimeoutMs?: number;
} = {}
} & ComboBoxInputOptions = {}
) {
await this.clear();
await this.comboBoxMainInput.click();
await this.typeValueInSearch(value);
await this.typeValueInSearch(value, options);
await this.page.keyboard.press('Enter');
await expect
.poll(async () => await this.getSelectedValue(), {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

import { EuiComboBoxWrapper } from './combo_box';
import type { ComboBoxInputOptions } from './combo_box';
import { EuiSelectableWrapper } from './selectable';
import { EuiCheckBoxWrapper } from './check_box';
import { EuiDataGridWrapper } from './data_grid';
Expand All @@ -16,6 +17,7 @@ import { EuiFieldTextWrapper } from './field_text';
import { EuiCodeBlockWrapper } from './code_block';
import { EuiSuperSelectWrapper } from './super_select';

export type { ComboBoxInputOptions };
export {
EuiComboBoxWrapper,
EuiSelectableWrapper,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ type CommonlyUsedTimeRange =
| 'Last_90 days'
| 'Last_1 year';

interface TimeoutOptions {
timeout?: number;
}

export class DashboardApp {
private readonly renderable: RenderablePage;
private readonly toasts: Toasts;
Expand Down Expand Up @@ -133,9 +137,9 @@ export class DashboardApp {
}

/** Navigates to the new dashboard creation page and waits for the editor toolbar to load. */
async openNewDashboard() {
async openNewDashboard(options?: TimeoutOptions) {
await this.page.gotoApp('dashboards', { hash: '/create' });
await expect(this.addTopNavButton).toBeVisible({ timeout: 20_000 });
await expect(this.addTopNavButton).toBeVisible({ timeout: options?.timeout ?? 20_000 });
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is not the main issue with the test:

Image

Could be that in Cloud typing Service name causes warning and affects the next steps?

I believe running flaky-test-runner against Kibana local servers doesn't really help to verify the fix. Please run the test against ECH deployment to confirm the issue is fixed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be that in Cloud typing Service name causes warning and affects the next steps?

Yes, this would break things.

A quick overview of the changes is:

  • Increase timeout from 20 seconds to 45 seconds because, running locally against an ECH environment over 30 times, one of them it got to the 20 second timeout
  • Added an explicit closing of a popup that caused some flakiness
  • [Newest change] Added an option to fill the search input instead of pressing each key (reduced the flakiness A LOT)

Not directly related to the test, more code related, but added the option for other parts of the same files to have a custom timeout or to use fill instead of type for future tests.

Note: Added photo of ECH test passing (did a commit afterwards because the date input is not the same locally vs ECH, everything else is the same and should pass)


private getSettingsFlyout() {
Expand Down Expand Up @@ -264,12 +268,12 @@ export class DashboardApp {
* Opens the "Add from library" flyout.
* Low-level building block used by addEmbeddable().
*/
private async openLibraryFlyout() {
private async openLibraryFlyout(options?: TimeoutOptions) {
await this.addTopNavButton.click();
await this.page.testSubj.click('addToDashboardTab-library');
await expect(this.savedObjectsFinderTable).toBeVisible();
await expect(this.savedObjectFinderLoadingIndicator).toBeHidden({
timeout: 30_000,
timeout: options?.timeout ?? 30_000,
});
}

Expand All @@ -287,8 +291,13 @@ export class DashboardApp {
*
* @param embeddableName - Name with dashes (e.g., 'Rendering-Test:-saved-search')
* @param embeddableType - Optional type filter (e.g., 'search', 'Visualization')
* @param options - Optional timeout overrides
*/
private async filterEmbeddableNames(embeddableName: string, embeddableType?: string) {
private async filterEmbeddableNames(
embeddableName: string,
embeddableType?: string,
options?: TimeoutOptions
) {
// Build search query using type filter and quoted name.
// type:(search) "Rendering Test:-saved-search" (only first dash replaced with space)
const typePrefix = embeddableType ? `type:(${embeddableType}) ` : '';
Expand All @@ -301,7 +310,7 @@ export class DashboardApp {

// Wait for search results to load
await expect(this.savedObjectFinderLoadingIndicator).toBeHidden({
timeout: 30_000,
timeout: options?.timeout ?? 30_000,
});
}

Expand All @@ -310,10 +319,11 @@ export class DashboardApp {
*
* @param embeddableName - Name with dashes (e.g., 'Rendering-Test:-saved-search')
* @param embeddableType - Optional type filter (e.g., 'search', 'Visualization')
* @param options - Optional timeout overrides
*/
async addEmbeddable(embeddableName: string, embeddableType?: string) {
await this.openLibraryFlyout();
await this.filterEmbeddableNames(embeddableName, embeddableType);
async addEmbeddable(embeddableName: string, embeddableType?: string, options?: TimeoutOptions) {
await this.openLibraryFlyout(options);
await this.filterEmbeddableNames(embeddableName, embeddableType, options);

// Click on the saved object title
const titleSelector = `savedObjectTitle${embeddableName.split(' ').join('-')}`;
Expand All @@ -335,19 +345,21 @@ export class DashboardApp {
* Wrapper around addEmbeddable() with type='search'.
*
* @param searchName - Name with dashes (e.g., 'Rendering-Test:-saved-search')
* @param options - Optional timeout overrides
*/
async addSavedSearch(searchName: string) {
return this.addEmbeddable(searchName, 'search');
async addSavedSearch(searchName: string, options?: TimeoutOptions) {
return this.addEmbeddable(searchName, 'search', options);
}

/**
* Adds a Lens visualization to the dashboard from the library.
* Wrapper around addEmbeddable() with type='lens'.
*
* @param lensName - Name of the Lens saved object
* @param options - Optional timeout overrides
*/
async addLens(lensName: string) {
return this.addEmbeddable(lensName, 'lens');
async addLens(lensName: string, options?: TimeoutOptions) {
return this.addEmbeddable(lensName, 'lens', options);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ const APM_DASHBOARD_DATA_VIEW_TITLE = 'traces-apm*,logs-apm*,metrics-apm*';

const { SERVICE_MAP_TEST_SERVICE, SERVICE_MAP_TEST_ENVIRONMENT_STAGING } = testData;

// Failing: See https://github.com/elastic/kibana/issues/265639
test.describe.skip(
test.describe(
'Service map embeddable',
{ tag: [...tags.stateful.classic, ...tags.serverless.observability.complete] },
() => {
Expand Down Expand Up @@ -47,14 +46,12 @@ test.describe.skip(
pageObjects,
}) => {
await test.step('open a new dashboard', async () => {
await pageObjects.dashboard.openNewDashboard();
await pageObjects.dashboard.openNewDashboard({ timeout: EXTENDED_TIMEOUT });
});

await test.step('set time range to last 1 hour to ensure test data is visible', async () => {
await pageObjects.datePicker.setCommonlyUsedTime('Last_1_hour');
await page
.getByTestId('dateRangePickerControlButton')
.waitFor({ timeout: EXTENDED_TIMEOUT });
await expect(page.getByTestId('dateRangePickerControlButton')).toContainText('Last 1 hour');
await page.getByTestId('dateRangePickerControlButton').blur();
});

Expand Down Expand Up @@ -90,7 +87,7 @@ test.describe.skip(
page,
'apmServiceMapEditorServiceNameComboBox'
);
await serviceNameComboBox.selectSingleOption(SERVICE_MAP_TEST_SERVICE);
await serviceNameComboBox.selectSingleOption(SERVICE_MAP_TEST_SERVICE, { useFill: true });

// Select environment from dropdown (has a default value so manually type and select)
const environmentInput = page.testSubj
Expand Down Expand Up @@ -148,6 +145,9 @@ test.describe.skip(

const popoverTitle = page.testSubj.locator('serviceMapPopoverTitle');
await expect(popoverTitle).toHaveText(SERVICE_MAP_TEST_SERVICE);

await page.keyboard.press('Escape');
await expect(popoverTitle).toBeHidden();
});

await test.step('maximize the Service map panel', async () => {
Expand Down
Loading