* [Visualizations] New vis wizard * Update functional tests * Create oss plugins for maps and lens and unregister alias function * Add new plugins to .i18nrc.json * Add readme and codeowners to the new plugins * update docs * fix tests * fix types * fixes * Update development docs * fix oss functional tests * Fix jest and x-pack functional tests * Fix functional test * changes on the layout * Cleanup and responsiveness * cleanup unecessary code * add common folder to the new OSS plugins * remove unecessary translations * Update limits.yml file * Fix basic label * Add experimental badge on controls vis * Nice improvements * fixes * Improving styles * Making modal go full height on smaller screens * Fixing sass lint warning * fix lint error * fix internationalization error * PR fixes * PR changes * Use useCallback where possible * Remove translations that need to be translated again * Lazy Load wizard modal * Remove legacyMapVisualizationWarning * Import the OSS plugins constants from the plugins * Export constant from lensOss * Change the new oss plugins from OSS to Oss * Add a new line to the kibana.json files of the new plugins * New nit fix * Fix spaces * Change the texts for the first step of the modal * Fix test * Fixes some of the PR comments * Add onClick funtionality to the entire aggregation based card * Cards description changes, introduce a copyFromRoot method to solve the problem of when disabling the x-pack plugic, to also disable the oss * Create new FTR for testing the functionality of the wizard when both maps and lens apps are disabled * fix eslint error * Change groupTitles and descriptions * Change input vis description * Remove the copyFromRoot from the signature of the ConfigDeprecationFactory and export it from the main entrypoint * Make the disabled cards badge clickable * Changes from code review * Fix functional tests failures * Rename groupTitle to titleInWizard to be more specific * Change vega vis note * minor design changes * fix problem with plugins list docs * Retrieve maps and lens landing page from docs service and add tracking url param * Fix funtional test for the new dashboard flow * Fix logic in alias registry for removing the discardOnRegister alias * no need to remove the alias entry from the discardOnRegister array Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: miukimiu <elizabet.oliveira@elastic.co>
424 lines
14 KiB
TypeScript
424 lines
14 KiB
TypeScript
/*
|
|
* 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 { FtrProviderContext } from '../ftr_provider_context';
|
|
import { VisualizeConstants } from '../../../src/plugins/visualize/public/application/visualize_constants';
|
|
|
|
export function VisualizePageProvider({ getService, getPageObjects }: FtrProviderContext) {
|
|
const testSubjects = getService('testSubjects');
|
|
const retry = getService('retry');
|
|
const find = getService('find');
|
|
const log = getService('log');
|
|
const globalNav = getService('globalNav');
|
|
const listingTable = getService('listingTable');
|
|
const { common, header, visEditor } = getPageObjects(['common', 'header', 'visEditor']);
|
|
|
|
/**
|
|
* This page object contains the visualization type selection, the landing page,
|
|
* and the open/save dialog functions
|
|
*/
|
|
class VisualizePage {
|
|
index = {
|
|
LOGSTASH_TIME_BASED: 'logstash-*',
|
|
LOGSTASH_NON_TIME_BASED: 'logstash*',
|
|
};
|
|
|
|
public async gotoVisualizationLandingPage() {
|
|
await common.navigateToApp('visualize');
|
|
}
|
|
|
|
public async clickNewVisualization() {
|
|
await listingTable.clickNewButton('createVisualizationPromptButton');
|
|
}
|
|
|
|
public async clickAggBasedVisualizations() {
|
|
await testSubjects.click('visGroupAggBasedExploreLink');
|
|
}
|
|
|
|
public async goBackToGroups() {
|
|
await testSubjects.click('goBackLink');
|
|
}
|
|
|
|
public async createVisualizationPromptButton() {
|
|
await testSubjects.click('createVisualizationPromptButton');
|
|
}
|
|
|
|
public async getChartTypes() {
|
|
const chartTypeField = await testSubjects.find('visNewDialogTypes');
|
|
const $ = await chartTypeField.parseDomContent();
|
|
return $('button')
|
|
.toArray()
|
|
.map((chart) => $(chart).findTestSubject('visTypeTitle').text().trim());
|
|
}
|
|
|
|
public async getPromotedVisTypes() {
|
|
const chartTypeField = await testSubjects.find('visNewDialogGroups');
|
|
const $ = await chartTypeField.parseDomContent();
|
|
const promotedVisTypes: string[] = [];
|
|
$('button')
|
|
.toArray()
|
|
.forEach((chart) => {
|
|
const title = $(chart).findTestSubject('visTypeTitle').text().trim();
|
|
if (title) {
|
|
promotedVisTypes.push(title);
|
|
}
|
|
});
|
|
return promotedVisTypes;
|
|
}
|
|
|
|
public async waitForVisualizationSelectPage() {
|
|
await retry.try(async () => {
|
|
const visualizeSelectTypePage = await testSubjects.find('visNewDialogTypes');
|
|
if (!(await visualizeSelectTypePage.isDisplayed())) {
|
|
throw new Error('wait for visualization select page');
|
|
}
|
|
});
|
|
}
|
|
|
|
public async waitForGroupsSelectPage() {
|
|
await retry.try(async () => {
|
|
const visualizeSelectGroupStep = await testSubjects.find('visNewDialogGroups');
|
|
if (!(await visualizeSelectGroupStep.isDisplayed())) {
|
|
throw new Error('wait for vis groups select step');
|
|
}
|
|
});
|
|
}
|
|
|
|
public async navigateToNewVisualization() {
|
|
await common.navigateToApp('visualize');
|
|
await header.waitUntilLoadingHasFinished();
|
|
await this.clickNewVisualization();
|
|
await this.waitForGroupsSelectPage();
|
|
}
|
|
|
|
public async navigateToNewAggBasedVisualization() {
|
|
await common.navigateToApp('visualize');
|
|
await header.waitUntilLoadingHasFinished();
|
|
await this.clickNewVisualization();
|
|
await this.clickAggBasedVisualizations();
|
|
await this.waitForVisualizationSelectPage();
|
|
}
|
|
|
|
public async hasVisType(type: string) {
|
|
return await testSubjects.exists(`visType-${type}`);
|
|
}
|
|
|
|
public async clickVisType(type: string) {
|
|
await testSubjects.click(`visType-${type}`);
|
|
await header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
public async clickAreaChart() {
|
|
await this.clickVisType('area');
|
|
}
|
|
|
|
public async clickDataTable() {
|
|
await this.clickVisType('table');
|
|
}
|
|
|
|
public async clickLineChart() {
|
|
await this.clickVisType('line');
|
|
}
|
|
|
|
public async clickRegionMap() {
|
|
await this.clickVisType('region_map');
|
|
}
|
|
|
|
public async hasRegionMap() {
|
|
return await this.hasVisType('region_map');
|
|
}
|
|
|
|
public async clickMarkdownWidget() {
|
|
await this.clickVisType('markdown');
|
|
}
|
|
|
|
public async clickMetric() {
|
|
await this.clickVisType('metric');
|
|
}
|
|
|
|
public async clickGauge() {
|
|
await this.clickVisType('gauge');
|
|
}
|
|
|
|
public async clickPieChart() {
|
|
await this.clickVisType('pie');
|
|
}
|
|
|
|
public async clickTileMap() {
|
|
await this.clickVisType('tile_map');
|
|
}
|
|
|
|
public async hasTileMap() {
|
|
return await this.hasVisType('tile_map');
|
|
}
|
|
|
|
public async clickTagCloud() {
|
|
await this.clickVisType('tagcloud');
|
|
}
|
|
|
|
public async clickVega() {
|
|
await this.clickVisType('vega');
|
|
}
|
|
|
|
public async clickVisualBuilder() {
|
|
await this.clickVisType('metrics');
|
|
}
|
|
|
|
public async clickVerticalBarChart() {
|
|
await this.clickVisType('histogram');
|
|
}
|
|
|
|
public async clickHeatmapChart() {
|
|
await this.clickVisType('heatmap');
|
|
}
|
|
|
|
public async clickInputControlVis() {
|
|
await this.clickVisType('input_control_vis');
|
|
}
|
|
|
|
public async clickLensWidget() {
|
|
await this.clickVisType('lens');
|
|
}
|
|
|
|
public async clickMapsApp() {
|
|
await this.clickVisType('maps');
|
|
}
|
|
|
|
public async hasMapsApp() {
|
|
return await this.hasVisType('maps');
|
|
}
|
|
|
|
public async createSimpleMarkdownViz(vizName: string) {
|
|
await this.gotoVisualizationLandingPage();
|
|
await this.navigateToNewVisualization();
|
|
await this.clickMarkdownWidget();
|
|
await visEditor.setMarkdownTxt(vizName);
|
|
await visEditor.clickGo();
|
|
await this.saveVisualization(vizName);
|
|
}
|
|
|
|
public async clickNewSearch(indexPattern = this.index.LOGSTASH_TIME_BASED) {
|
|
await testSubjects.click(`savedObjectTitle${indexPattern.split(' ').join('-')}`);
|
|
await header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
public 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();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes all existing visualizations
|
|
*/
|
|
public async deleteAllVisualizations() {
|
|
await retry.try(async () => {
|
|
await listingTable.checkListingSelectAllCheckbox();
|
|
await listingTable.clickDeleteSelected();
|
|
await common.clickConfirmOnModal();
|
|
await testSubjects.find('createVisualizationPromptButton');
|
|
});
|
|
}
|
|
|
|
public async isBetaInfoShown() {
|
|
return await testSubjects.exists('betaVisInfo');
|
|
}
|
|
|
|
public async getBetaTypeLinks() {
|
|
return await find.allByCssSelector('[data-vis-stage="beta"]');
|
|
}
|
|
|
|
public async getExperimentalTypeLinks() {
|
|
return await find.allByCssSelector('[data-vis-stage="experimental"]');
|
|
}
|
|
|
|
public async isExperimentalInfoShown() {
|
|
return await testSubjects.exists('experimentalVisInfo');
|
|
}
|
|
|
|
public async getExperimentalInfo() {
|
|
return await testSubjects.find('experimentalVisInfo');
|
|
}
|
|
|
|
public async getSideEditorExists() {
|
|
return await find.existsByCssSelector('.visEditor__collapsibleSidebar');
|
|
}
|
|
|
|
public async clickSavedSearch(savedSearchName: string) {
|
|
await testSubjects.click(`savedObjectTitle${savedSearchName.split(' ').join('-')}`);
|
|
await header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
public async clickUnlinkSavedSearch() {
|
|
await testSubjects.click('showUnlinkSavedSearchPopover');
|
|
await testSubjects.click('unlinkSavedSearch');
|
|
await header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
public async ensureSavePanelOpen() {
|
|
log.debug('ensureSavePanelOpen');
|
|
await header.waitUntilLoadingHasFinished();
|
|
const isOpen = await testSubjects.exists('savedObjectSaveModal', { timeout: 5000 });
|
|
if (!isOpen) {
|
|
await testSubjects.click('visualizeSaveButton');
|
|
}
|
|
}
|
|
|
|
public 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="#/"]');
|
|
}
|
|
|
|
public async loadSavedVisualization(vizName: string, { navigateToVisualize = true } = {}) {
|
|
if (navigateToVisualize) {
|
|
await this.clickLoadSavedVisButton();
|
|
}
|
|
await this.openSavedVisualization(vizName);
|
|
}
|
|
|
|
public async openSavedVisualization(vizName: string) {
|
|
const dataTestSubj = `visListingTitleLink-${vizName.split(' ').join('-')}`;
|
|
await testSubjects.click(dataTestSubj, 20000);
|
|
await header.waitUntilLoadingHasFinished();
|
|
}
|
|
|
|
public async waitForVisualizationSavedToastGone() {
|
|
await testSubjects.waitForDeleted('saveVisualizationSuccess');
|
|
}
|
|
|
|
public 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>}
|
|
*/
|
|
public async onLandingPage() {
|
|
log.debug(`VisualizePage.onLandingPage`);
|
|
return await testSubjects.exists('visualizationLandingPage');
|
|
}
|
|
|
|
public 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.');
|
|
});
|
|
}
|
|
}
|
|
|
|
public async saveVisualization(
|
|
vizName: string,
|
|
{ saveAsNew = false, redirectToOrigin = false } = {}
|
|
) {
|
|
await this.ensureSavePanelOpen();
|
|
await testSubjects.setValue('savedObjectTitle', vizName);
|
|
|
|
const saveAsNewCheckboxExists = await testSubjects.exists('saveAsNewCheckbox');
|
|
if (saveAsNewCheckboxExists) {
|
|
const state = saveAsNew ? 'check' : 'uncheck';
|
|
log.debug('save as new checkbox exists. Setting its state to', state);
|
|
await testSubjects.setEuiSwitch('saveAsNewCheckbox', state);
|
|
}
|
|
|
|
const redirectToOriginCheckboxExists = await testSubjects.exists('returnToOriginModeSwitch');
|
|
if (redirectToOriginCheckboxExists) {
|
|
const state = redirectToOrigin ? 'check' : 'uncheck';
|
|
log.debug('redirect to origin checkbox exists. Setting its state to', state);
|
|
await testSubjects.setEuiSwitch('returnToOriginModeSwitch', state);
|
|
}
|
|
log.debug('Click Save Visualization button');
|
|
|
|
await testSubjects.click('confirmSaveSavedObjectButton');
|
|
|
|
// Confirm that the Visualization has actually been saved
|
|
await testSubjects.existOrFail('saveVisualizationSuccess');
|
|
const message = await common.closeToast();
|
|
await header.waitUntilLoadingHasFinished();
|
|
await common.waitForSaveModalToClose();
|
|
|
|
return message;
|
|
}
|
|
|
|
public async saveVisualizationExpectSuccess(
|
|
vizName: string,
|
|
{ saveAsNew = false, redirectToOrigin = false } = {}
|
|
) {
|
|
const saveMessage = await this.saveVisualization(vizName, { saveAsNew, redirectToOrigin });
|
|
if (!saveMessage) {
|
|
throw new Error(
|
|
`Expected saveVisualization to respond with the saveMessage from the toast, got ${saveMessage}`
|
|
);
|
|
}
|
|
}
|
|
|
|
public async saveVisualizationExpectSuccessAndBreadcrumb(
|
|
vizName: string,
|
|
{ saveAsNew = false, redirectToOrigin = false } = {}
|
|
) {
|
|
await this.saveVisualizationExpectSuccess(vizName, { saveAsNew, redirectToOrigin });
|
|
await retry.waitFor(
|
|
'last breadcrumb to have new vis name',
|
|
async () => (await globalNav.getLastBreadcrumb()) === vizName
|
|
);
|
|
}
|
|
|
|
public async saveVisualizationAndReturn() {
|
|
await header.waitUntilLoadingHasFinished();
|
|
await testSubjects.existOrFail('visualizesaveAndReturnButton');
|
|
await testSubjects.click('visualizesaveAndReturnButton');
|
|
}
|
|
|
|
public async linkedToOriginatingApp() {
|
|
await header.waitUntilLoadingHasFinished();
|
|
await testSubjects.existOrFail('visualizesaveAndReturnButton');
|
|
}
|
|
|
|
public async notLinkedToOriginatingApp() {
|
|
await header.waitUntilLoadingHasFinished();
|
|
await testSubjects.missingOrFail('visualizesaveAndReturnButton');
|
|
}
|
|
|
|
public async cancelAndReturn(showConfirmModal: boolean) {
|
|
await header.waitUntilLoadingHasFinished();
|
|
await testSubjects.existOrFail('visualizeCancelAndReturnButton');
|
|
await testSubjects.click('visualizeCancelAndReturnButton');
|
|
if (showConfirmModal) {
|
|
await retry.waitFor(
|
|
'confirm modal to show',
|
|
async () => await testSubjects.exists('appLeaveConfirmModal')
|
|
);
|
|
await testSubjects.exists('confirmModalConfirmButton');
|
|
await testSubjects.click('confirmModalConfirmButton');
|
|
}
|
|
}
|
|
}
|
|
|
|
return new VisualizePage();
|
|
}
|