mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -04:00
Fixes https://github.com/elastic/kibana/issues/25892 This should address the flakiness observed in this issue by retrying if there is a stale element in the complex `PageObjects.visualize.filterOnTableCell()` method, and using `testSubjects.getVisibleText()` rather than calling `getVisibleText()` directly on the element without retrying.
1262 lines
46 KiB
JavaScript
1262 lines
46 KiB
JavaScript
/*
|
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
|
* license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright
|
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
|
* the Apache License, Version 2.0 (the "License"); you may
|
|
* not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
import { VisualizeConstants } from '../../../src/core_plugins/kibana/public/visualize/visualize_constants';
|
|
import Bluebird from 'bluebird';
|
|
import expect from 'expect.js';
|
|
import Keys from 'leadfoot/keys';
|
|
|
|
export function VisualizePageProvider({ getService, getPageObjects }) {
|
|
const remote = getService('remote');
|
|
const config = getService('config');
|
|
const testSubjects = getService('testSubjects');
|
|
const retry = getService('retry');
|
|
const find = getService('find');
|
|
const log = getService('log');
|
|
const flyout = getService('flyout');
|
|
const renderable = getService('renderable');
|
|
const table = getService('table');
|
|
const PageObjects = getPageObjects(['common', 'header']);
|
|
const defaultFindTimeout = config.get('timeouts.find');
|
|
|
|
class VisualizePage {
|
|
|
|
get index() {
|
|
return {
|
|
LOGSTASH_TIME_BASED: 'logstash-*',
|
|
LOGSTASH_NON_TIME_BASED: 'logstash*'
|
|
};
|
|
}
|
|
|
|
async navigateToNewVisualization() {
|
|
log.debug('navigateToApp visualize new');
|
|
await PageObjects.common.navigateToUrl('visualize', 'new');
|
|
await this.waitForVisualizationSelectPage();
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async waitForVisualizationSelectPage() {
|
|
await retry.try(async () => {
|
|
const visualizeSelectTypePage = await testSubjects.find('visualizeSelectTypePage');
|
|
if (!visualizeSelectTypePage.isDisplayed()) {
|
|
throw new Error('wait for visualization select page');
|
|
}
|
|
});
|
|
}
|
|
|
|
async clickVisType(type) {
|
|
await testSubjects.click(`visType-${type}`);
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickAreaChart() {
|
|
await find.clickByPartialLinkText('Area');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickDataTable() {
|
|
await find.clickByPartialLinkText('Data Table');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickLineChart() {
|
|
await find.clickByPartialLinkText('Line');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickRegionMap() {
|
|
await find.clickByPartialLinkText('Region Map');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickMarkdownWidget() {
|
|
await find.clickByPartialLinkText('Markdown');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickAddMetric() {
|
|
await find.clickByCssSelector('[group-name="metrics"] [data-test-subj="visualizeEditorAddAggregationButton"]');
|
|
}
|
|
|
|
async clickAddBucket() {
|
|
await find.clickByCssSelector('[group-name="buckets"] [data-test-subj="visualizeEditorAddAggregationButton"]');
|
|
}
|
|
|
|
async clickMetric() {
|
|
await find.clickByPartialLinkText('Metric');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickGauge() {
|
|
await find.clickByPartialLinkText('Gauge');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickPieChart() {
|
|
await find.clickByPartialLinkText('Pie');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickTileMap() {
|
|
await find.clickByPartialLinkText('Coordinate Map');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickTagCloud() {
|
|
await find.clickByPartialLinkText('Tag Cloud');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickVega() {
|
|
await find.clickByPartialLinkText('Vega');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickVisualBuilder() {
|
|
await find.clickByPartialLinkText('Visual Builder');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickEditorSidebarCollapse() {
|
|
await testSubjects.click('collapseSideBarButton');
|
|
}
|
|
|
|
async selectTagCloudTag(tagDisplayText) {
|
|
await testSubjects.click(tagDisplayText);
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async getTextTag() {
|
|
const elements = await find.allByCssSelector('text');
|
|
return await Promise.all(elements.map(async element => await element.getVisibleText()));
|
|
}
|
|
|
|
async getTextSizes() {
|
|
const tags = await find.allByCssSelector('text');
|
|
async function returnTagSize(tag) {
|
|
const style = await tag.getAttribute('style');
|
|
return style.match(/font-size: ([^;]*);/)[1];
|
|
}
|
|
return await Promise.all(tags.map(returnTagSize));
|
|
}
|
|
|
|
async clickVerticalBarChart() {
|
|
await find.clickByPartialLinkText('Vertical Bar');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickHeatmapChart() {
|
|
await find.clickByPartialLinkText('Heat Map');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickInputControlVis() {
|
|
await find.clickByPartialLinkText('Controls');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async getChartTypeCount() {
|
|
const tags = await find.allByCssSelector('a.wizard-vis-type');
|
|
return tags.length;
|
|
}
|
|
|
|
async getChartTypes() {
|
|
const chartTypes = await testSubjects.findAll('visualizeWizardChartTypeTitle');
|
|
async function getChartType(chart) {
|
|
return await chart.getVisibleText();
|
|
}
|
|
const getChartTypesPromises = chartTypes.map(getChartType);
|
|
return await Promise.all(getChartTypesPromises);
|
|
}
|
|
|
|
async selectVisSourceIfRequired() {
|
|
log.debug('selectVisSourceIfRequired');
|
|
const selectPage = await testSubjects.findAll('visualizeSelectSearch');
|
|
if (selectPage.length) {
|
|
log.debug('a search is required for this visualization');
|
|
await this.clickNewSearch();
|
|
}
|
|
}
|
|
|
|
async getExperimentalTypeLinks() {
|
|
return await remote.findAllByPartialLinkText('(Experimental)');
|
|
}
|
|
|
|
async isExperimentalInfoShown() {
|
|
return await testSubjects.exists('experimentalVisInfo');
|
|
}
|
|
|
|
async getExperimentalInfo() {
|
|
return await testSubjects.find('experimentalVisInfo');
|
|
}
|
|
|
|
async clickAbsoluteButton() {
|
|
await find.clickByCssSelector(
|
|
'ul.nav.nav-pills.nav-stacked.kbn-timepicker-modes:contains("absolute")',
|
|
defaultFindTimeout * 2);
|
|
}
|
|
|
|
async setMarkdownTxt(markdownTxt) {
|
|
const input = await testSubjects.find('markdownTextarea');
|
|
await input.clearValue();
|
|
await input.type(markdownTxt);
|
|
}
|
|
|
|
async getMarkdownText() {
|
|
const markdownContainer = await testSubjects.find('markdownBody');
|
|
return markdownContainer.getVisibleText();
|
|
}
|
|
|
|
async getMarkdownBodyDescendentText(selector) {
|
|
const markdownContainer = await testSubjects.find('markdownBody');
|
|
const element = await find.descendantDisplayedByCssSelector(selector, markdownContainer);
|
|
return element.getVisibleText();
|
|
}
|
|
|
|
async getVegaSpec() {
|
|
// Adapted from console_page.js:getVisibleTextFromAceEditor(). Is there a common utilities file?
|
|
const editor = await testSubjects.find('vega-editor');
|
|
const lines = await editor.findAllByClassName('ace_line_group');
|
|
const linesText = await Bluebird.map(lines, l => l.getVisibleText());
|
|
return linesText.join('\n');
|
|
}
|
|
|
|
async getVegaViewContainer() {
|
|
return await find.byCssSelector('div.vgaVis__view');
|
|
}
|
|
|
|
async getVegaControlContainer() {
|
|
return await find.byCssSelector('div.vgaVis__controls');
|
|
}
|
|
|
|
async setFromTime(timeString) {
|
|
const input = await find.byCssSelector('input[ng-model="absolute.from"]', defaultFindTimeout * 2);
|
|
await input.clearValue();
|
|
await input.type(timeString);
|
|
}
|
|
|
|
async setToTime(timeString) {
|
|
const input = await find.byCssSelector('input[ng-model="absolute.to"]', defaultFindTimeout * 2);
|
|
await input.clearValue();
|
|
await input.type(timeString);
|
|
}
|
|
|
|
async addInputControl() {
|
|
await testSubjects.click('inputControlEditorAddBtn');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async inputControlSubmit() {
|
|
await testSubjects.click('inputControlSubmitBtn');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async inputControlClear() {
|
|
await testSubjects.click('inputControlClearBtn');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async checkCheckbox(selector) {
|
|
const element = await testSubjects.find(selector);
|
|
const isSelected = await element.isSelected();
|
|
if(!isSelected) {
|
|
log.debug(`checking checkbox ${selector}`);
|
|
await testSubjects.click(selector);
|
|
}
|
|
}
|
|
|
|
async uncheckCheckbox(selector) {
|
|
const element = await testSubjects.find(selector);
|
|
const isSelected = await element.isSelected();
|
|
if(isSelected) {
|
|
log.debug(`unchecking checkbox ${selector}`);
|
|
await testSubjects.click(selector);
|
|
}
|
|
}
|
|
|
|
async setSelectByOptionText(selectId, optionText) {
|
|
const options = await find.allByCssSelector(`#${selectId} > option`);
|
|
const optionsTextPromises = options.map(async (optionElement) => {
|
|
return await optionElement.getVisibleText();
|
|
});
|
|
const optionsText = await Promise.all(optionsTextPromises);
|
|
|
|
const optionIndex = optionsText.indexOf(optionText);
|
|
if (optionIndex === -1) {
|
|
throw new Error(`Unable to find option '${optionText}' in select ${selectId}. Available options: ${optionsText.join(',')}`);
|
|
}
|
|
await options[optionIndex].click();
|
|
}
|
|
|
|
async isInspectorButtonEnabled() {
|
|
const button = await testSubjects.find('openInspectorButton');
|
|
const ariaDisabled = await button.getAttribute('aria-disabled');
|
|
return ariaDisabled !== 'true';
|
|
}
|
|
|
|
async getSideEditorExists() {
|
|
return await find.existsByCssSelector('.collapsible-sidebar');
|
|
}
|
|
|
|
async openInspector() {
|
|
log.debug('Open Inspector');
|
|
const isOpen = await testSubjects.exists('inspectorPanel');
|
|
if (!isOpen) {
|
|
await retry.try(async () => {
|
|
await testSubjects.click('openInspectorButton');
|
|
await testSubjects.find('inspectorPanel');
|
|
});
|
|
}
|
|
}
|
|
|
|
async closeInspector() {
|
|
log.debug('Close Inspector');
|
|
let isOpen = await testSubjects.exists('inspectorPanel');
|
|
if (isOpen) {
|
|
await retry.try(async () => {
|
|
await flyout.close('inspectorPanel');
|
|
isOpen = await testSubjects.exists('inspectorPanel');
|
|
if (isOpen) {
|
|
throw new Error('Failed to close inspector');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
async setInspectorTablePageSize(size) {
|
|
const panel = await testSubjects.find('inspectorPanel');
|
|
await find.clickByButtonText('Rows per page: 20', panel);
|
|
// The buttons for setting table page size are in a popover element. This popover
|
|
// element appears as if it's part of the inspectorPanel but it's really attached
|
|
// to the body element by a portal.
|
|
const tableSizesPopover = await find.byCssSelector('.euiPanel');
|
|
await find.clickByButtonText(`${size} rows`, tableSizesPopover);
|
|
}
|
|
|
|
async getMetric() {
|
|
const metricElement = await find.byCssSelector('div[ng-controller="KbnMetricVisController"]');
|
|
return await metricElement.getVisibleText();
|
|
}
|
|
|
|
async getGaugeValue() {
|
|
const elements = await find.allByCssSelector('[data-test-subj="visualizationLoader"] .chart svg');
|
|
return await Promise.all(elements.map(async element => await element.getVisibleText()));
|
|
}
|
|
|
|
async clickMetricEditor() {
|
|
await find.clickByCssSelector('button[data-test-subj="toggleEditor"]');
|
|
}
|
|
|
|
async clickNewSearch(indexPattern = this.index.LOGSTASH_TIME_BASED) {
|
|
await testSubjects.click(`paginatedListItem-${indexPattern}`);
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickSavedSearch(savedSearchName) {
|
|
await find.clickByPartialLinkText(savedSearchName);
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickUnlinkSavedSearch() {
|
|
await testSubjects.doubleClick('unlinkSavedSearch');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async setValue(newValue) {
|
|
await find.clickByCssSelector('button[ng-click="numberListCntr.add()"]', defaultFindTimeout * 2);
|
|
const input = await find.byCssSelector('input[ng-model="numberListCntr.getList()[$index]"]');
|
|
await input.clearValue();
|
|
await input.type(newValue);
|
|
}
|
|
|
|
async selectSearch(searchName) {
|
|
await find.clickByLinkText(searchName);
|
|
}
|
|
|
|
async getErrorMessage() {
|
|
const element = await find.byCssSelector('.item>h4');
|
|
return await element.getVisibleText();
|
|
}
|
|
|
|
// clickBucket(bucketType) 'X-Axis', 'Split Area', 'Split Chart'
|
|
async clickBucket(bucketName, type = 'bucket') {
|
|
const testSubject = type === 'bucket' ? 'bucketsAggGroup' : 'metricsAggGroup';
|
|
await retry.try(async () => {
|
|
const chartTypes = await retry.try(
|
|
async () => await find.allByCssSelector(`[data-test-subj="${testSubject}"] .list-group-menu-item`));
|
|
log.debug('found bucket types ' + chartTypes.length);
|
|
|
|
async function getChartType(chart) {
|
|
const chartString = await chart.getVisibleText();
|
|
if (chartString === bucketName) {
|
|
await chart.click();
|
|
return true;
|
|
}
|
|
}
|
|
const getChartTypesPromises = chartTypes.map(getChartType);
|
|
const clickResult = await Promise.all(getChartTypesPromises);
|
|
if (!clickResult.some(result => result === true)) {
|
|
throw new Error(`bucket ${bucketName} not found`);
|
|
}
|
|
});
|
|
}
|
|
|
|
async selectAggregation(myString, groupName = 'buckets', childAggregationType = null) {
|
|
const selector = `
|
|
[group-name="${groupName}"]
|
|
vis-editor-agg-params:not(.ng-hide)
|
|
${childAggregationType ? `vis-editor-agg-params[group-name="'${childAggregationType}'"]:not(.ng-hide)` : ''}
|
|
.agg-select
|
|
`;
|
|
|
|
await retry.try(async () => {
|
|
await find.clickByCssSelector(selector);
|
|
const input = await find.byCssSelector(`${selector} input.ui-select-search`);
|
|
await input.type(myString);
|
|
await remote.pressKeys('\uE006');
|
|
});
|
|
await PageObjects.common.sleep(500);
|
|
}
|
|
|
|
/**
|
|
* Set the test for a filter aggregation.
|
|
* @param {*} filterValue the string value of the filter
|
|
* @param {*} filterIndex used when multiple filters are configured on the same aggregation
|
|
* @param {*} aggregationId the ID if the aggregation. On Tests, it start at from 2
|
|
*/
|
|
async setFilterAggregationValue(filterValue, filterIndex = 0, aggregationId = 2) {
|
|
const inputField = await testSubjects.find(`visEditorFilterInput_${aggregationId}_${filterIndex}`);
|
|
await inputField.type(filterValue);
|
|
}
|
|
|
|
async addNewFilterAggregation() {
|
|
return await testSubjects.click('visEditorAddFilterButton');
|
|
}
|
|
|
|
async toggleOpenEditor(index, toState = 'true') {
|
|
// index, see selectYAxisAggregation
|
|
const toggle = await find.byCssSelector(`button[aria-controls="visAggEditorParams${index}"]`);
|
|
const toggleOpen = await toggle.getAttribute('aria-expanded');
|
|
log.debug(`toggle ${index} expand = ${toggleOpen}`);
|
|
if (toggleOpen !== toState) {
|
|
log.debug(`toggle ${index} click()`);
|
|
await toggle.click();
|
|
}
|
|
}
|
|
|
|
async selectYAxisAggregation(agg, field, label, index = 1) {
|
|
// index starts on the first "count" metric at 1
|
|
// Each new metric or aggregation added to a visualization gets the next index.
|
|
// So to modify a metric or aggregation tests need to keep track of the
|
|
// order they are added.
|
|
await this.toggleOpenEditor(index);
|
|
const aggSelect = await find
|
|
.byCssSelector(`#visAggEditorParams${index} div [data-test-subj="visEditorAggSelect"] div span[aria-label="Select box activate"]`);
|
|
// open agg selection list
|
|
await aggSelect.click();
|
|
// select our agg
|
|
const aggItem = await find.byCssSelector(`[data-test-subj="${agg}"]`);
|
|
await aggItem.click();
|
|
const fieldSelect = await find
|
|
.byCssSelector(`#visAggEditorParams${index} > [agg-param="agg.type.params[0]"] > div > div > div.ui-select-match > span`);
|
|
// open field selection list
|
|
await fieldSelect.click();
|
|
// select our field
|
|
await testSubjects.click(field);
|
|
// enter custom label
|
|
await this.setCustomLabel(label, index);
|
|
}
|
|
|
|
async setCustomLabel(label, index = 1) {
|
|
const customLabel = await find.byCssSelector(`#visEditorStringInput${index}customLabel`);
|
|
customLabel.type(label);
|
|
}
|
|
|
|
async setAxisExtents(min, max, axis = 'LeftAxis-1') {
|
|
const axisOptions = await find.byCssSelector(`div[aria-label="Toggle ${axis} options"]`);
|
|
const isOpen = await axisOptions.getAttribute('aria-expanded');
|
|
if (isOpen === 'false') {
|
|
log.debug(`click to open ${axis} options`);
|
|
await axisOptions.click();
|
|
}
|
|
// it would be nice to get the correct axis by name like "LeftAxis-1"
|
|
// instead of an incremented index, but this link isn't under the div above
|
|
const advancedLink =
|
|
await find.byCssSelector(`#axisOptionsValueAxis-1 .kuiSideBarOptionsLink .kuiSideBarOptionsLink__caret`);
|
|
|
|
const advancedLinkState = await advancedLink.getAttribute('class');
|
|
if (advancedLinkState.includes('fa-caret-right')) {
|
|
await advancedLink.session.moveMouseTo(advancedLink);
|
|
log.debug('click advancedLink');
|
|
await advancedLink.click();
|
|
}
|
|
const checkbox = await find.byCssSelector('input[ng-model="axis.scale.setYExtents"]');
|
|
const checkboxState = await checkbox.getAttribute('class');
|
|
if (checkboxState.includes('ng-empty')) {
|
|
await checkbox.session.moveMouseTo(checkbox);
|
|
await checkbox.click();
|
|
}
|
|
const maxField = await find.byCssSelector('[ng-model="axis.scale.max"]');
|
|
await maxField.type(max);
|
|
const minField = await find.byCssSelector('[ng-model="axis.scale.min"]');
|
|
await minField.type(min);
|
|
|
|
}
|
|
|
|
async getField() {
|
|
const field = await retry.try(
|
|
async () => await find.byCssSelector('.ng-valid-required[name="field"] .ui-select-match-text'));
|
|
return await field.getVisibleText();
|
|
}
|
|
|
|
async selectField(fieldValue, groupName = 'buckets', childAggregationType = null) {
|
|
const selector = `
|
|
[group-name="${groupName}"]
|
|
vis-editor-agg-params:not(.ng-hide)
|
|
${childAggregationType ? `vis-editor-agg-params[group-name="'${childAggregationType}'"]:not(.ng-hide)` : ''}
|
|
.field-select
|
|
`;
|
|
|
|
await retry.try(async () => {
|
|
await find.clickByCssSelector(selector);
|
|
const input = await find.byCssSelector(`${selector} input.ui-select-search`);
|
|
await input.type(fieldValue);
|
|
await remote.pressKeys('\uE006');
|
|
});
|
|
await PageObjects.common.sleep(500);
|
|
}
|
|
|
|
async selectFieldById(fieldValue, id) {
|
|
await find.clickByCssSelector(`#${id} > option[label="${fieldValue}"]`);
|
|
}
|
|
|
|
async orderBy(fieldValue) {
|
|
await find.clickByCssSelector(
|
|
'select.form-control.ng-pristine.ng-valid.ng-untouched.ng-valid-required[ng-model="agg.params.orderBy"]'
|
|
+ `option:contains("${fieldValue}")`);
|
|
}
|
|
|
|
async selectOrderBy(fieldValue) {
|
|
await find.clickByCssSelector(`select[name="orderBy"] > option[value="${fieldValue}"]`);
|
|
}
|
|
|
|
async getInputTypeParam(paramName) {
|
|
const input = await find.byCssSelector(`input[ng-model="agg.params.${paramName}"]`);
|
|
return await input.getProperty('value');
|
|
}
|
|
|
|
async getInterval() {
|
|
const select = await find.byCssSelector('select[ng-model="agg.params.interval"]');
|
|
const selectedIndex = await select.getProperty('selectedIndex');
|
|
const intervalElement = await find.byCssSelector(
|
|
`select[ng-model="agg.params.interval"] option:nth-child(${(selectedIndex + 1)})`);
|
|
return await intervalElement.getProperty('label');
|
|
}
|
|
|
|
async setInterval(newValue) {
|
|
const input = await find.byCssSelector('select[ng-model="agg.params.interval"]');
|
|
await input.type(newValue);
|
|
await remote.pressKeys(Keys.RETURN);
|
|
}
|
|
|
|
async setCustomInterval(newValue) {
|
|
await this.setInterval('Custom');
|
|
const input = await find.byCssSelector('input[name="customInterval"]');
|
|
await input.clearValue();
|
|
await input.type(newValue);
|
|
}
|
|
|
|
async setNumericInterval(newValue, { append } = {}) {
|
|
const input = await find.byCssSelector('input[name="interval"]');
|
|
if (!append) {
|
|
await input.clearValue();
|
|
}
|
|
await input.type(newValue + '');
|
|
await PageObjects.common.sleep(1000);
|
|
}
|
|
|
|
async setSize(newValue) {
|
|
const input = await find.byCssSelector(`vis-editor-agg-params[aria-hidden="false"] input[name="size"]`);
|
|
await input.clearValue();
|
|
await input.type(String(newValue));
|
|
}
|
|
|
|
async toggleDisabledAgg(agg) {
|
|
await testSubjects.click(`aggregationEditor${agg} disableAggregationBtn`);
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async toggleAggregationEditor(agg) {
|
|
await testSubjects.click(`aggregationEditor${agg} toggleEditor`);
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async toggleOtherBucket() {
|
|
return await find.clickByCssSelector('vis-editor-agg-params:not(.ng-hide) input[name="showOther"]');
|
|
}
|
|
|
|
async toggleMissingBucket() {
|
|
return await find.clickByCssSelector('vis-editor-agg-params:not(.ng-hide) input[name="showMissing"]');
|
|
}
|
|
|
|
async isApplyEnabled() {
|
|
const applyButton = await testSubjects.find('visualizeEditorRenderButton');
|
|
return await applyButton.isEnabled();
|
|
}
|
|
|
|
async clickGo() {
|
|
await testSubjects.click('visualizeEditorRenderButton');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
// For some reason there are two `data-render-complete` tags on each visualization in the visualize page.
|
|
const countOfDataCompleteFlags = 2;
|
|
await renderable.waitForRender(countOfDataCompleteFlags);
|
|
}
|
|
|
|
async clickReset() {
|
|
await testSubjects.click('visualizeEditorResetButton');
|
|
}
|
|
|
|
async toggleAutoMode() {
|
|
await testSubjects.click('visualizeEditorAutoButton');
|
|
}
|
|
|
|
async sizeUpEditor() {
|
|
await testSubjects.click('visualizeEditorResizer');
|
|
await remote.pressKeys(Keys.ARROW_RIGHT);
|
|
}
|
|
|
|
async clickOptions() {
|
|
await find.clickByPartialLinkText('Options');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async changeHeatmapColorNumbers(value = 6) {
|
|
const input = await testSubjects.find(`heatmapOptionsColorsNumberInput`);
|
|
await input.clearValue();
|
|
await input.type(`${value}`);
|
|
}
|
|
|
|
async clickMetricsAndAxes() {
|
|
await testSubjects.click('visEditorTabadvanced');
|
|
}
|
|
|
|
async clickOptionsTab() {
|
|
await testSubjects.click('visEditorTaboptions');
|
|
}
|
|
|
|
async clickEnableCustomRanges() {
|
|
await testSubjects.click('heatmapEnableCustomRanges');
|
|
}
|
|
|
|
async clickAddRange() {
|
|
await testSubjects.click(`heatmapAddRangeButton`);
|
|
}
|
|
|
|
async isCustomRangeTableShown() {
|
|
await testSubjects.exists('heatmapCustomRangesTable');
|
|
}
|
|
|
|
async addCustomRange(from, to) {
|
|
const table = await testSubjects.find('heatmapCustomRangesTable');
|
|
const lastRow = await table.findByCssSelector('tr:last-child');
|
|
const fromCell = await lastRow.findByCssSelector('td:first-child input');
|
|
fromCell.clearValue();
|
|
fromCell.type(`${from}`);
|
|
const toCell = await lastRow.findByCssSelector('td:nth-child(2) input');
|
|
toCell.clearValue();
|
|
toCell.type(`${to}`);
|
|
}
|
|
|
|
async clickYAxisOptions(axisId) {
|
|
await testSubjects.click(`toggleYAxisOptions-${axisId}`);
|
|
}
|
|
|
|
async clickYAxisAdvancedOptions(axisId) {
|
|
await testSubjects.click(`toggleYAxisAdvancedOptions-${axisId}`);
|
|
}
|
|
|
|
async changeYAxisFilterLabelsCheckbox(axisId, enabled) {
|
|
const selector = `yAxisFilterLabelsCheckbox-${axisId}`;
|
|
enabled ? await this.checkCheckbox(selector) : await this.uncheckCheckbox(selector);
|
|
}
|
|
|
|
async selectChartMode(mode) {
|
|
const selector = await find.byCssSelector(`#seriesMode0 > option[label="${mode}"]`);
|
|
await selector.click();
|
|
}
|
|
|
|
async selectYAxisScaleType(axisId, scaleType) {
|
|
const selectElement = await testSubjects.find(`scaleSelectYAxis-${axisId}`);
|
|
const selector = await selectElement.findByCssSelector(`option[label="${scaleType}"]`);
|
|
await selector.click();
|
|
}
|
|
|
|
async clickData() {
|
|
await testSubjects.click('visualizeEditDataLink');
|
|
}
|
|
|
|
async clickVisEditorTab(tabName) {
|
|
await testSubjects.click('visEditorTab' + tabName);
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async selectWMS() {
|
|
await find.clickByCssSelector('input[name="wms.enabled"]');
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async ensureSavePanelOpen() {
|
|
log.debug('ensureSavePanelOpen');
|
|
let isOpen = await testSubjects.exists('savedObjectSaveModal');
|
|
await retry.try(async () => {
|
|
while (!isOpen) {
|
|
await testSubjects.click('visualizeSaveButton');
|
|
isOpen = await testSubjects.exists('savedObjectSaveModal');
|
|
}
|
|
});
|
|
}
|
|
|
|
async saveVisualization(vizName, { saveAsNew = false } = {}) {
|
|
await this.ensureSavePanelOpen();
|
|
await testSubjects.setValue('savedObjectTitle', vizName);
|
|
if (saveAsNew) {
|
|
log.debug('Check save as new visualization');
|
|
await testSubjects.click('saveAsNewCheckbox');
|
|
}
|
|
log.debug('Click Save Visualization button');
|
|
await testSubjects.click('confirmSaveSavedObjectButton');
|
|
}
|
|
|
|
async saveVisualizationExpectSuccess(vizName, { saveAsNew = false } = {}) {
|
|
await this.saveVisualization(vizName, { saveAsNew });
|
|
const successToast = await testSubjects.exists('saveVisualizationSuccess', defaultFindTimeout);
|
|
expect(successToast).to.be(true);
|
|
}
|
|
|
|
async saveVisualizationExpectFail(vizName, { saveAsNew = false } = {}) {
|
|
await this.saveVisualization(vizName, { saveAsNew });
|
|
const errorToast = await testSubjects.exists('saveVisualizationError', defaultFindTimeout);
|
|
expect(errorToast).to.be(true);
|
|
}
|
|
|
|
async clickLoadSavedVisButton() {
|
|
// TODO: Use a test subject selector once we rewrite breadcrumbs to accept each breadcrumb
|
|
// element as a child instead of building the breadcrumbs dynamically.
|
|
await find.clickByCssSelector('[href="#/visualize"]');
|
|
}
|
|
|
|
async filterVisByName(vizName) {
|
|
const input = await find.byCssSelector('input[name="filter"]');
|
|
await input.click();
|
|
// can't uses dashes in saved visualizations when filtering
|
|
// or extended character sets
|
|
// https://github.com/elastic/kibana/issues/6300
|
|
await input.type(vizName.replace('-', ' '));
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickVisualizationByName(vizName) {
|
|
log.debug('clickVisualizationByLinkText(' + vizName + ')');
|
|
|
|
return retry.try(function tryingForTime() {
|
|
return remote
|
|
.setFindTimeout(defaultFindTimeout)
|
|
.findByPartialLinkText(vizName)
|
|
.click();
|
|
});
|
|
}
|
|
|
|
// this starts by clicking the Load Saved Viz button, not from the
|
|
// bottom half of the "Create a new visualization Step 1" page
|
|
async loadSavedVisualization(vizName) {
|
|
await this.clickLoadSavedVisButton();
|
|
await this.openSavedVisualization(vizName);
|
|
}
|
|
|
|
async openSavedVisualization(vizName) {
|
|
await this.clickVisualizationByName(vizName);
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async getXAxisLabels() {
|
|
const chartTypes = await find.allByCssSelector('.x > g');
|
|
async function getChartType(chart) {
|
|
return await chart.getVisibleText();
|
|
}
|
|
const getChartTypesPromises = chartTypes.map(getChartType);
|
|
return await Promise.all(getChartTypesPromises);
|
|
}
|
|
|
|
async getYAxisLabels() {
|
|
const chartTypes = await find.allByCssSelector('.y > g');
|
|
const getChartTypesPromises = chartTypes.map(async chart => await chart.getVisibleText());
|
|
return await Promise.all(getChartTypesPromises);
|
|
}
|
|
|
|
/*
|
|
** This method gets the chart data and scales it based on chart height and label.
|
|
** Returns an array of height values
|
|
*/
|
|
async getAreaChartData(dataLabel, axis = 'ValueAxis-1') {
|
|
const yAxisRatio = await this.getChartYAxisRatio(axis);
|
|
|
|
const rectangle = await find.byCssSelector('rect.background');
|
|
const yAxisHeight = await rectangle.getAttribute('height');
|
|
log.debug(`height --------- ${yAxisHeight}`);
|
|
|
|
const path = await retry.try(
|
|
async () => await find.byCssSelector(`path[data-label="${dataLabel}"]`, defaultFindTimeout * 2));
|
|
const data = await path.getAttribute('d');
|
|
log.debug(data);
|
|
// This area chart data starts with a 'M'ove to a x,y location, followed
|
|
// by a bunch of 'L'ines from that point to the next. Those points are
|
|
// the values we're going to use to calculate the data values we're testing.
|
|
// So git rid of the one 'M' and split the rest on the 'L's.
|
|
const tempArray = data.replace('M', '').split('L');
|
|
const chartSections = tempArray.length / 2;
|
|
// log.debug('chartSections = ' + chartSections + ' height = ' + yAxisHeight + ' yAxisLabel = ' + yAxisLabel);
|
|
const chartData = [];
|
|
for (let i = 0; i < chartSections; i++) {
|
|
chartData[i] = Math.round((yAxisHeight - tempArray[i].split(',')[1]) * yAxisRatio);
|
|
log.debug('chartData[i] =' + chartData[i]);
|
|
}
|
|
return chartData;
|
|
}
|
|
|
|
// The current test shows dots, not a line. This function gets the dots and normalizes their height.
|
|
async getLineChartData(dataLabel = 'Count', axis = 'ValueAxis-1') {
|
|
// 1). get the range/pixel ratio
|
|
const yAxisRatio = await this.getChartYAxisRatio(axis);
|
|
// 2). find and save the y-axis pixel size (the chart height)
|
|
const rectangle = await find.byCssSelector('clipPath rect');
|
|
const yAxisHeight = await rectangle.getAttribute('height');
|
|
// 3). get the visWrapper__chart elements
|
|
const chartTypes = await retry.try(
|
|
async () => await find
|
|
.allByCssSelector(`.visWrapper__chart circle[data-label="${dataLabel}"][fill-opacity="1"]`, defaultFindTimeout * 2));
|
|
|
|
// 5). for each chart element, find the green circle, then the cy position
|
|
async function getChartType(chart) {
|
|
const cy = await chart.getAttribute('cy');
|
|
// the point_series_options test has data in the billions range and
|
|
// getting 11 digits of precision with these calculations is very hard
|
|
return Math.round(((yAxisHeight - cy) * yAxisRatio).toPrecision(6));
|
|
}
|
|
|
|
// 4). pass the chartTypes to the getChartType function
|
|
const getChartTypesPromises = chartTypes.map(getChartType);
|
|
return await Promise.all(getChartTypesPromises);
|
|
}
|
|
|
|
// this is ALMOST identical to DiscoverPage.getBarChartData
|
|
async getBarChartData(dataLabel = 'Count', axis = 'ValueAxis-1') {
|
|
// 1). get the range/pixel ratio
|
|
const yAxisRatio = await this.getChartYAxisRatio(axis);
|
|
// 3). get the visWrapper__chart elements
|
|
const chartTypes = await find.allByCssSelector(`svg > g > g.series > rect[data-label="${dataLabel}"]`);
|
|
|
|
async function getChartType(chart) {
|
|
const barHeight = await chart.getAttribute('height');
|
|
return Math.round(barHeight * yAxisRatio);
|
|
}
|
|
const getChartTypesPromises = chartTypes.map(getChartType);
|
|
return await Promise.all(getChartTypesPromises);
|
|
}
|
|
|
|
|
|
// Returns value per pixel
|
|
async getChartYAxisRatio(axis = 'ValueAxis-1') {
|
|
// 1). get the maximum chart Y-Axis marker value and Y position
|
|
const maxYAxisChartMarker = await retry.try(
|
|
async () => await find.byCssSelector(`div.visAxis__splitAxes--y > div > svg > g.${axis} > g:last-of-type.tick`)
|
|
);
|
|
const maxYLabel = (await maxYAxisChartMarker.getVisibleText()).replace(/,/g, '');
|
|
const maxYLabelYPosition = (await maxYAxisChartMarker.getPosition()).y;
|
|
log.debug(`maxYLabel = ${maxYLabel}, maxYLabelYPosition = ${maxYLabelYPosition}`);
|
|
|
|
// 2). get the minimum chart Y-Axis marker value and Y position
|
|
const minYAxisChartMarker = await find.byCssSelector(
|
|
'div.visAxis__column--y.visAxis__column--left > div > div > svg:nth-child(2) > g > g:nth-child(1).tick'
|
|
);
|
|
const minYLabel = (await minYAxisChartMarker.getVisibleText()).replace(',', '');
|
|
const minYLabelYPosition = (await minYAxisChartMarker.getPosition()).y;
|
|
return ((maxYLabel - minYLabel) / (minYLabelYPosition - maxYLabelYPosition));
|
|
}
|
|
|
|
|
|
async getHeatmapData() {
|
|
const chartTypes = await retry.try(
|
|
async () => await find.allByCssSelector('svg > g > g.series rect', defaultFindTimeout * 2));
|
|
log.debug('rects=' + chartTypes);
|
|
async function getChartType(chart) {
|
|
return await chart.getAttribute('data-label');
|
|
}
|
|
const getChartTypesPromises = chartTypes.map(getChartType);
|
|
return await Promise.all(getChartTypesPromises);
|
|
}
|
|
|
|
async getPieChartData() {
|
|
const chartTypes = await find.allByCssSelector('path.slice', defaultFindTimeout * 2);
|
|
|
|
const getChartTypesPromises = chartTypes.map(async chart => await chart.getAttribute('d'));
|
|
return await Promise.all(getChartTypesPromises);
|
|
}
|
|
|
|
async getPieChartLabels() {
|
|
const chartTypes = await find.allByCssSelector('path.slice', defaultFindTimeout * 2);
|
|
|
|
const getChartTypesPromises = chartTypes.map(async chart => await chart.getAttribute('data-label'));
|
|
return await Promise.all(getChartTypesPromises);
|
|
}
|
|
async expectPieChartError() {
|
|
return await testSubjects.existOrFail('visLibVisualizeError');
|
|
}
|
|
|
|
async getChartAreaWidth() {
|
|
const rect = await retry.try(async () => find.byCssSelector('clipPath rect'));
|
|
return await rect.getAttribute('width');
|
|
}
|
|
|
|
async getChartAreaHeight() {
|
|
const rect = await retry.try(async () => find.byCssSelector('clipPath rect'));
|
|
return await rect.getAttribute('height');
|
|
}
|
|
|
|
/**
|
|
* If you are writing new tests, you should rather look into getTableVisContent method instead.
|
|
*/
|
|
async getTableVisData() {
|
|
return await testSubjects.getVisibleText('paginated-table-body');
|
|
}
|
|
|
|
/**
|
|
* This function is the newer function to retrieve data from within a table visualization.
|
|
* It uses a better return format, than the old getTableVisData, by properly splitting
|
|
* cell values into arrays. Please use this function for newer tests.
|
|
*/
|
|
async getTableVisContent({ stripEmptyRows = true } = { }) {
|
|
return await retry.try(async () => {
|
|
const container = await testSubjects.find('tableVis');
|
|
const allTables = await testSubjects.findAllDescendant('paginated-table-body', container);
|
|
|
|
if (allTables.length === 0) {
|
|
return [];
|
|
}
|
|
|
|
const allData = await Promise.all(allTables.map(async (t) => {
|
|
let data = await table.getDataFromElement(t);
|
|
if (stripEmptyRows) {
|
|
data = data.filter(row => row.length > 0 && row.some(cell => cell.trim().length > 0));
|
|
}
|
|
return data;
|
|
}));
|
|
|
|
if (allTables.length === 1) {
|
|
// If there was only one table we return only the data for that table
|
|
// This prevents an unnecessary array around that single table, which
|
|
// is the case we have in most tests.
|
|
return allData[0];
|
|
}
|
|
|
|
return allData;
|
|
});
|
|
}
|
|
|
|
async getInspectorTableData() {
|
|
// TODO: we should use datat-test-subj=inspectorTable as soon as EUI supports it
|
|
const inspectorPanel = await testSubjects.find('inspectorPanel');
|
|
const tableBody = await retry.try(async () => inspectorPanel.findByTagName('tbody'));
|
|
// Convert the data into a nested array format:
|
|
// [ [cell1_in_row1, cell2_in_row1], [cell1_in_row2, cell2_in_row2] ]
|
|
const rows = await tableBody.findAllByTagName('tr');
|
|
return await Promise.all(rows.map(async row => {
|
|
const cells = await row.findAllByTagName('td');
|
|
return await Promise.all(cells.map(async cell => cell.getVisibleText()));
|
|
}));
|
|
}
|
|
|
|
async getInspectorTableHeaders() {
|
|
// TODO: we should use datat-test-subj=inspectorTable as soon as EUI supports it
|
|
const dataTableHeader = await retry.try(async () => {
|
|
const inspectorPanel = await testSubjects.find('inspectorPanel');
|
|
return await inspectorPanel.findByTagName('thead');
|
|
});
|
|
const cells = await dataTableHeader.findAllByTagName('th');
|
|
return await Promise.all(cells.map(async (cell) => {
|
|
const untrimmed = await cell.getVisibleText();
|
|
return untrimmed.trim();
|
|
}));
|
|
}
|
|
|
|
async toggleIsFilteredByCollarCheckbox() {
|
|
await testSubjects.click('isFilteredByCollarCheckbox');
|
|
}
|
|
|
|
async getMarkdownData() {
|
|
const markdown = await retry.try(async () => find.byCssSelector('visualize'));
|
|
return await markdown.getVisibleText();
|
|
}
|
|
|
|
async clickColumns() {
|
|
await find.clickByCssSelector('div.schemaEditors > div > div > button:nth-child(2)');
|
|
}
|
|
|
|
async waitForVisualization() {
|
|
return await find.byCssSelector('.visualization');
|
|
}
|
|
|
|
async waitForVisualizationSavedToastGone() {
|
|
return await testSubjects.waitForDeleted('saveVisualizationSuccess');
|
|
}
|
|
|
|
async getZoomSelectors(zoomSelector) {
|
|
return await find.allByCssSelector(zoomSelector);
|
|
}
|
|
|
|
async clickMapButton(zoomSelector) {
|
|
await retry.try(async () => {
|
|
const zooms = await this.getZoomSelectors(zoomSelector);
|
|
await Promise.all(zooms.map(async zoom => await zoom.click()));
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
});
|
|
}
|
|
|
|
async getVisualizationRequest() {
|
|
log.debug('getVisualizationRequest');
|
|
await this.openInspector();
|
|
await testSubjects.click('inspectorViewChooser');
|
|
await testSubjects.click('inspectorViewChooserRequests');
|
|
await testSubjects.click('inspectorRequestDetailRequest');
|
|
return await testSubjects.getVisibleText('inspectorRequestBody');
|
|
}
|
|
|
|
async getVisualizationResponse() {
|
|
log.debug('getVisualizationResponse');
|
|
await this.openInspector();
|
|
await testSubjects.click('inspectorViewChooser');
|
|
await testSubjects.click('inspectorViewChooserRequests');
|
|
await testSubjects.click('inspectorRequestDetailResponse');
|
|
return await testSubjects.getVisibleText('inspectorResponseBody');
|
|
}
|
|
|
|
async getMapBounds() {
|
|
const request = await this.getVisualizationRequest();
|
|
const requestObject = JSON.parse(request);
|
|
return requestObject.aggs.filter_agg.filter.geo_bounding_box['geo.coordinates'];
|
|
}
|
|
|
|
async clickMapZoomIn() {
|
|
await this.clickMapButton('a.leaflet-control-zoom-in');
|
|
}
|
|
|
|
async clickMapZoomOut() {
|
|
await this.clickMapButton('a.leaflet-control-zoom-out');
|
|
}
|
|
|
|
async getMapZoomEnabled(zoomSelector) {
|
|
const zooms = await this.getZoomSelectors(zoomSelector);
|
|
const classAttributes = await Promise.all(zooms.map(async zoom => await zoom.getAttribute('class')));
|
|
return !classAttributes.join('').includes('leaflet-disabled');
|
|
}
|
|
|
|
async zoomAllTheWayOut() {
|
|
// we can tell we're at level 1 because zoom out is disabled
|
|
return await retry.try(async () => {
|
|
await this.clickMapZoomOut();
|
|
const enabled = await this.getMapZoomOutEnabled();
|
|
//should be able to zoom more as current config has 0 as min level.
|
|
if (enabled) {
|
|
throw new Error('Not fully zoomed out yet');
|
|
}
|
|
});
|
|
}
|
|
|
|
async getMapZoomInEnabled() {
|
|
return await this.getMapZoomEnabled('a.leaflet-control-zoom-in');
|
|
}
|
|
|
|
async getMapZoomOutEnabled() {
|
|
return await this.getMapZoomEnabled('a.leaflet-control-zoom-out');
|
|
}
|
|
|
|
async clickMapFitDataBounds() {
|
|
return await this.clickMapButton('a.fa-crop');
|
|
}
|
|
|
|
async clickLandingPageBreadcrumbLink() {
|
|
log.debug('clickLandingPageBreadcrumbLink');
|
|
await find.clickByCssSelector(`a[href="#${VisualizeConstants.LANDING_PAGE_PATH}"]`);
|
|
}
|
|
|
|
/**
|
|
* Returns true if already on the landing page (that page doesn't have a link to itself).
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
async onLandingPage() {
|
|
log.debug(`VisualizePage.onLandingPage`);
|
|
const exists = await testSubjects.exists('visualizeLandingPage');
|
|
return exists;
|
|
}
|
|
|
|
async gotoLandingPage() {
|
|
log.debug('VisualizePage.gotoLandingPage');
|
|
const onPage = await this.onLandingPage();
|
|
if (!onPage) {
|
|
await retry.try(async () => {
|
|
await this.clickLandingPageBreadcrumbLink();
|
|
const onLandingPage = await this.onLandingPage();
|
|
if (!onLandingPage) throw new Error('Not on the landing page.');
|
|
});
|
|
}
|
|
}
|
|
|
|
async getLegendEntries() {
|
|
const legendEntries = await find.allByCssSelector('.visLegend__valueTitle', defaultFindTimeout * 2);
|
|
return await Promise.all(legendEntries.map(async chart => await chart.getAttribute('data-label')));
|
|
}
|
|
|
|
async openLegendOptionColors(name) {
|
|
await retry.try(async () => {
|
|
// This click has been flaky in opening the legend, hence the retry. See
|
|
// https://github.com/elastic/kibana/issues/17468
|
|
await testSubjects.click(`legend-${name}`);
|
|
// arbitrary color chosen, any available would do
|
|
const isOpen = await this.doesLegendColorChoiceExist('#EF843C');
|
|
if (!isOpen) {
|
|
throw new Error('legend color selector not open');
|
|
}
|
|
});
|
|
}
|
|
|
|
async filterOnTableCell(column, row) {
|
|
await retry.try(async () => {
|
|
const table = await testSubjects.find('tableVis');
|
|
const cell = await table.findByCssSelector(`tbody tr:nth-child(${row}) td:nth-child(${column})`);
|
|
await remote.moveMouseTo(cell);
|
|
const filterBtn = await testSubjects.findDescendant('filterForCellValue', cell);
|
|
await filterBtn.click();
|
|
});
|
|
}
|
|
|
|
async toggleLegend(show = true) {
|
|
const isVisible = remote.findByCssSelector('vislib-legend .legend-ul');
|
|
if ((show && !isVisible) || (!show && isVisible)) {
|
|
await testSubjects.click('vislibToggleLegend');
|
|
}
|
|
}
|
|
|
|
async filterLegend(name) {
|
|
await this.toggleLegend();
|
|
await testSubjects.click(`legend-${name}`);
|
|
await testSubjects.click(`legend-${name}-filterIn`);
|
|
}
|
|
|
|
async doesLegendColorChoiceExist(color) {
|
|
return await testSubjects.exists(`legendSelectColor-${color}`);
|
|
}
|
|
|
|
async selectNewLegendColorChoice(color) {
|
|
await testSubjects.click(`legendSelectColor-${color}`);
|
|
}
|
|
|
|
async doesSelectedLegendColorExist(color) {
|
|
return await testSubjects.exists(`legendSelectedColor-${color}`);
|
|
}
|
|
|
|
async getYAxisTitle() {
|
|
const title = await find.byCssSelector('.y-axis-div .y-axis-title text');
|
|
return await title.getVisibleText();
|
|
}
|
|
|
|
async selectBucketType(type) {
|
|
const bucketType = await find.byCssSelector(`[data-test-subj="${type}"]`);
|
|
return await bucketType.click();
|
|
}
|
|
|
|
async filterPieSlice(name) {
|
|
const slice = await this.getPieSlice(name);
|
|
// Since slice is an SVG element we can't simply use .click() for it
|
|
await remote.moveMouseTo(slice);
|
|
await remote.clickMouseButton();
|
|
}
|
|
|
|
async getPieSlice(name) {
|
|
return await testSubjects.find(`pieSlice-${name.split(' ').join('-')}`);
|
|
}
|
|
|
|
async getAllPieSlices(name) {
|
|
return await testSubjects.findAll(`pieSlice-${name.split(' ').join('-')}`);
|
|
}
|
|
|
|
async getPieSliceStyle(name) {
|
|
log.debug(`VisualizePage.getPieSliceStyle(${name})`);
|
|
const pieSlice = await this.getPieSlice(name);
|
|
return await pieSlice.getAttribute('style');
|
|
}
|
|
|
|
async getAllPieSliceStyles(name) {
|
|
log.debug(`VisualizePage.getAllPieSliceStyles(${name})`);
|
|
const pieSlices = await this.getAllPieSlices(name);
|
|
return await Promise.all(pieSlices.map(async pieSlice => await pieSlice.getAttribute('style')));
|
|
}
|
|
|
|
async getBucketErrorMessage() {
|
|
const error = await find.byCssSelector('.visEditorAggParam__error');
|
|
const errorMessage = await error.getProperty('innerText');
|
|
log.debug(errorMessage);
|
|
return errorMessage;
|
|
}
|
|
|
|
async selectSortMetric(agg, metric) {
|
|
const sortMetric = await find.byCssSelector(`[data-test-subj="visEditorOrder${agg}-${metric}"]`);
|
|
return await sortMetric.click();
|
|
}
|
|
|
|
async selectCustomSortMetric(agg, metric, field) {
|
|
await this.selectSortMetric(agg, 'custom');
|
|
await this.selectAggregation(metric, 'groupName');
|
|
await this.selectField(field, 'groupName');
|
|
}
|
|
|
|
async clickSplitDirection(direction) {
|
|
const activeParamPanel = await find.byCssSelector('vis-editor-agg-params[aria-hidden="false"]');
|
|
const button = await testSubjects.findDescendant(`splitBy-${direction}`, activeParamPanel);
|
|
await button.click();
|
|
}
|
|
|
|
async countNestedTables() {
|
|
const vis = await testSubjects.find('tableVis');
|
|
const result = [];
|
|
|
|
for (let i = 1; true; i++) {
|
|
const selector = new Array(i).fill('.agg-table-group').join(' ');
|
|
const tables = await vis.findAllByCssSelector(selector);
|
|
if (tables.length === 0) {
|
|
break;
|
|
}
|
|
result.push(tables.length);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
}
|
|
|
|
return new VisualizePage();
|
|
}
|