mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
* Add tests to catch error * Fix test * Don't store column and sort state in panel uiState unless explicitly overridden in a dashboard. * add debug messages * Elements can go stale between the find by and the click event so wrap in a retry * fix bad merge with master
534 lines
17 KiB
JavaScript
534 lines
17 KiB
JavaScript
import _ from 'lodash';
|
|
import { DashboardConstants } from '../../../src/core_plugins/kibana/public/dashboard/dashboard_constants';
|
|
|
|
export const PIE_CHART_VIS_NAME = 'Visualization PieChart';
|
|
|
|
export function DashboardPageProvider({ getService, getPageObjects }) {
|
|
const log = getService('log');
|
|
const find = getService('find');
|
|
const retry = getService('retry');
|
|
const config = getService('config');
|
|
const remote = getService('remote');
|
|
const esArchiver = getService('esArchiver');
|
|
const kibanaServer = getService('kibanaServer');
|
|
const testSubjects = getService('testSubjects');
|
|
const PageObjects = getPageObjects(['common', 'header']);
|
|
|
|
const defaultFindTimeout = config.get('timeouts.find');
|
|
|
|
class DashboardPage {
|
|
async initTests() {
|
|
const logstash = esArchiver.loadIfNeeded('logstash_functional');
|
|
await kibanaServer.uiSettings.replace({
|
|
'dateFormat:tz': 'UTC',
|
|
'defaultIndex': 'logstash-*'
|
|
});
|
|
|
|
log.debug('load kibana index with visualizations');
|
|
await esArchiver.load('dashboard');
|
|
|
|
await PageObjects.common.navigateToApp('dashboard');
|
|
return logstash;
|
|
}
|
|
|
|
async clickEditVisualization() {
|
|
log.debug('clickEditVisualization');
|
|
await testSubjects.click('dashboardPanelToggleMenuIcon');
|
|
await testSubjects.click('dashboardPanelEditLink');
|
|
|
|
await retry.try(async () => {
|
|
const current = await remote.getCurrentUrl();
|
|
if (current.indexOf('visualize') < 0) {
|
|
throw new Error('not on visualize page');
|
|
}
|
|
});
|
|
}
|
|
|
|
async clickFullScreenMode() {
|
|
log.debug(`clickFullScreenMode`);
|
|
await testSubjects.click('dashboardFullScreenMode');
|
|
}
|
|
|
|
async fullScreenModeMenuItemExists() {
|
|
return await testSubjects.exists('dashboardFullScreenMode');
|
|
}
|
|
|
|
async exitFullScreenTextButtonExists() {
|
|
return await testSubjects.exists('exitFullScreenModeText');
|
|
}
|
|
|
|
async getExitFullScreenTextButton() {
|
|
return await testSubjects.find('exitFullScreenModeText');
|
|
}
|
|
|
|
async exitFullScreenLogoButtonExists() {
|
|
return await testSubjects.exists('exitFullScreenModeLogo');
|
|
}
|
|
|
|
async getExitFullScreenLogoButton() {
|
|
return await testSubjects.find('exitFullScreenModeLogo');
|
|
}
|
|
|
|
async clickExitFullScreenLogoButton() {
|
|
await testSubjects.click('exitFullScreenModeLogo');
|
|
}
|
|
|
|
async clickExitFullScreenTextButton() {
|
|
await testSubjects.click('exitFullScreenModeText');
|
|
}
|
|
|
|
/**
|
|
* Returns true if already on the dashboard landing page (that page doesn't have a link to itself).
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
async onDashboardLandingPage() {
|
|
log.debug(`onDashboardLandingPage`);
|
|
const exists = await testSubjects.exists('dashboardLandingPage');
|
|
return exists;
|
|
}
|
|
|
|
async clickDashboardBreadcrumbLink() {
|
|
log.debug('clickDashboardBreadcrumbLink');
|
|
await find.clickByCssSelector(`a[href="#${DashboardConstants.LANDING_PAGE_PATH}"]`);
|
|
}
|
|
|
|
async gotoDashboardLandingPage() {
|
|
log.debug('gotoDashboardLandingPage');
|
|
const onPage = await this.onDashboardLandingPage();
|
|
if (!onPage) {
|
|
await retry.try(async () => {
|
|
await this.clickDashboardBreadcrumbLink();
|
|
const onDashboardLandingPage = await this.onDashboardLandingPage();
|
|
if (!onDashboardLandingPage) throw new Error('Not on the landing page.');
|
|
});
|
|
}
|
|
}
|
|
|
|
async getQueryInputElement() {
|
|
return await testSubjects.find('queryInput');
|
|
}
|
|
|
|
async getQuery() {
|
|
log.debug(`getQuery`);
|
|
const queryInputElement = await this.getQueryInputElement();
|
|
return await queryInputElement.getProperty('value');
|
|
}
|
|
|
|
async setQuery(query) {
|
|
log.debug(`setQuery(${query})`);
|
|
return await testSubjects.setValue('queryInput', query);
|
|
}
|
|
|
|
async clickFilterButton() {
|
|
log.debug('Clicking filter button');
|
|
return await testSubjects.click('querySubmitButton');
|
|
}
|
|
|
|
async clickClone() {
|
|
log.debug('Clicking clone');
|
|
await testSubjects.click('dashboardClone');
|
|
}
|
|
|
|
async confirmClone() {
|
|
log.debug('Confirming clone');
|
|
await testSubjects.click('cloneConfirmButton');
|
|
}
|
|
|
|
async cancelClone() {
|
|
log.debug('Canceling clone');
|
|
await testSubjects.click('cloneCancelButton');
|
|
}
|
|
|
|
async setClonedDashboardTitle(title) {
|
|
await testSubjects.setValue('clonedDashboardTitle', title);
|
|
}
|
|
|
|
async clickEdit() {
|
|
log.debug('Clicking edit');
|
|
return await testSubjects.click('dashboardEditMode');
|
|
}
|
|
|
|
async getIsInViewMode() {
|
|
log.debug('getIsInViewMode');
|
|
return await testSubjects.exists('dashboardEditMode');
|
|
}
|
|
|
|
async clickCancelOutOfEditMode() {
|
|
log.debug('clickCancelOutOfEditMode');
|
|
return await testSubjects.click('dashboardViewOnlyMode');
|
|
}
|
|
|
|
async clickNewDashboard() {
|
|
return await testSubjects.click('newDashboardLink');
|
|
}
|
|
|
|
async clickCreateDashboardPrompt() {
|
|
await testSubjects.click('createDashboardPromptButton');
|
|
}
|
|
|
|
async getCreateDashboardPromptExists() {
|
|
return await testSubjects.exists('createDashboardPromptButton');
|
|
}
|
|
|
|
async clickListItemCheckbox() {
|
|
await testSubjects.click('dashboardListItemCheckbox');
|
|
}
|
|
|
|
async clickDeleteSelectedDashboards() {
|
|
await testSubjects.click('deleteSelectedDashboards');
|
|
}
|
|
|
|
async clickAddVisualization() {
|
|
await testSubjects.click('dashboardAddPanelButton');
|
|
}
|
|
|
|
async clickAddNewVisualizationLink() {
|
|
await testSubjects.click('addNewSavedObjectLink');
|
|
}
|
|
|
|
async clickOptions() {
|
|
await testSubjects.click('dashboardOptionsButton');
|
|
}
|
|
|
|
async isOptionsOpen() {
|
|
log.debug('isOptionsOpen');
|
|
return await testSubjects.exists('dashboardDarkThemeCheckbox');
|
|
}
|
|
|
|
async openOptions() {
|
|
log.debug('openOptions');
|
|
const isOpen = await this.isOptionsOpen();
|
|
if (!isOpen) {
|
|
return await testSubjects.click('dashboardOptionsButton');
|
|
}
|
|
}
|
|
|
|
async isDarkThemeOn() {
|
|
log.debug('isDarkThemeOn');
|
|
await this.openOptions();
|
|
const darkThemeCheckbox = await testSubjects.find('dashboardDarkThemeCheckbox');
|
|
return await darkThemeCheckbox.getProperty('checked');
|
|
}
|
|
|
|
async useDarkTheme(on) {
|
|
await this.openOptions();
|
|
const isDarkThemeOn = await this.isDarkThemeOn();
|
|
if (isDarkThemeOn !== on) {
|
|
return await testSubjects.click('dashboardDarkThemeCheckbox');
|
|
}
|
|
}
|
|
|
|
async filterVizNames(vizName) {
|
|
const visFilter = await find.byCssSelector('input[placeholder="Visualizations Filter..."]');
|
|
await visFilter.click();
|
|
await remote.pressKeys(vizName);
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickVizNameLink(vizName) {
|
|
await find.clickByPartialLinkText(vizName);
|
|
}
|
|
|
|
async closeAddVizualizationPanel() {
|
|
log.debug('closeAddVizualizationPanel');
|
|
await find.clickByCssSelector('i.fa fa-chevron-up');
|
|
}
|
|
|
|
async gotoDashboardEditMode(dashboardName) {
|
|
await this.loadSavedDashboard(dashboardName);
|
|
await this.clickEdit();
|
|
}
|
|
|
|
async filterSearchNames(name) {
|
|
await testSubjects.setValue('savedObjectFinderSearchInput', name);
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async clickSavedSearchTab() {
|
|
await testSubjects.click('addSavedSearchTab');
|
|
}
|
|
|
|
async addSavedSearch(searchName) {
|
|
await this.clickAddVisualization();
|
|
await this.clickSavedSearchTab();
|
|
await this.filterSearchNames(searchName);
|
|
|
|
await find.clickByPartialLinkText(searchName);
|
|
await PageObjects.header.clickToastOK();
|
|
await this.clickAddVisualization();
|
|
}
|
|
|
|
async addVisualization(vizName) {
|
|
await this.clickAddVisualization();
|
|
log.debug('filter visualization (' + vizName + ')');
|
|
await this.filterVizNames(vizName);
|
|
await this.clickVizNameLink(vizName);
|
|
await PageObjects.header.clickToastOK();
|
|
// this second click of 'Add' collapses the Add Visualization pane
|
|
await this.clickAddVisualization();
|
|
}
|
|
|
|
async renameDashboard(dashName) {
|
|
log.debug(`Naming dashboard ` + dashName);
|
|
await testSubjects.click('dashboardRenameButton');
|
|
await testSubjects.setValue('dashboardTitle', dashName);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param dashName {String}
|
|
* @param saveOptions {{storeTimeWithDashboard: boolean, saveAsNew: boolean}}
|
|
*/
|
|
async saveDashboard(dashName, saveOptions = {}) {
|
|
await this.enterDashboardTitleAndClickSave(dashName, saveOptions);
|
|
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
|
|
// verify that green message at the top of the page.
|
|
// it's only there for about 5 seconds
|
|
return await PageObjects.header.getToastMessage();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param dashboardTitle {String}
|
|
* @param saveOptions {{storeTimeWithDashboard: boolean, saveAsNew: boolean}}
|
|
*/
|
|
async enterDashboardTitleAndClickSave(dashboardTitle, saveOptions = {}) {
|
|
await testSubjects.click('dashboardSaveMenuItem');
|
|
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
|
|
log.debug('entering new title');
|
|
await testSubjects.setValue('dashboardTitle', dashboardTitle);
|
|
|
|
if (saveOptions.storeTimeWithDashboard !== undefined) {
|
|
await this.setStoreTimeWithDashboard(saveOptions.storeTimeWithDashboard);
|
|
}
|
|
|
|
if (saveOptions.saveAsNew !== undefined) {
|
|
await this.setSaveAsNewCheckBox(saveOptions.saveAsNew);
|
|
}
|
|
|
|
await retry.try(async () => {
|
|
log.debug('clicking final Save button for named dashboard');
|
|
return await testSubjects.click('confirmSaveDashboardButton');
|
|
});
|
|
}
|
|
|
|
async clickDashboardByLinkText(dashName) {
|
|
await find.clickByLinkText(dashName);
|
|
}
|
|
|
|
async clearSearchValue() {
|
|
log.debug(`clearSearchValue`);
|
|
|
|
await this.gotoDashboardLandingPage();
|
|
|
|
await retry.try(async () => {
|
|
const searchFilter = await testSubjects.find('searchFilter');
|
|
await searchFilter.clearValue();
|
|
});
|
|
}
|
|
|
|
async searchForDashboardWithName(dashName) {
|
|
log.debug(`searchForDashboardWithName: ${dashName}`);
|
|
|
|
await this.gotoDashboardLandingPage();
|
|
|
|
await retry.try(async () => {
|
|
const searchFilter = await testSubjects.find('searchFilter');
|
|
await searchFilter.clearValue();
|
|
await searchFilter.click();
|
|
// Note: this replacement of - to space is to preserve original logic but I'm not sure why or if it's needed.
|
|
await searchFilter.type(dashName.replace('-', ' '));
|
|
});
|
|
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
async getCountOfDashboardsInListingTable() {
|
|
const dashboardTitles = await testSubjects.findAll('dashboardListingTitleLink');
|
|
return dashboardTitles.length;
|
|
}
|
|
|
|
async getDashboardCountWithName(dashName) {
|
|
log.debug(`getDashboardCountWithName: ${dashName}`);
|
|
|
|
await this.searchForDashboardWithName(dashName);
|
|
const links = await find.allByLinkText(dashName);
|
|
return links.length;
|
|
}
|
|
|
|
// use the search filter box to narrow the results down to a single
|
|
// entry, or at least to a single page of results
|
|
async loadSavedDashboard(dashName) {
|
|
log.debug(`Load Saved Dashboard ${dashName}`);
|
|
|
|
await retry.try(async () => {
|
|
await this.searchForDashboardWithName(dashName);
|
|
await this.clickDashboardByLinkText(dashName);
|
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
|
|
|
const onDashboardLandingPage = await this.onDashboardLandingPage();
|
|
if (onDashboardLandingPage) {
|
|
throw new Error('Failed to open the dashboard up');
|
|
}
|
|
});
|
|
}
|
|
|
|
async getPanelTitles() {
|
|
log.debug('in getPanelTitles');
|
|
const titleObjects = await testSubjects.findAll('dashboardPanelTitle');
|
|
|
|
function getTitles(chart) {
|
|
return chart.getVisibleText();
|
|
}
|
|
const getTitlePromises = _.map(titleObjects, getTitles);
|
|
return Promise.all(getTitlePromises);
|
|
}
|
|
|
|
async getDashboardPanels() {
|
|
return await testSubjects.findAll('dashboardPanel');
|
|
}
|
|
|
|
|
|
async getPanelDimensions() {
|
|
const panels = await find.allByCssSelector('.react-grid-item'); // These are gridster-defined elements and classes
|
|
async function getPanelDimensions(panel) {
|
|
const size = await panel.getSize();
|
|
return {
|
|
width: size.width,
|
|
height: size.height
|
|
};
|
|
}
|
|
|
|
const getDimensionsPromises = _.map(panels, getPanelDimensions);
|
|
return await Promise.all(getDimensionsPromises);
|
|
}
|
|
|
|
async getPanelCount() {
|
|
log.debug('getPanelCount');
|
|
const panels = await find.allByCssSelector('.react-grid-item');
|
|
return panels.length;
|
|
}
|
|
|
|
getTestVisualizations() {
|
|
return [
|
|
{ name: PIE_CHART_VIS_NAME, description: 'PieChart' },
|
|
{ name: 'Visualization☺ VerticalBarChart', description: 'VerticalBarChart' },
|
|
{ name: 'Visualization漢字 AreaChart', description: 'AreaChart' },
|
|
{ name: 'Visualization☺漢字 DataTable', description: 'DataTable' },
|
|
{ name: 'Visualization漢字 LineChart', description: 'LineChart' },
|
|
{ name: 'Visualization TileMap', description: 'TileMap' },
|
|
{ name: 'Visualization MetricChart', description: 'MetricChart' }
|
|
];
|
|
}
|
|
|
|
getTestVisualizationNames() {
|
|
return this.getTestVisualizations().map(visualization => visualization.name);
|
|
}
|
|
|
|
async showPanelEditControlsDropdownMenu() {
|
|
const editLinkExists = await testSubjects.exists('dashboardPanelEditLink');
|
|
if (editLinkExists) return;
|
|
await testSubjects.click('dashboardPanelToggleMenuIcon');
|
|
}
|
|
|
|
async clickDashboardPanelEditLink() {
|
|
await this.showPanelEditControlsDropdownMenu();
|
|
await testSubjects.click('dashboardPanelEditLink');
|
|
}
|
|
|
|
async clickDashboardPanelRemoveIcon() {
|
|
await this.showPanelEditControlsDropdownMenu();
|
|
await testSubjects.click('dashboardPanelRemoveIcon');
|
|
}
|
|
|
|
async addVisualizations(visualizations) {
|
|
for (const vizName of visualizations) {
|
|
await this.addVisualization(vizName);
|
|
}
|
|
}
|
|
|
|
async setTimepickerInDataRange() {
|
|
const fromTime = '2015-09-19 06:31:44.000';
|
|
const toTime = '2015-09-23 18:31:44.000';
|
|
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
|
|
}
|
|
|
|
async setSaveAsNewCheckBox(checked) {
|
|
log.debug('saveAsNewCheckbox: ' + checked);
|
|
const saveAsNewCheckbox = await testSubjects.find('saveAsNewCheckbox');
|
|
const isAlreadyChecked = await saveAsNewCheckbox.getProperty('checked');
|
|
if (isAlreadyChecked !== checked) {
|
|
log.debug('Flipping save as new checkbox');
|
|
await retry.try(() => saveAsNewCheckbox.click());
|
|
}
|
|
}
|
|
|
|
async setStoreTimeWithDashboard(checked) {
|
|
log.debug('Storing time with dashboard: ' + checked);
|
|
const storeTimeCheckbox = await testSubjects.find('storeTimeWithDashboard');
|
|
const isAlreadyChecked = await storeTimeCheckbox.getProperty('checked');
|
|
if (isAlreadyChecked !== checked) {
|
|
log.debug('Flipping store time checkbox');
|
|
await retry.try(() => storeTimeCheckbox.click());
|
|
}
|
|
}
|
|
|
|
async getFilters(timeout = defaultFindTimeout) {
|
|
return await find.allByCssSelector('.filter-bar .filter', timeout);
|
|
}
|
|
|
|
async getFilterDescriptions(timeout = defaultFindTimeout) {
|
|
const filters = await find.allByCssSelector(
|
|
'.filter-bar > .filter > .filter-description',
|
|
timeout);
|
|
return _.map(filters, async (filter) => await filter.getVisibleText());
|
|
}
|
|
|
|
async filterOnPieSlice() {
|
|
log.debug('Filtering on a pie slice');
|
|
await retry.try(async () => {
|
|
const slices = await find.allByCssSelector('svg > g > path.slice');
|
|
log.debug('Slices found:' + slices.length);
|
|
return slices[0].click();
|
|
});
|
|
}
|
|
|
|
async toggleExpandPanel() {
|
|
log.debug('toggleExpandPanel');
|
|
await testSubjects.moveMouseTo('dashboardPanelTitle');
|
|
const expandShown = await testSubjects.exists('dashboardPanelExpandIcon');
|
|
if (!expandShown) {
|
|
await testSubjects.click('dashboardPanelToggleMenuIcon');
|
|
}
|
|
await testSubjects.click('dashboardPanelExpandIcon');
|
|
}
|
|
|
|
async getSharedItemsCount() {
|
|
log.debug('in getSharedItemsCount');
|
|
const attributeName = 'data-shared-items-count';
|
|
const element = await find.byCssSelector(`[${attributeName}]`);
|
|
if (element) {
|
|
return await element.getAttribute(attributeName);
|
|
}
|
|
|
|
throw new Error('no element');
|
|
}
|
|
|
|
async getPanelSharedItemData() {
|
|
log.debug('in getPanelSharedItemData');
|
|
const sharedItems = await find.allByCssSelector('[data-shared-item]');
|
|
return await Promise.all(sharedItems.map(async sharedItem => {
|
|
return {
|
|
title: await sharedItem.getAttribute('data-title'),
|
|
description: await sharedItem.getAttribute('data-description')
|
|
};
|
|
}));
|
|
}
|
|
}
|
|
|
|
return new DashboardPage();
|
|
}
|