[Lens] Add functional tests on chart transitions and pie chart (#74083)

This commit is contained in:
Marta Bondyra 2020-08-06 18:52:21 +02:00 committed by GitHub
parent 042254f026
commit fa2251dd31
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 255 additions and 106 deletions

View file

@ -57,7 +57,7 @@ export function ChangeIndexPattern({
panelPaddingSize="s"
ownFocus
>
<div style={{ width: 320 }}>
<div style={{ width: 320 }} data-test-subj="lnsChangeIndexPatternPopup">
<EuiPopoverTitle>
{i18n.translate('xpack.lens.indexPattern.changeIndexPatternTitle', {
defaultMessage: 'Change index pattern',

View file

@ -122,6 +122,7 @@ export const pieVisualization: Visualization<PieVisualizationState, PieVisualiza
supportsMoreColumns: sortedColumns.length < MAX_TREEMAP_BUCKETS,
filterOperations: bucketedOperations,
required: true,
dataTestSubj: 'lnsPie_groupByDimensionPanel',
},
{
groupId: 'metric',
@ -133,6 +134,7 @@ export const pieVisualization: Visualization<PieVisualizationState, PieVisualiza
supportsMoreColumns: !layer.metric,
filterOperations: numberMetricOperations,
required: true,
dataTestSubj: 'lnsPie_sizeByDimensionPanel',
},
],
};
@ -150,6 +152,7 @@ export const pieVisualization: Visualization<PieVisualizationState, PieVisualiza
supportsMoreColumns: sortedColumns.length < MAX_PIE_BUCKETS,
filterOperations: bucketedOperations,
required: true,
dataTestSubj: 'lnsPie_sliceByDimensionPanel',
},
{
groupId: 'metric',
@ -161,6 +164,7 @@ export const pieVisualization: Visualization<PieVisualizationState, PieVisualiza
supportsMoreColumns: !layer.metric,
filterOperations: numberMetricOperations,
required: true,
dataTestSubj: 'lnsPie_sizeByDimensionPanel',
},
],
};

View file

@ -6,7 +6,7 @@
import React from 'react';
import { mountWithIntl as mount, shallowWithIntl as shallow } from 'test_utils/enzyme_helpers';
import { EuiButtonGroupProps, EuiSuperSelect } from '@elastic/eui';
import { EuiButtonGroupProps, EuiSuperSelect, EuiButtonGroup } from '@elastic/eui';
import { LayerContextMenu, XyToolbar } from './xy_config_panel';
import { FramePublicAPI } from '../types';
import { State } from './types';
@ -52,7 +52,7 @@ describe('XY Config panels', () => {
);
const options = component
.find('[data-test-subj="lnsXY_seriesType"]')
.find(EuiButtonGroup)
.first()
.prop('options') as EuiButtonGroupProps['options'];
@ -79,7 +79,7 @@ describe('XY Config panels', () => {
);
const options = component
.find('[data-test-subj="lnsXY_seriesType"]')
.find(EuiButtonGroup)
.first()
.prop('options') as EuiButtonGroupProps['options'];

View file

@ -95,13 +95,13 @@ export function LayerContextMenu(props: VisualizationLayerWidgetProps<State>) {
})}
name="chartType"
className="eui-displayInlineBlock"
data-test-subj="lnsXY_seriesType"
options={visualizationTypes
.filter((t) => isHorizontalSeries(t.id as SeriesType) === horizontalOnly)
.map((t) => ({
id: t.id,
label: t.label,
iconType: t.icon || 'empty',
'data-test-subj': `lnsXY_seriesType-${t.id}`,
}))}
idSelected={layer.seriesType}
onChange={(seriesType) => {

View file

@ -191,8 +191,9 @@ export default ({ getService }: FtrProviderContext) => {
expect(results.saved_overall).to.eql({
lnsMetric: 1,
bar_stacked: 1,
lnsPie: 1,
});
expect(results.saved_overall_total).to.eql(2);
expect(results.saved_overall_total).to.eql(3);
await esArchiver.unload('lens/basic');
});

View file

@ -0,0 +1,64 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['header', 'common', 'dashboard', 'timePicker', 'lens']);
const find = getService('find');
const dashboardAddPanel = getService('dashboardAddPanel');
const elasticChart = getService('elasticChart');
const browser = getService('browser');
const retry = getService('retry');
const testSubjects = getService('testSubjects');
const filterBar = getService('filterBar');
async function clickInChart(x: number, y: number) {
const el = await elasticChart.getCanvas();
await browser.getActions().move({ x, y, origin: el._webElement }).click().perform();
}
describe('lens dashboard tests', () => {
it('metric should be embeddable', async () => {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await dashboardAddPanel.clickOpenAddPanel();
await dashboardAddPanel.filterEmbeddableNames('Artistpreviouslyknownaslens');
await find.clickByButtonText('Artistpreviouslyknownaslens');
await dashboardAddPanel.closeAddPanel();
await PageObjects.lens.goToTimeRange();
await PageObjects.lens.assertMetric('Maximum of bytes', '19,986');
});
it('should be able to add filters/timerange by clicking in XYChart', async () => {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await dashboardAddPanel.clickOpenAddPanel();
await dashboardAddPanel.filterEmbeddableNames('lnsXYvis');
await find.clickByButtonText('lnsXYvis');
await dashboardAddPanel.closeAddPanel();
await PageObjects.lens.goToTimeRange();
await clickInChart(5, 5); // hardcoded position of bar
await retry.try(async () => {
await testSubjects.click('applyFiltersPopoverButton');
await testSubjects.missingOrFail('applyFiltersPopoverButton');
});
await PageObjects.lens.assertExactText(
'[data-test-subj="embeddablePanelHeading-lnsXYvis"]',
'lnsXYvis'
);
const time = await PageObjects.timePicker.getTimeConfig();
expect(time.start).to.equal('Sep 21, 2015 @ 09:00:00.000');
expect(time.end).to.equal('Sep 21, 2015 @ 12:00:00.000');
const hasIpFilter = await filterBar.hasFilter('ip', '97.220.3.248');
expect(hasIpFilter).to.be(true);
});
});
}

View file

@ -28,6 +28,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
this.tags(['ciGroup4', 'skipFirefox']);
loadTestFile(require.resolve('./smokescreen'));
loadTestFile(require.resolve('./dashboard'));
loadTestFile(require.resolve('./persistent_context'));
loadTestFile(require.resolve('./lens_reporting'));
});

View file

@ -8,115 +8,20 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects([
'header',
'common',
'visualize',
'dashboard',
'header',
'timePicker',
'lens',
]);
const PageObjects = getPageObjects(['visualize', 'lens']);
const find = getService('find');
const dashboardAddPanel = getService('dashboardAddPanel');
const elasticChart = getService('elasticChart');
const browser = getService('browser');
const retry = getService('retry');
const testSubjects = getService('testSubjects');
const filterBar = getService('filterBar');
const listingTable = getService('listingTable');
async function assertExpectedMetric(metricCount: string = '19,986') {
await PageObjects.lens.assertExactText(
'[data-test-subj="lns_metric_title"]',
'Maximum of bytes'
);
await PageObjects.lens.assertExactText('[data-test-subj="lns_metric_value"]', metricCount);
}
async function assertExpectedTable() {
await PageObjects.lens.assertExactText(
'[data-test-subj="lnsDataTable"] thead .euiTableCellContent__text',
'Maximum of bytes'
);
await PageObjects.lens.assertExactText(
'[data-test-subj="lnsDataTable"] [data-test-subj="lnsDataTableCellValue"]',
'19,986'
);
}
async function assertExpectedChart() {
await PageObjects.lens.assertExactText(
'[data-test-subj="embeddablePanelHeading-lnsXYvis"]',
'lnsXYvis'
);
}
async function assertExpectedTimerange() {
const time = await PageObjects.timePicker.getTimeConfig();
expect(time.start).to.equal('Sep 21, 2015 @ 09:00:00.000');
expect(time.end).to.equal('Sep 21, 2015 @ 12:00:00.000');
}
async function clickOnBarHistogram() {
const el = await elasticChart.getCanvas();
await browser.getActions().move({ x: 5, y: 5, origin: el._webElement }).click().perform();
}
describe('lens smokescreen tests', () => {
it('should allow editing saved visualizations', async () => {
await PageObjects.visualize.gotoVisualizationLandingPage();
await listingTable.searchForItemWithName('Artistpreviouslyknownaslens');
await PageObjects.lens.clickVisualizeListItemTitle('Artistpreviouslyknownaslens');
await PageObjects.lens.goToTimeRange();
await assertExpectedMetric();
await PageObjects.lens.assertMetric('Maximum of bytes', '19,986');
});
it('metric should be embeddable in dashboards', async () => {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await dashboardAddPanel.clickOpenAddPanel();
await dashboardAddPanel.filterEmbeddableNames('Artistpreviouslyknownaslens');
await find.clickByButtonText('Artistpreviouslyknownaslens');
await dashboardAddPanel.closeAddPanel();
await PageObjects.lens.goToTimeRange();
await assertExpectedMetric();
});
it('click on the bar in XYChart adds proper filters/timerange in dashboard', async () => {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await dashboardAddPanel.clickOpenAddPanel();
await dashboardAddPanel.filterEmbeddableNames('lnsXYvis');
await find.clickByButtonText('lnsXYvis');
await dashboardAddPanel.closeAddPanel();
await PageObjects.lens.goToTimeRange();
await clickOnBarHistogram();
await retry.try(async () => {
await testSubjects.click('applyFiltersPopoverButton');
await testSubjects.missingOrFail('applyFiltersPopoverButton');
});
await assertExpectedChart();
await assertExpectedTimerange();
const hasIpFilter = await filterBar.hasFilter('ip', '97.220.3.248');
expect(hasIpFilter).to.be(true);
});
it('should allow seamless transition to and from table view', async () => {
await PageObjects.visualize.gotoVisualizationLandingPage();
await listingTable.searchForItemWithName('Artistpreviouslyknownaslens');
await PageObjects.lens.clickVisualizeListItemTitle('Artistpreviouslyknownaslens');
await PageObjects.lens.goToTimeRange();
await assertExpectedMetric();
await PageObjects.lens.switchToVisualization('lnsDatatable');
await assertExpectedTable();
await PageObjects.lens.switchToVisualization('lnsMetric');
await assertExpectedMetric();
});
it('should allow creation of lens visualizations', async () => {
it('should allow creation of lens xy chart', async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickVisType('lens');
await PageObjects.lens.goToTimeRange();
@ -165,6 +70,19 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(await find.allByCssSelector('.echLegendItem')).to.have.length(3);
});
it('should allow seamless transition to and from table view', async () => {
await PageObjects.visualize.gotoVisualizationLandingPage();
await listingTable.searchForItemWithName('Artistpreviouslyknownaslens');
await PageObjects.lens.clickVisualizeListItemTitle('Artistpreviouslyknownaslens');
await PageObjects.lens.goToTimeRange();
await PageObjects.lens.assertMetric('Maximum of bytes', '19,986');
await PageObjects.lens.switchToVisualization('lnsDatatable');
expect(await PageObjects.lens.getDatatableHeaderText()).to.eql('Maximum of bytes');
expect(await PageObjects.lens.getDatatableCellText(0, 0)).to.eql('19,986');
await PageObjects.lens.switchToVisualization('lnsMetric');
await PageObjects.lens.assertMetric('Maximum of bytes', '19,986');
});
it('should switch from a multi-layer stacked bar to a multi-layer line chart', async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickVisType('lens');
@ -190,5 +108,94 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(await PageObjects.lens.getLayerCount()).to.eql(2);
});
it('should allow transition from line chart to donut chart and to bar chart', async () => {
await PageObjects.visualize.gotoVisualizationLandingPage();
await listingTable.searchForItemWithName('lnsXYvis');
await PageObjects.lens.clickVisualizeListItemTitle('lnsXYvis');
await PageObjects.lens.goToTimeRange();
expect(await PageObjects.lens.hasChartSwitchWarning('donut')).to.eql(true);
await PageObjects.lens.switchToVisualization('donut');
expect(await PageObjects.lens.getTitle()).to.eql('lnsXYvis');
expect(await PageObjects.lens.getDimensionTriggerText('lnsPie_sliceByDimensionPanel')).to.eql(
'Top values of ip'
);
expect(await PageObjects.lens.getDimensionTriggerText('lnsPie_sizeByDimensionPanel')).to.eql(
'Average of bytes'
);
expect(await PageObjects.lens.hasChartSwitchWarning('bar')).to.eql(false);
await PageObjects.lens.switchToVisualization('bar');
expect(await PageObjects.lens.getTitle()).to.eql('lnsXYvis');
expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_xDimensionPanel')).to.eql(
'Top values of ip'
);
expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_yDimensionPanel')).to.eql(
'Average of bytes'
);
});
it('should allow seamless transition from bar chart to line chart using layer chart switch', async () => {
await PageObjects.visualize.gotoVisualizationLandingPage();
await listingTable.searchForItemWithName('lnsXYvis');
await PageObjects.lens.clickVisualizeListItemTitle('lnsXYvis');
await PageObjects.lens.goToTimeRange();
await PageObjects.lens.switchLayerSeriesType('line');
expect(await PageObjects.lens.getTitle()).to.eql('lnsXYvis');
expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_xDimensionPanel')).to.eql(
'@timestamp'
);
expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_yDimensionPanel')).to.eql(
'Average of bytes'
);
expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_splitDimensionPanel')).to.eql(
'Top values of ip'
);
});
it('should allow seamless transition from pie chart to treemap chart', async () => {
await PageObjects.visualize.gotoVisualizationLandingPage();
await listingTable.searchForItemWithName('lnsPieVis');
await PageObjects.lens.clickVisualizeListItemTitle('lnsPieVis');
await PageObjects.lens.goToTimeRange();
expect(await PageObjects.lens.hasChartSwitchWarning('treemap')).to.eql(false);
await PageObjects.lens.switchToVisualization('treemap');
expect(
await PageObjects.lens.getDimensionTriggerText('lnsPie_groupByDimensionPanel', 0)
).to.eql('Top values of geo.dest');
expect(
await PageObjects.lens.getDimensionTriggerText('lnsPie_groupByDimensionPanel', 1)
).to.eql('Top values of geo.src');
expect(await PageObjects.lens.getDimensionTriggerText('lnsPie_sizeByDimensionPanel')).to.eql(
'Average of bytes'
);
});
it('should allow creating a pie chart and switching to datatable', async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickVisType('lens');
await PageObjects.lens.goToTimeRange();
await PageObjects.lens.switchToVisualization('pie');
await PageObjects.lens.configureDimension({
dimension: 'lnsPie_sliceByDimensionPanel > lns-empty-dimension',
operation: 'date_histogram',
field: '@timestamp',
});
await PageObjects.lens.configureDimension({
dimension: 'lnsPie_sizeByDimensionPanel > lns-empty-dimension',
operation: 'avg',
field: 'bytes',
});
expect(await PageObjects.lens.hasChartSwitchWarning('lnsDatatable')).to.eql(false);
await PageObjects.lens.switchToVisualization('lnsDatatable');
expect(await PageObjects.lens.getDatatableHeaderText()).to.eql('@timestamp per 3 hours');
expect(await PageObjects.lens.getDatatableCellText(0, 0)).to.eql('2015-09-20 00:00');
expect(await PageObjects.lens.getDatatableHeaderText(1)).to.eql('Average of bytes');
expect(await PageObjects.lens.getDatatableCellText(0, 1)).to.eql('6,011.351');
});
});
}

View file

@ -176,9 +176,26 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
*/
async hasChartSwitchWarning(subVisualizationId: string) {
await this.openChartSwitchPopover();
const element = await testSubjects.find(`lnsChartSwitchPopover_${subVisualizationId}`);
return await testSubjects.descendantExists('euiKeyPadMenuItem__betaBadgeWrapper', element);
return await find.descendantExistsByCssSelector(
'.euiKeyPadMenuItem__betaBadgeWrapper',
element
);
},
/**
* Uses the Lens layer switcher to switch seriesType for xy charts.
*
* @param subVisualizationId - the ID of the sub-visualization to switch to, such as
* line,
*/
async switchLayerSeriesType(seriesType: string) {
await retry.try(async () => {
await testSubjects.click('lns_layer_settings');
await testSubjects.exists(`lnsXY_seriesType-${seriesType}`);
});
return await testSubjects.click(`lnsXY_seriesType-${seriesType}`);
},
/**
@ -205,5 +222,60 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
await PageObjects.header.waitUntilLoadingHasFinished();
await testSubjects.missingOrFail('lnsApp_saveAndReturnButton');
},
/**
* Gets label of dimension trigger in dimension panel
*
* @param dimension - the selector of the dimension
*/
async getDimensionTriggerText(dimension: string, index = 0) {
const dimensionElements = await testSubjects.findAll(dimension);
const trigger = await testSubjects.findDescendant(
'lns-dimensionTrigger',
dimensionElements[index]
);
return await trigger.getVisibleText();
},
/**
* Gets text of the specified datatable header cell
*
* @param index - index of th element in datatable
*/
async getDatatableHeaderText(index = 0) {
return find
.byCssSelector(
`[data-test-subj="lnsDataTable"] thead th:nth-child(${
index + 1
}) .euiTableCellContent__text`
)
.then((el) => el.getVisibleText());
},
/**
* Gets text of the specified datatable cell
*
* @param rowIndex - index of row of the cell
* @param colIndex - index of column of the cell
*/
async getDatatableCellText(rowIndex = 0, colIndex = 0) {
return find
.byCssSelector(
`[data-test-subj="lnsDataTable"] tr:nth-child(${rowIndex + 1}) td:nth-child(${
colIndex + 1
})`
)
.then((el) => el.getVisibleText());
},
/**
* Asserts that metric has expected title and count
*
* @param title - expected title
* @param count - expected count of metric
*/
async assertMetric(title: string, count: string) {
await this.assertExactText('[data-test-subj="lns_metric_title"]', title);
await this.assertExactText('[data-test-subj="lns_metric_value"]', count);
},
});
}