kibana/test/functional/page_objects/dashboard_page_controls.ts
Hannah Mudge eea3b1b8f4
[Controls] Fix flaky time slider test (#169553)
Closes https://github.com/elastic/kibana/issues/169404

## Summary

The `Pin start` tooltip has a tendency to get in the way of the
`timeSlider-nextTimeWindow` button:


![image](dc4855ab-6c59-4003-83f1-de778f97b0f8)

To prevent this, I've made it so that the popover is closed **before**
the `next` button is pressed.


### [Flaky Test
Runner](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3681)


![image](d339d256-dced-4bbd-b287-e04cd1268835)

### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
2023-10-23 13:57:51 -06:00

751 lines
29 KiB
TypeScript

/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import expect from '@kbn/expect';
import {
OPTIONS_LIST_CONTROL,
RANGE_SLIDER_CONTROL,
ControlWidth,
} from '@kbn/controls-plugin/common';
import { OptionsListSearchTechnique } from '@kbn/controls-plugin/common/options_list/types';
import { ControlGroupChainingSystem } from '@kbn/controls-plugin/common/control_group/types';
import { OptionsListSortingType } from '@kbn/controls-plugin/common/options_list/suggestions_sorting';
import { WebElementWrapper } from '../services/lib/web_element_wrapper';
import { FtrService } from '../ftr_provider_context';
const CONTROL_DISPLAY_NAMES: { [key: string]: string } = {
default: 'No field selected yet',
[OPTIONS_LIST_CONTROL]: 'Options list',
[RANGE_SLIDER_CONTROL]: 'Range slider',
};
interface OptionsListAdditionalSettings {
searchTechnique?: OptionsListSearchTechnique;
defaultSortType?: OptionsListSortingType;
ignoreTimeout?: boolean;
allowMultiple?: boolean;
hideExclude?: boolean;
hideExists?: boolean;
hideSort?: boolean;
}
export const OPTIONS_LIST_ANIMAL_SOUND_SUGGESTIONS: { [key: string]: number } = {
hiss: 5,
ruff: 4,
bark: 3,
grrr: 3,
meow: 3,
growl: 2,
grr: 2,
'bow ow ow': 1,
};
export class DashboardPageControls extends FtrService {
private readonly log = this.ctx.getService('log');
private readonly find = this.ctx.getService('find');
private readonly retry = this.ctx.getService('retry');
private readonly browser = this.ctx.getService('browser');
private readonly testSubjects = this.ctx.getService('testSubjects');
private readonly common = this.ctx.getPageObject('common');
/* -----------------------------------------------------------
General controls functions
----------------------------------------------------------- */
public async expectControlsEmpty() {
await this.testSubjects.existOrFail('controls-empty');
}
public async getAllControlIds() {
const controlFrames = await this.testSubjects.findAll('control-frame');
const ids = await Promise.all(
controlFrames.map(async (controlFrame) => await controlFrame.getAttribute('data-control-id'))
);
this.log.debug('Got all control ids:', ids);
return ids;
}
public async getAllControlTitles() {
const titleObjects = await this.testSubjects.findAll('control-frame-title');
const titles = await Promise.all(
titleObjects.map(async (title) => (await title.getVisibleText()).split('\n')[0])
);
this.log.debug('Got all control titles:', titles);
return titles;
}
public async doesControlTitleExist(title: string) {
const titles = await this.getAllControlTitles();
return Boolean(titles.find((currentTitle) => currentTitle.indexOf(title)));
}
public async getControlsCount() {
const allTitles = await this.getAllControlTitles();
return allTitles.length;
}
public async clearAllControls() {
const controlIds = await this.getAllControlIds();
for (const controlId of controlIds) {
await this.removeExistingControl(controlId);
}
}
public async openControlsMenu() {
const isOpen = await this.testSubjects.exists(`controls-create-button`, { timeout: 2500 });
if (!isOpen) {
await this.testSubjects.click('dashboard-controls-menu-button');
}
}
public async openCreateControlFlyout() {
this.log.debug(`Opening flyout for creating a control`);
await this.openControlsMenu();
await this.testSubjects.click('controls-create-button');
await this.retry.try(async () => {
await this.testSubjects.existOrFail('control-editor-flyout');
});
await this.controlEditorVerifyType('default');
}
/* -----------------------------------------------------------
Control group editor flyout
----------------------------------------------------------- */
public async openControlGroupSettingsFlyout() {
this.log.debug('Open controls group settings flyout');
await this.openControlsMenu();
await this.testSubjects.click('controls-settings-button');
await this.retry.try(async () => {
await this.testSubjects.existOrFail('control-group-settings-flyout');
});
}
public async deleteAllControls() {
this.log.debug('Delete all controls');
if ((await this.getControlsCount()) === 0) return;
await this.openControlGroupSettingsFlyout();
await this.testSubjects.click('delete-all-controls-button');
await this.testSubjects.click('confirmModalConfirmButton');
expect(await this.getControlsCount()).to.be(0);
}
public async adjustControlsLayout(layout: 'oneLine' | 'twoLine') {
this.log.debug(`Adjust controls layout to "${layout}"`);
await this.openControlGroupSettingsFlyout();
await this.testSubjects.existOrFail('control-group-layout-options');
await this.testSubjects.click(`control-editor-layout-${layout}`);
await this.testSubjects.click('control-group-editor-save');
}
public async updateChainingSystem(chainingSystem: ControlGroupChainingSystem) {
this.log.debug(`Update control group chaining system to ${chainingSystem}`);
await this.openControlGroupSettingsFlyout();
await this.testSubjects.existOrFail('control-group-chaining');
// currently there are only two chaining systems, so a switch is used.
const switchStateToChainingSystem: { [key: string]: ControlGroupChainingSystem } = {
true: 'HIERARCHICAL',
false: 'NONE',
};
const switchState = await this.testSubjects.getAttribute('control-group-chaining', 'checked');
if (chainingSystem !== switchStateToChainingSystem[switchState]) {
await this.testSubjects.click('control-group-chaining');
}
await this.testSubjects.click('control-group-editor-save');
}
public async setSwitchState(goalState: boolean, subject: string) {
await this.testSubjects.existOrFail(subject);
const currentStateIsChecked =
(await this.testSubjects.getAttribute(subject, 'aria-checked')) === 'true';
if (currentStateIsChecked !== goalState) {
await this.testSubjects.click(subject);
}
await this.retry.try(async () => {
const stateIsChecked = (await this.testSubjects.getAttribute(subject, 'checked')) === 'true';
expect(stateIsChecked).to.be(goalState);
});
}
public async updateValidationSetting(validate: boolean) {
this.log.debug(`Update control group validation setting to ${validate}`);
await this.openControlGroupSettingsFlyout();
await this.setSwitchState(validate, 'control-group-validate-selections');
await this.testSubjects.click('control-group-editor-save');
}
public async updateAllQuerySyncSettings(querySync: boolean) {
this.log.debug(`Update all control group query sync settings to ${querySync}`);
await this.openControlGroupSettingsFlyout();
await this.setSwitchState(querySync, 'control-group-query-sync');
await this.testSubjects.click('control-group-editor-save');
}
public async ensureAdvancedQuerySyncIsOpened() {
const advancedAccordion = await this.testSubjects.find(`control-group-query-sync-advanced`);
const opened = await advancedAccordion.elementHasClass('euiAccordion-isOpen');
if (!opened) {
await this.testSubjects.click(`control-group-query-sync-advanced`);
await this.retry.try(async () => {
expect(await advancedAccordion.elementHasClass('euiAccordion-isOpen')).to.be(true);
});
}
}
public async updateSyncTimeRangeAdvancedSetting(syncTimeRange: boolean) {
this.log.debug(`Update filter sync advanced setting to ${syncTimeRange}`);
await this.openControlGroupSettingsFlyout();
await this.ensureAdvancedQuerySyncIsOpened();
await this.setSwitchState(syncTimeRange, 'control-group-query-sync-time-range');
await this.testSubjects.click('control-group-editor-save');
}
public async updateSyncQueryAdvancedSetting(syncQuery: boolean) {
this.log.debug(`Update filter sync advanced setting to ${syncQuery}`);
await this.openControlGroupSettingsFlyout();
await this.ensureAdvancedQuerySyncIsOpened();
await this.setSwitchState(syncQuery, 'control-group-query-sync-query');
await this.testSubjects.click('control-group-editor-save');
}
public async updateSyncFilterAdvancedSetting(syncFilters: boolean) {
this.log.debug(`Update filter sync advanced setting to ${syncFilters}`);
await this.openControlGroupSettingsFlyout();
await this.ensureAdvancedQuerySyncIsOpened();
await this.setSwitchState(syncFilters, 'control-group-query-sync-filters');
await this.testSubjects.click('control-group-editor-save');
}
/* -----------------------------------------------------------
Individual controls functions
----------------------------------------------------------- */
// Control Frame functions
public async getControlElementById(controlId: string): Promise<WebElementWrapper> {
const errorText = `Control frame ${controlId} could not be found`;
let controlElement: WebElementWrapper | undefined;
await this.retry.try(async () => {
const controlFrames = await this.testSubjects.findAll('control-frame');
const framesWithIds = await Promise.all(
controlFrames.map(async (frame) => {
const id = await frame.getAttribute('data-control-id');
return { id, element: frame };
})
);
const foundControlFrame = framesWithIds.find(({ id }) => id === controlId);
if (!foundControlFrame) throw new Error(errorText);
controlElement = foundControlFrame.element;
});
if (!controlElement) throw new Error(errorText);
return controlElement;
}
public async createControl({
controlType,
dataViewTitle,
fieldName,
grow,
title,
width,
additionalSettings,
}: {
controlType: string;
title?: string;
fieldName: string;
width?: ControlWidth;
dataViewTitle?: string;
grow?: boolean;
additionalSettings?: OptionsListAdditionalSettings;
}) {
this.log.debug(`Creating ${controlType} control ${title ?? fieldName}`);
await this.openCreateControlFlyout();
if (dataViewTitle) await this.controlsEditorSetDataView(dataViewTitle);
if (fieldName) await this.controlsEditorSetfield(fieldName, controlType);
if (title) await this.controlEditorSetTitle(title);
if (width) await this.controlEditorSetWidth(width);
if (grow !== undefined) await this.controlEditorSetGrow(grow);
if (additionalSettings) {
if (controlType === OPTIONS_LIST_CONTROL) {
// only options lists currently have additional settings
await this.optionsListSetAdditionalSettings(additionalSettings);
}
}
await this.controlEditorSave();
}
public async createTimeSliderControl() {
this.log.debug(`Creating time slider control`);
await this.openControlsMenu();
await this.testSubjects.click('controls-create-timeslider-button');
}
public async hoverOverExistingControl(controlId: string) {
const elementToHover = await this.getControlElementById(controlId);
await this.retry.try(async () => {
await elementToHover.moveMouseTo();
await this.testSubjects.existOrFail(`control-action-${controlId}-delete`);
});
}
public async clickExistingControl(controlId: string) {
const elementToClick = await this.getControlElementById(controlId);
await this.retry.try(async () => {
await elementToClick.click();
await this.testSubjects.existOrFail(`control-action-${controlId}-delete`);
});
}
public async editExistingControl(controlId: string) {
this.log.debug(`Opening control editor for control: ${controlId}`);
await this.hoverOverExistingControl(controlId);
await this.testSubjects.click(`control-action-${controlId}-edit`);
}
public async removeExistingControl(controlId: string) {
this.log.debug(`Removing control: ${controlId}`);
await this.hoverOverExistingControl(controlId);
await this.testSubjects.click(`control-action-${controlId}-delete`);
await this.common.clickConfirmOnModal();
}
public async clearControlSelections(controlId: string) {
this.log.debug(`clearing all selections from control ${controlId}`);
await this.hoverOverExistingControl(controlId);
await this.testSubjects.click(`control-action-${controlId}-erase`);
}
public async verifyControlType(controlId: string, expectedType: string) {
let controlButton;
switch (expectedType) {
case OPTIONS_LIST_CONTROL: {
controlButton = await this.find.byXPath(`//div[@id='controlFrame--${controlId}']//button`);
break;
}
case RANGE_SLIDER_CONTROL: {
controlButton = await this.find.byXPath(
`//div[@id='controlFrame--${controlId}']//div[contains(@class, 'rangeSliderAnchor__button')]`
);
break;
}
default: {
this.log.error('An invalid control type was provided.');
break;
}
}
if (controlButton) {
const testSubj = await controlButton.getAttribute('data-test-subj');
expect(testSubj).to.equal(`${expectedType}-${controlId}`);
}
}
// Options list functions
public async optionsListSetAdditionalSettings({
ignoreTimeout,
allowMultiple,
searchTechnique,
}: OptionsListAdditionalSettings) {
const getSettingTestSubject = (setting: string) =>
`optionsListControl__${setting}AdditionalSetting`;
if (allowMultiple) await this.testSubjects.click(getSettingTestSubject('allowMultiple'));
if (ignoreTimeout) await this.testSubjects.click(getSettingTestSubject('runPastTimeout'));
if (searchTechnique) {
this.log.debug(`clicking search technique: ${searchTechnique}`);
await this.find.clickByCssSelector(
`[data-test-subj=${getSettingTestSubject(
`${searchTechnique}SearchOption`
)}] label[for="${searchTechnique}"]`
);
}
}
public async optionsListGetSelectionsString(controlId: string) {
this.log.debug(`Getting selections string for Options List: ${controlId}`);
await this.optionsListWaitForLoading(controlId);
const controlElement = await this.getControlElementById(controlId);
return (await controlElement.getVisibleText()).split('\n')[1];
}
public async isOptionsListPopoverOpen(controlId: string) {
const isPopoverOpen = await this.find.existsByCssSelector(`#control-popover-${controlId}`);
this.log.debug(`Is popover open: ${isPopoverOpen} for Options List: ${controlId}`);
return isPopoverOpen;
}
public async optionsListOpenPopover(controlId: string) {
this.log.debug(`Opening popover for Options List: ${controlId}`);
await this.testSubjects.click(`optionsList-control-${controlId}`);
await this.retry.try(async () => {
await this.testSubjects.existOrFail(`optionsList-control-popover`);
});
}
public async optionsListEnsurePopoverIsClosed(controlId: string) {
this.log.debug(`Ensure popover is closed for Options List: ${controlId}`);
await this.retry.try(async () => {
const isPopoverOpen = await this.isOptionsListPopoverOpen(controlId);
if (isPopoverOpen) {
await this.testSubjects.click(`optionsList-control-${controlId}`);
await this.testSubjects.waitForDeleted(`optionsList-control-available-options`);
}
});
}
public async optionsListPopoverAssertOpen() {
await this.retry.try(async () => {
if (!(await this.testSubjects.exists(`optionsList-control-available-options`))) {
throw new Error('options list popover must be open before calling selectOption');
}
});
}
public async optionsListPopoverGetAvailableOptionsCount() {
this.log.debug(`getting available options count from options list`);
await this.optionsListPopoverWaitForLoading();
const availableOptions = await this.testSubjects.find(`optionsList-control-available-options`);
return +(await availableOptions.getAttribute('data-option-count'));
}
public async optionsListPopoverGetAvailableOptions() {
this.log.debug(`getting available options from options list`);
await this.optionsListPopoverWaitForLoading();
const availableOptions = await this.testSubjects.find(`optionsList-control-available-options`);
const optionsCount = await this.optionsListPopoverGetAvailableOptionsCount();
const selectableListItems = await availableOptions.findByClassName('euiSelectableList__list');
const suggestions: { [key: string]: number } = {};
while (Object.keys(suggestions).length < optionsCount) {
await selectableListItems._webElement.sendKeys(this.browser.keys.ARROW_DOWN);
const currentOption = await selectableListItems.findByCssSelector('[aria-selected="true"]');
const [suggestion, docCount] = (await currentOption.getVisibleText()).split('\n');
if (suggestion !== 'Exists') {
suggestions[suggestion] = Number(docCount);
}
}
const invalidSelectionElements = await availableOptions.findAllByClassName(
'optionsList__selectionInvalid'
);
const invalidSelections = await Promise.all(
invalidSelectionElements.map(async (option) => {
return await option.getVisibleText();
})
);
return { suggestions, invalidSelections };
}
public async ensureAvailableOptionsEqual(
controlId: string,
expectation: { suggestions: { [key: string]: number }; invalidSelections: string[] },
skipOpen?: boolean
) {
await this.optionsListWaitForLoading(controlId);
if (!skipOpen) await this.optionsListOpenPopover(controlId);
await this.retry.try(async () => {
expect(await this.optionsListPopoverGetAvailableOptions()).to.eql(expectation);
});
if (await this.testSubjects.exists('optionsList-cardinality-label')) {
expect(await this.optionsListGetCardinalityValue()).to.be(
Object.keys(expectation.suggestions).length.toLocaleString()
);
}
if (!skipOpen) await this.optionsListEnsurePopoverIsClosed(controlId);
}
public async optionsListGetCardinalityValue() {
this.log.debug(`getting the value of the cardinality badge`);
const cardinalityLabel = await (
await this.testSubjects.find('optionsList-cardinality-label')
).getVisibleText();
return cardinalityLabel.split(' ')[0];
}
public async optionsListPopoverSearchForOption(search: string) {
this.log.debug(`searching for ${search} in options list`);
await this.optionsListPopoverAssertOpen();
await this.testSubjects.setValue(`optionsList-control-search-input`, search);
await this.optionsListPopoverWaitForLoading();
}
public async optionsListPopoverClearSearch() {
this.log.debug(`clearing search from options list`);
await this.optionsListPopoverAssertOpen();
await this.find.clickByCssSelector('.euiFormControlLayoutClearButton');
await this.optionsListPopoverWaitForLoading();
}
public async optionsListPopoverSetSort(sort: OptionsListSortingType) {
this.log.debug(`select sorting type for suggestions`);
await this.optionsListPopoverAssertOpen();
await this.testSubjects.click('optionsListControl__sortingOptionsButton');
await this.retry.try(async () => {
await this.testSubjects.existOrFail('optionsListControl__sortingOptionsPopover');
});
await this.testSubjects.click(`optionsList__sortOrder_${sort.direction}`);
await this.testSubjects.click(`optionsList__sortBy_${sort.by}`);
await this.testSubjects.click('optionsListControl__sortingOptionsButton');
await this.retry.try(async () => {
await this.testSubjects.missingOrFail(`optionsListControl__sortingOptionsPopover`);
});
await this.optionsListPopoverWaitForLoading();
}
public async optionsListPopoverSelectExists() {
await this.retry.try(async () => {
await this.testSubjects.existOrFail(`optionsList-control-selection-exists`);
await this.testSubjects.click(`optionsList-control-selection-exists`);
});
}
public async optionsListPopoverSelectOption(availableOption: string) {
this.log.debug(`selecting ${availableOption} from options list`);
await this.optionsListPopoverSearchForOption(availableOption);
await this.retry.try(async () => {
await this.testSubjects.existOrFail(`optionsList-control-selection-${availableOption}`);
await this.testSubjects.click(`optionsList-control-selection-${availableOption}`);
});
await this.optionsListPopoverClearSearch();
}
public async optionsListPopoverSetIncludeSelections(include: boolean) {
this.log.debug(`exclude selections`);
await this.optionsListPopoverAssertOpen();
const buttonGroup = await this.testSubjects.find('optionsList__includeExcludeButtonGroup');
await (
await this.find.descendantDisplayedByCssSelector(
include ? '[data-text="Include"]' : '[data-text="Exclude"]',
buttonGroup
)
).click();
}
public async optionsListWaitForLoading(controlId: string) {
this.log.debug(`wait for ${controlId} to load`);
const enabled = await this.testSubjects.waitForEnabled(`optionsList-control-${controlId}`);
if (!enabled) {
throw new Error(`${controlId} did not finish loading within the given time limit`);
}
}
public async optionsListPopoverWaitForLoading() {
this.log.debug(`wait for the suggestions in the popover to load`);
await this.optionsListPopoverAssertOpen();
await this.testSubjects.waitForDeleted('optionsList-control-popover-loading');
}
/* -----------------------------------------------------------
Control editor flyout
----------------------------------------------------------- */
// Generic control editor functions
public async controlEditorSetTitle(title: string) {
this.log.debug(`Setting control title to ${title}`);
await this.testSubjects.setValue('control-editor-title-input', title);
}
public async controlEditorSetWidth(width: ControlWidth) {
this.log.debug(`Setting control width to ${width}`);
await this.testSubjects.click(`control-editor-width-${width}`);
}
public async controlEditorSetGrow(grow: boolean) {
this.log.debug(`Setting control grow to ${grow}`);
const growSwitch = await this.testSubjects.find('control-editor-grow-switch');
if ((await growSwitch.getAttribute('aria-checked')) !== `'${grow}'`) {
await growSwitch.click();
}
}
public async controlEditorSave() {
this.log.debug(`Saving changes in control editor`);
await this.testSubjects.click(`control-editor-save`);
await this.retry.waitFor('flyout to close', async () => {
return !(await this.testSubjects.exists('control-editor-flyout'));
});
}
public async controlEditorCancel() {
this.log.debug(`Canceling changes in control editor`);
await this.testSubjects.click(`control-editor-cancel`);
if (await this.testSubjects.exists('confirmModalTitleText')) {
await this.common.clickConfirmOnModal();
}
}
public async controlsEditorSetDataView(dataViewTitle: string) {
this.log.debug(`Setting control data view to ${dataViewTitle}`);
await this.testSubjects.click('open-data-view-picker');
await this.retry.try(async () => {
await this.testSubjects.existOrFail('data-view-picker-popover');
});
await this.testSubjects.click(`data-view-picker-${dataViewTitle}`);
}
public async controlsEditorSetfield(
fieldName: string,
expectedType?: string,
shouldSearch: boolean = true
) {
this.log.debug(`Setting control field to ${fieldName}`);
if (shouldSearch) {
await this.testSubjects.setValue('field-search-input', fieldName);
}
await this.retry.try(async () => {
await this.testSubjects.existOrFail(`field-picker-select-${fieldName}`);
});
await this.testSubjects.click(`field-picker-select-${fieldName}`);
if (expectedType) await this.controlEditorVerifyType(expectedType);
}
public async controlEditorVerifyType(type: string) {
this.log.debug(`Verifying that the control editor picked the type ${type}`);
const autoSelectedType = await this.testSubjects.getVisibleText('control-editor-type');
expect(autoSelectedType).to.equal(CONTROL_DISPLAY_NAMES[type]);
}
// Options List editor functions
public async optionsListEditorGetCurrentDataView(openAndCloseFlyout?: boolean) {
if (openAndCloseFlyout) {
await this.openCreateControlFlyout();
}
const dataViewName = (await this.testSubjects.find('open-data-view-picker')).getVisibleText();
if (openAndCloseFlyout) {
await this.controlEditorCancel();
}
return dataViewName;
}
// Range slider functions
public async rangeSliderGetLowerBoundAttribute(controlId: string, attribute: string) {
this.log.debug(`Getting range slider lower bound ${attribute} for ${controlId}`);
return await this.testSubjects.getAttribute(
`range-slider-control-${controlId} > rangeSlider__lowerBoundFieldNumber`,
attribute
);
}
public async rangeSliderGetUpperBoundAttribute(controlId: string, attribute: string) {
this.log.debug(`Getting range slider upper bound ${attribute} for ${controlId}`);
return await this.testSubjects.getAttribute(
`range-slider-control-${controlId} > rangeSlider__upperBoundFieldNumber`,
attribute
);
}
public async rangeSliderSetLowerBound(controlId: string, value: string) {
this.log.debug(`Setting range slider lower bound to ${value}`);
await this.retry.try(async () => {
await this.testSubjects.setValue(
`range-slider-control-${controlId} > rangeSlider__lowerBoundFieldNumber`,
value
);
expect(await this.rangeSliderGetLowerBoundAttribute(controlId, 'value')).to.be(value);
});
}
public async rangeSliderSetUpperBound(controlId: string, value: string) {
this.log.debug(`Setting range slider lower bound to ${value}`);
await this.retry.try(async () => {
await this.testSubjects.setValue(
`range-slider-control-${controlId} > rangeSlider__upperBoundFieldNumber`,
value
);
expect(await this.rangeSliderGetUpperBoundAttribute(controlId, 'value')).to.be(value);
});
}
public async rangeSliderOpenPopover(controlId: string) {
this.log.debug(`Opening popover for Range Slider: ${controlId}`);
// EuiDualRange only opens the popover when one of the number **inputs** is clicked - it does not open when
// the delimiter is clicked, so need to ensure we're clicking an input
await this.testSubjects.click(
`range-slider-control-${controlId} > rangeSlider__lowerBoundFieldNumber`
);
await this.retry.try(async () => {
await this.testSubjects.existOrFail(`rangeSlider__slider`);
});
}
public async rangeSliderEnsurePopoverIsClosed(controlId: string) {
this.log.debug(`Closing popover for Range Slider: ${controlId}`);
const controlLabel = await this.find.byXPath(`//div[@data-control-id='${controlId}']//label`);
await controlLabel.click();
await this.testSubjects.waitForDeleted(`rangeSlider__slider`);
}
public async rangeSliderPopoverAssertOpen() {
await this.retry.try(async () => {
if (!(await this.testSubjects.exists(`rangeSlider__slider`))) {
throw new Error('range slider popover must be open before calling selectOption');
}
});
}
public async rangeSliderWaitForLoading(controlId: string) {
await this.find.waitForDeletedByCssSelector(
`[data-test-subj="range-slider-control-${controlId}"] .euiLoadingSpinner`
);
}
public async validateRange(
compare: 'value' | 'placeholder', // if 'value', compare actual selections; otherwise, compare the default range
controlId: string,
expectedLowerBound: string,
expectedUpperBound: string
) {
expect(await this.rangeSliderGetLowerBoundAttribute(controlId, compare)).to.be(
expectedLowerBound
);
expect(await this.rangeSliderGetUpperBoundAttribute(controlId, compare)).to.be(
expectedUpperBound
);
}
// Time slider functions
public async gotoNextTimeSlice() {
await this.closeTimeSliderPopover(); // prevents the pin tooltip from getting in the way
await this.testSubjects.click('timeSlider-nextTimeWindow');
}
public async closeTimeSliderPopover() {
const isOpen = await this.testSubjects.exists('timeSlider-popoverContents');
if (isOpen) {
await this.testSubjects.click('timeSlider-popoverToggleButton');
}
}
public async getTimeSliceFromTimeSlider() {
const isOpen = await this.testSubjects.exists('timeSlider-popoverContents');
if (!isOpen) {
await this.testSubjects.click('timeSlider-popoverToggleButton');
await this.retry.try(async () => {
await this.testSubjects.existOrFail('timeSlider-popoverContents');
});
}
const popover = await this.testSubjects.find('timeSlider-popoverContents');
const dualRangeSlider = await this.find.descendantDisplayedByCssSelector(
'.euiRangeDraggable',
popover
);
const value = await dualRangeSlider.getAttribute('aria-valuetext');
return value;
}
}