mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -04:00
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:  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)  ### 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>
751 lines
29 KiB
TypeScript
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;
|
|
}
|
|
}
|