Expand coverage of dashboard tests (#17703)

* Expand coverage of dashboard tests and decrease time

* Fix timing error when sub urls fail to save from too fast app link clicking

* discover doesn't have breadcrumbs

* Check top nav text so it works on both listing and saved object edit/view pages

* need to do the add panel operations one at a time

* Need both types of input in filter

* Give test data a title

* Remove incorrect and unnecessary comment

* Move data around and get rid of 6_3 specific naming as we will end up migrating the data as we progress

* Remove code accidentally checked in
This commit is contained in:
Stacey Gammon 2018-05-01 09:12:36 -04:00 committed by GitHub
parent c10dc7560a
commit 3c8c23c9ef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
50 changed files with 2169 additions and 663 deletions

View file

@ -10,13 +10,16 @@ exports[`DashboardPanel matches snapshot 1`] = `
>
<div
class="panel-heading"
data-test-subj="dashboardPanelHeading-myembeddabletitle"
>
<span
aria-label="Dashboard panel: "
aria-label="Dashboard panel: my embeddable title"
class="panel-title"
data-test-subj="dashboardPanelTitle"
title=""
/>
title="my embeddable title"
>
my embeddable title
</span>
<div
class="kuiMicroButtonGroup"
>

View file

@ -11,6 +11,7 @@ import {
updateViewMode,
setPanels,
updateTimeRange,
embeddableIsInitialized,
} from '../actions';
import { Provider } from 'react-redux';
@ -37,6 +38,8 @@ beforeAll(() => {
store.dispatch(updateTimeRange({ to: 'now', from: 'now-15m' }));
store.dispatch(updateViewMode(DashboardViewMode.EDIT));
store.dispatch(setPanels({ 'foo1': { panelIndex: 'foo1' } }));
const metadata = { title: 'my embeddable title', editUrl: 'editme' };
store.dispatch(embeddableIsInitialized({ metadata, panelId: 'foo1' }));
});
test('DashboardPanel matches snapshot', () => {

View file

@ -13,7 +13,7 @@ export function PanelHeader({ title, actions, isViewOnlyMode, hidePanelTitles })
}
return (
<div className="panel-heading">
<div className="panel-heading" data-test-subj={`dashboardPanelHeading-${title.replace(/\s/g, '')}`}>
<span
data-test-subj="dashboardPanelTitle"
className="panel-title"

View file

@ -2,7 +2,7 @@
ng-switch="mode"
ng-init="mode = 'visualization'"
>
<h2 class="kuiLocalDropdownTitle">
<h2 class="kuiLocalDropdownTitle" data-test-subj="dashboardAddPanel">
Add Panels
</h2>

View file

@ -2,7 +2,7 @@ import React from 'react';
function NoDataComponent() {
return (
<div className="metrics_issue">
<div className="metrics_issue" data-test-subj="noTSVBDataMessage">
<div className="metrics_issue__title">No data to display for the selected metrics .</div>
</div>
);

View file

@ -34,7 +34,7 @@ function MarkdownVisualization(props) {
if (reversed) className += ' reversed';
const markdownError = markdownSource instanceof Error ? markdownSource : null;
markdown = (
<div className={className}>
<div className={className} data-test-subj="tsvbMarkdown">
{markdownError && <ErrorComponent error={markdownError} />}
<style type="text/css">{model.markdown_css}</style>
<div className={contentClassName}>

View file

@ -135,7 +135,13 @@ class Metric extends Component {
<div ref={(el) => this.inner = el} className="rhythm_metric__inner" style={styles.inner}>
<div className="rhythm_metric__primary">
{ primaryLabel }
<div style={styles.primary_value} className="rhythm_metric__primary-value">{ primaryValue }</div>
<div
style={styles.primary_value}
data-test-subj="tsvbMetricValue"
className="rhythm_metric__primary-value"
>
{ primaryValue }
</div>
</div>
{ secondarySnippet }
{additionalLabel}

View file

@ -47,7 +47,7 @@ class TopN extends Component {
style={styles.innerBar}
/>
</td>
<td width="1*" className="rhythm_top_n__value">{ value }</td>
<td width="1*" className="rhythm_top_n__value" data-test-subj="tsvbTopNValue">{ value }</td>
</tr>
);
};

View file

@ -17,6 +17,7 @@ export default props => (row, i) => {
<div
key={key}
className={classes.join(' ')}
data-test-subj="tsvbLegendItem"
>
<button
onClick={event => props.onToggle(event, row.id)}

View file

@ -16,6 +16,7 @@ export class TagCloudVisualization {
const cloudContainer = document.createElement('div');
cloudContainer.classList.add('tagcloud-vis');
cloudContainer.setAttribute('data-test-subj', 'tagCloudVisualization');
this._containerNode.appendChild(cloudContainer);
this._vis = vis;

View file

@ -7,6 +7,7 @@
</div>
<button
data-test-subj="filterEditorModalCloseButton"
class="kuiModalHeaderCloseButton kuiIcon fa-times"
ng-click="filterEditor.onCancel()"
aria-label="Close filter popover"

View file

@ -2010,7 +2010,7 @@ Licensed under the MIT license.
ctx.lineTo(xrange.to + subPixel, yrange.to);
} else {
ctx.moveTo(xrange.from, yrange.to + subPixel);
ctx.lineTo(xrange.to, yrange.to + subPixel);
ctx.lineTo(xrange.to, yrange.to + subPixel);
}
ctx.stroke();
} else {
@ -2525,9 +2525,9 @@ Licensed under the MIT license.
radius = series.points.radius,
symbol = series.points.symbol;
// If the user sets the line width to 0, we change it to a very
// If the user sets the line width to 0, we change it to a very
// small value. A line width of 0 seems to force the default of 1.
// Doing the conditional here allows the shadow setting to still be
// Doing the conditional here allows the shadow setting to still be
// optional even with a lineWidth of 0.
if( lw == 0 )
@ -2771,7 +2771,7 @@ Licensed under the MIT license.
fragments.push(
'<td class="legendColorBox"><div style="border:1px solid ' + options.legend.labelBoxBorderColor + ';padding:1px"><div style="width:4px;height:0;border:5px solid ' + entry.color + ';overflow:hidden"></div></div></td>' +
'<td class="legendLabel">' + entry.label + '</td>'
'<td class="legendLabel" data-test-subj="flotLegendLabel">' + entry.label + '</td>'
);
}

View file

@ -65,6 +65,7 @@
<li ng-style="{'visibility':'hidden'}" ng-if="page.last">
<button
class="euiButtonEmpty euiButtonEmpty--text euiButtonEmpty--small euiPaginationButton"
data-test-subj="paginateNext"
ng-click="paginate.goToPage(page.next)"
>
<span class="euiButtonEmpty__content">»</span>
@ -73,6 +74,7 @@
<li ng-style="{'visibility':'visible'}" ng-if="!page.last">
<button
class="euiButtonEmpty euiButtonEmpty--text euiButtonEmpty--small euiPaginationButton"
data-test-subj="paginateNext"
ng-click="paginate.goToPage(page.next)"
>
<span class="euiButtonEmpty__content">»</span>

View file

@ -0,0 +1,46 @@
import expect from 'expect.js';
import {
VisualizeConstants
} from '../../../../src/core_plugins/kibana/public/visualize/visualize_constants';
export default function ({ getService, getPageObjects }) {
const retry = getService('retry');
const PageObjects = getPageObjects(['dashboard', 'header', 'visualize']);
const remote = getService('remote');
const dashboardAddPanel = getService('dashboardAddPanel');
describe('create and add embeddables', async () => {
before(async () => {
await PageObjects.dashboard.loadSavedDashboard('few panels');
});
describe('add new visualization link', () => {
it('adds a new visualization', async () => {
const originalPanelCount = await PageObjects.dashboard.getPanelCount();
await PageObjects.dashboard.clickEdit();
await dashboardAddPanel.ensureAddPanelIsShowing();
await dashboardAddPanel.clickAddNewEmbeddableLink();
await PageObjects.visualize.clickAreaChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.visualize.saveVisualization('visualization from add new link');
return retry.try(async () => {
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(originalPanelCount + 1);
});
});
it('saves the saved visualization url to the app link', async () => {
await PageObjects.header.clickVisualize();
const currentUrl = await remote.getCurrentUrl();
expect(currentUrl).to.contain(VisualizeConstants.EDIT_PATH);
});
after(async () => {
await PageObjects.header.clickDashboard();
});
});
});
}

View file

@ -1,266 +0,0 @@
import expect from 'expect.js';
import {
VisualizeConstants
} from '../../../../src/core_plugins/kibana/public/visualize/visualize_constants';
export default function ({ getService, getPageObjects }) {
const retry = getService('retry');
const log = getService('log');
const dashboardVisualizations = getService('dashboardVisualizations');
const remote = getService('remote');
const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'visualize', 'discover']);
const testVisualizationTitles = [];
const testVisualizationDescriptions = [];
describe('dashboard tab', function describeIndexTests() {
before(async function () {
await PageObjects.dashboard.initTests();
await PageObjects.dashboard.preserveCrossAppState();
});
after(async function () {
// avoids any 'Object with id x not found' errors when switching tests.
await PageObjects.header.clickVisualize();
await PageObjects.visualize.gotoLandingPage();
await PageObjects.header.clickDashboard();
await PageObjects.dashboard.gotoDashboardLandingPage();
});
it('should be able to add visualizations to dashboard', async function addVisualizations() {
await PageObjects.dashboard.clickNewDashboard();
await dashboardVisualizations.createAndAddTSVBVisualization('TSVB');
await PageObjects.dashboard.addVisualizations(PageObjects.dashboard.getTestVisualizationNames());
await dashboardVisualizations.createAndAddSavedSearch({ name: 'saved search', fields: ['bytes', 'agent'] });
testVisualizationTitles.push('TSVB');
testVisualizationTitles.splice(1, 0, ...PageObjects.dashboard.getTestVisualizationNames());
testVisualizationTitles.push('saved search');
testVisualizationDescriptions.push('');
testVisualizationDescriptions.splice(
1, 0, ...PageObjects.dashboard.getTestVisualizations().map(visualization => visualization.description)
);
testVisualizationDescriptions.push('');
});
it('set the timepicker time to that which contains our test data', async function setTimepicker() {
await PageObjects.dashboard.setTimepickerInDataRange();
});
it('saved search loaded with columns', async () => {
const headers = await PageObjects.discover.getColumnHeaders();
expect(headers.length).to.be(3);
expect(headers[1]).to.be('bytes');
expect(headers[2]).to.be('agent');
});
it('should save and load dashboard', async function saveAndLoadDashboard() {
const dashboardName = 'Dashboard Test 1';
await PageObjects.dashboard.saveDashboard(dashboardName);
await PageObjects.dashboard.gotoDashboardLandingPage();
await retry.try(function () {
log.debug('now re-load previously saved dashboard');
return PageObjects.dashboard.loadSavedDashboard(dashboardName);
});
});
it('should have all the expected visualizations', function checkVisualizations() {
return retry.tryForTime(10000, function () {
return PageObjects.dashboard.getPanelTitles()
.then(function (panelTitles) {
expect(panelTitles).to.eql(testVisualizationTitles);
});
})
.then(function () {
});
});
it('retains dark theme in state', async function () {
await PageObjects.dashboard.clickEdit();
await PageObjects.dashboard.useDarkTheme(true);
await PageObjects.header.clickVisualize();
await PageObjects.header.clickDashboard();
const isDarkThemeOn = await PageObjects.dashboard.isDarkThemeOn();
expect(isDarkThemeOn).to.equal(true);
});
it('should be able to hide all panel titles', async function () {
await PageObjects.dashboard.checkHideTitle();
await retry.tryForTime(10000, async function () {
const titles = await PageObjects.dashboard.getPanelTitles();
expect(titles[0]).to.eql('');
});
});
it('should be able to unhide all panel titles', async function () {
await PageObjects.dashboard.checkHideTitle();
await retry.tryForTime(10000, async function () {
const titles = await PageObjects.dashboard.getPanelTitles();
expect(titles[0]).to.eql('TSVB');
});
});
describe('expanding a panel', () => {
it('hides other panels', async () => {
// Don't expand TSVB since it doesn't have the spy panel.
const panels = await PageObjects.dashboard.getDashboardPanels();
await PageObjects.dashboard.toggleExpandPanel(panels[1]);
await retry.try(async () => {
const panels = await PageObjects.dashboard.getDashboardPanels();
expect(panels.length).to.eql(1);
});
});
it('does not show the spy pane toggle if mouse is not hovering', async () => {
// move mouse off the panel.
await PageObjects.header.clickTimepicker();
await PageObjects.header.clickTimepicker();
// no spy pane without hover
const spyToggleExists = await PageObjects.visualize.getSpyToggleExists();
expect(spyToggleExists).to.be(false);
});
it('shows the spy pane toggle on hover', async () => {
const panels = await PageObjects.dashboard.getDashboardPanels();
// Simulate hover
await remote.moveMouseTo(panels[0]);
const spyToggleExists = await PageObjects.visualize.getSpyToggleExists();
expect(spyToggleExists).to.be(true);
});
// This was an actual bug that appeared, where the spy pane appeared on panels after adding them, but
// disappeared when a new dashboard was opened up.
it('shows the spy pane toggle directly after opening a dashboard', async () => {
await PageObjects.dashboard.saveDashboard('spy pane test');
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.dashboard.loadSavedDashboard('spy pane test');
const panels = await PageObjects.dashboard.getDashboardPanels();
// Simulate hover
await remote.moveMouseTo(panels[1]);
const spyToggleExists = await PageObjects.visualize.getSpyToggleExists();
expect(spyToggleExists).to.be(true);
});
it('shows other panels after being minimized', async () => {
// Panels are all minimized on a fresh open of a dashboard, so we need to re-expand in order to then minimize.
await PageObjects.dashboard.toggleExpandPanel();
await PageObjects.dashboard.toggleExpandPanel();
// Add a retry to fix https://github.com/elastic/kibana/issues/14574. Perhaps the recent changes to this
// being a CSS update is causing the UI to change slower than grabbing the panels?
retry.try(async () => {
const panels = await PageObjects.dashboard.getDashboardPanels();
expect(panels.length).to.eql(testVisualizationTitles.length);
});
});
});
describe('embed mode', () => {
it('hides the chrome', async () => {
let isChromeVisible = await PageObjects.common.isChromeVisible();
expect(isChromeVisible).to.be(true);
const currentUrl = await remote.getCurrentUrl();
const newUrl = currentUrl + '&embed=true';
// Embed parameter only works on a hard refresh.
const useTimeStamp = true;
await remote.get(newUrl.toString(), useTimeStamp);
await retry.try(async () => {
isChromeVisible = await PageObjects.common.isChromeVisible();
expect(isChromeVisible).to.be(false);
});
});
after(async function () {
console.log('showing chrome again');
const currentUrl = await remote.getCurrentUrl();
const newUrl = currentUrl.replace('&embed=true', '');
// First use the timestamp to cause a hard refresh so the new embed parameter works correctly.
let useTimeStamp = true;
await remote.get(newUrl.toString(), useTimeStamp);
// Then get rid of the timestamp so the rest of the tests work with state and app switching.
useTimeStamp = false;
await remote.get(newUrl.toString(), useTimeStamp);
});
});
describe('full screen mode', () => {
it('option not available in edit mode', async () => {
await PageObjects.dashboard.clickEdit();
const exists = await PageObjects.dashboard.fullScreenModeMenuItemExists();
expect(exists).to.be(false);
});
it('available in view mode', async () => {
await PageObjects.dashboard.saveDashboard('full screen test');
const exists = await PageObjects.dashboard.fullScreenModeMenuItemExists();
expect(exists).to.be(true);
});
it('hides the chrome', async () => {
let isChromeVisible = await PageObjects.common.isChromeVisible();
expect(isChromeVisible).to.be(true);
await PageObjects.dashboard.clickFullScreenMode();
await retry.try(async () => {
isChromeVisible = await PageObjects.common.isChromeVisible();
expect(isChromeVisible).to.be(false);
});
});
it('displays exit full screen logo button', async () => {
const exists = await PageObjects.dashboard.exitFullScreenLogoButtonExists();
expect(exists).to.be(true);
});
it('displays exit full screen logo button when panel is expanded', async () => {
await PageObjects.dashboard.toggleExpandPanel();
const exists = await PageObjects.dashboard.exitFullScreenTextButtonExists();
expect(exists).to.be(true);
});
it('exits when the text button is clicked on', async () => {
const logoButton = await PageObjects.dashboard.getExitFullScreenLogoButton();
await remote.moveMouseTo(logoButton);
await PageObjects.dashboard.clickExitFullScreenTextButton();
await retry.try(async () => {
const isChromeVisible = await PageObjects.common.isChromeVisible();
expect(isChromeVisible).to.be(true);
});
});
});
describe('add new visualization link', () => {
it('adds a new visualization', async () => {
await PageObjects.dashboard.clickEdit();
await PageObjects.dashboard.clickAddVisualization();
await PageObjects.dashboard.clickAddNewVisualizationLink();
await PageObjects.visualize.clickAreaChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.visualize.saveVisualization('visualization from add new link');
return retry.try(async () => {
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(testVisualizationTitles.length + 1);
});
});
it('saves the saved visualization url to the app link', async () => {
await PageObjects.header.clickVisualize();
const currentUrl = await remote.getCurrentUrl();
expect(currentUrl).to.contain(VisualizeConstants.EDIT_PATH);
});
after(async () => {
await PageObjects.header.clickDashboard();
});
});
});
}

View file

@ -1,42 +1,105 @@
import { PIE_CHART_VIS_NAME } from '../../page_objects/dashboard_page';
import expect from 'expect.js';
export default function ({ getService, getPageObjects }) {
const dashboardExpect = getService('dashboardExpect');
const dashboardVisualizations = getService('dashboardVisualizations');
const dashboardAddPanel = getService('dashboardAddPanel');
const testSubjects = getService('testSubjects');
const filterBar = getService('filterBar');
const PageObjects = getPageObjects(['dashboard', 'header', 'visualize']);
describe('dashboard filter bar', function describeIndexTests() {
before(async function () {
await PageObjects.dashboard.initTests();
await PageObjects.dashboard.preserveCrossAppState();
});
after(async function () {
// avoids any 'Object with id x not found' errors when switching tests.
await PageObjects.header.clickVisualize();
await PageObjects.visualize.gotoLandingPage();
await PageObjects.header.clickDashboard();
describe('dashboard filter bar', async () => {
before(async () => {
await PageObjects.dashboard.gotoDashboardLandingPage();
});
it('Filter bar field list uses default index pattern on an empty dashboard', async () => {
await PageObjects.dashboard.clickNewDashboard();
await testSubjects.click('addFilter');
await dashboardExpect.fieldSuggestionIndexPatterns(['logstash-*']);
describe('Add a filter bar', async function () {
before(async () => {
await PageObjects.dashboard.gotoDashboardLandingPage();
});
it('should show on an empty dashboard', async function () {
await PageObjects.dashboard.clickNewDashboard();
const hasAddFilter = await testSubjects.exists('addFilter');
expect(hasAddFilter).to.be(true);
});
it ('should continue to show for visualizations with no search source', async () => {
await dashboardAddPanel.addVisualization('input control');
const hasAddFilter = await testSubjects.exists('addFilter');
expect(hasAddFilter).to.be(true);
});
});
// TODO: Use a data set that has more than one index pattern to better test this.
it('Filter bar field list shows index pattern of vis when one is added', async () => {
await PageObjects.dashboard.addVisualizations([PIE_CHART_VIS_NAME]);
await testSubjects.click('filterfieldSuggestionList');
await dashboardExpect.fieldSuggestionIndexPatterns(['logstash-*']);
describe('filter editor field list', async () => {
before(async () => {
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.dashboard.clickNewDashboard();
});
it('uses default index pattern on an empty dashboard', async () => {
await testSubjects.click('addFilter');
await dashboardExpect.fieldSuggestionIndexPatterns(['logstash-*']);
});
it('shows index pattern of vis when one is added', async () => {
await dashboardAddPanel.addVisualization('animal sounds pie');
await PageObjects.header.waitUntilLoadingHasFinished();
await filterBar.ensureFieldEditorModalIsClosed();
await testSubjects.click('addFilter');
await dashboardExpect.fieldSuggestionIndexPatterns(['animals-*']);
});
it('works when a vis with no index pattern is added', async () => {
await dashboardAddPanel.addVisualization('markdown');
await PageObjects.header.waitUntilLoadingHasFinished();
await filterBar.ensureFieldEditorModalIsClosed();
await testSubjects.click('addFilter');
await dashboardExpect.fieldSuggestionIndexPatterns(['animals-*']);
});
});
it('Filter bar field list works when a vis with no index pattern is added', async () => {
await dashboardVisualizations.createAndAddMarkdown({ name: 'markdown', markdown: 'hi ima markdown' });
await testSubjects.click('addFilter');
await dashboardExpect.fieldSuggestionIndexPatterns(['logstash-*']);
describe('filter pills', async function () {
before(async () => {
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.setTimepickerIn63DataRange();
});
it('are not selected by default', async function () {
const filters = await PageObjects.dashboard.getFilters(1000);
expect(filters.length).to.equal(0);
});
it('are added when a pie chart slice is clicked', async function () {
await dashboardAddPanel.addVisualization('Rendering Test: pie');
await PageObjects.dashboard.waitForRenderComplete();
await PageObjects.dashboard.filterOnPieSlice('4,886');
const filters = await PageObjects.dashboard.getFilters();
expect(filters.length).to.equal(1);
await dashboardExpect.pieSliceCount(1);
});
it('are preserved after saving a dashboard', async () => {
await PageObjects.dashboard.saveDashboard('with filters');
await PageObjects.header.waitUntilLoadingHasFinished();
const filters = await PageObjects.dashboard.getFilters();
expect(filters.length).to.equal(1);
await dashboardExpect.pieSliceCount(1);
});
it('are preserved after opening a dashboard saved with filters', async () => {
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.dashboard.loadSavedDashboard('with filters');
await PageObjects.header.waitUntilLoadingHasFinished();
const filters = await PageObjects.dashboard.getFilters();
expect(filters.length).to.equal(1);
await dashboardExpect.pieSliceCount(1);
});
});
});
}

View file

@ -0,0 +1,198 @@
import expect from 'expect.js';
/**
* Test the querying capabilities of dashboard, and make sure visualizations show the expected results, especially
* with nested queries and filters on the visualizations themselves.
*/
export default function ({ getService, getPageObjects }) {
const dashboardExpect = getService('dashboardExpect');
const dashboardAddPanel = getService('dashboardAddPanel');
const testSubjects = getService('testSubjects');
const filterBar = getService('filterBar');
const PageObjects = getPageObjects(['dashboard', 'header', 'visualize']);
describe('dashboard filtering', async () => {
before(async () => {
await PageObjects.dashboard.gotoDashboardLandingPage();
});
describe('adding a filter that excludes all data', async () => {
before(async () => {
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.setTimepickerIn63DataRange();
await dashboardAddPanel.addEveryVisualization('"Filter Bytes Test"');
await dashboardAddPanel.addEverySavedSearch('"Filter Bytes Test"');
await dashboardAddPanel.closeAddPanel();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.waitForRenderComplete();
await filterBar.addFilter('bytes', 'is', '12345678', 'kuiTextInput');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.waitForRenderComplete();
});
it('filters on pie charts', async () => {
await dashboardExpect.pieSliceCount(0);
});
it('area, bar and heatmap charts filtered', async () => {
await dashboardExpect.seriesElementCount(0);
});
it('data tables are filtered', async () => {
await dashboardExpect.dataTableRowCount(0);
});
it('goal and guages are filtered', async () => {
await dashboardExpect.goalAndGuageLabelsExist(['0', '0%']);
});
it('tsvb time series shows no data message', async () => {
expect(await testSubjects.exists('noTSVBDataMessage')).to.be(true);
await dashboardExpect.tsvbTimeSeriesLegendCount(0);
});
it('metric value shows no data', async () => {
await dashboardExpect.metricValuesExist(['-']);
});
it('tag cloud values are filtered', async () => {
await dashboardExpect.emptyTagCloudFound();
});
it('tsvb metric is filtered', async () => {
await dashboardExpect.tsvbMetricValuesExist(['0 custom template']);
});
it('tsvb top n is filtered', async () => {
await dashboardExpect.tsvbTopNValuesExist(['0', '0']);
});
it('saved search is filtered', async () => {
await dashboardExpect.savedSearchRowCount(0);
});
it('timelion is filtered', async () => {
await dashboardExpect.timelionLegendCount(0);
});
it('vega is filtered', async () => {
await dashboardExpect.vegaTextsDoNotExist(['5,000']);
});
});
describe('disabling a filter unfilters the data on', async () => {
before(async () => {
await testSubjects.click('disableFilter-bytes');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.waitForRenderComplete();
});
it('pie charts', async () => {
await dashboardExpect.pieSliceCount(5);
});
it('area, bar and heatmap charts', async () => {
await dashboardExpect.seriesElementCount(3);
});
it('data tables', async () => {
await dashboardExpect.dataTableRowCount(10);
});
it('goal and guages', async () => {
await dashboardExpect.goalAndGuageLabelsExist(['40%', '7,544']);
});
it('tsvb time series', async () => {
expect(await testSubjects.exists('noTSVBDataMessage')).to.be(false);
await dashboardExpect.tsvbTimeSeriesLegendCount(10);
});
it('metric value', async () => {
await dashboardExpect.metricValuesExist(['101']);
});
it('tag cloud', async () => {
await dashboardExpect.tagCloudWithValuesFound(['9,972', '4,886', '1,944', '9,025']);
});
it('tsvb metric', async () => {
await dashboardExpect.tsvbMetricValuesExist(['50,465 custom template']);
});
it('tsvb top n', async () => {
await dashboardExpect.tsvbTopNValuesExist(['6,308.13', '6,308.13']);
});
it('tsvb markdown', async () => {
await dashboardExpect.tsvbMarkdownWithValuesExists(['7,209.29']);
});
it('saved searches', async () => {
await dashboardExpect.savedSearchRowCount(1);
});
it('vega', async () => {
await dashboardExpect.vegaTextsExist(['5,000']);
});
});
describe('nested filtering', async () => {
before(async () => {
await PageObjects.dashboard.gotoDashboardLandingPage();
});
it('visualization saved with a query filters data', async () => {
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.setTimepickerIn63DataRange();
await dashboardAddPanel.addVisualization('animal sounds pie');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.waitForRenderComplete();
await dashboardExpect.pieSliceCount(5);
await PageObjects.dashboard.clickEditVisualization();
await PageObjects.dashboard.setQuery('weightLbs:>50');
await PageObjects.dashboard.clickFilterButton();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.waitForRenderComplete();
await dashboardExpect.pieSliceCount(3);
await PageObjects.visualize.saveVisualization('animal sounds pie');
await PageObjects.header.clickDashboard();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.waitForRenderComplete();
await dashboardExpect.pieSliceCount(3);
});
it('Nested visualization filter pills filters data as expected', async () => {
await PageObjects.dashboard.clickEditVisualization();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.waitForRenderComplete();
await PageObjects.dashboard.filterOnPieSlice('grr');
await PageObjects.header.waitUntilLoadingHasFinished();
await dashboardExpect.pieSliceCount(1);
await PageObjects.visualize.saveVisualization('animal sounds pie');
await PageObjects.header.clickDashboard();
await dashboardExpect.pieSliceCount(1);
});
it('Pie chart linked to saved search filters data', async () => {
await dashboardAddPanel.addVisualization('Filter Test: animals: linked to search with filter');
await dashboardExpect.pieSliceCount(3);
});
it('Pie chart linked to saved search filters shows no data with conflicting dashboard query', async () => {
await PageObjects.dashboard.setQuery('weightLbs:<40');
await PageObjects.dashboard.clickFilterButton();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.waitForRenderComplete();
await dashboardExpect.pieSliceCount(0);
});
});
});
}

View file

@ -1,51 +1,21 @@
import expect from 'expect.js';
export default function ({ getService, getPageObjects }) {
const find = getService('find');
const remote = getService('remote');
const PageObjects = getPageObjects(['dashboard', 'header', 'common']);
const VIS_TITLES = [
PageObjects.dashboard.getTestVisualizationNames()[0],
PageObjects.dashboard.getTestVisualizationNames()[1],
PageObjects.dashboard.getTestVisualizationNames()[2],
];
// Order returned by find.allByCssSelector is not guaranteed and can change based on timing
// Use this function to avoid looking for elements by hard-coded array index.
const getPanelTitleElement = async (title) => {
const panelTitleElements = await find.allByCssSelector('.panel-title');
for (let i = 0; i < panelTitleElements.length; i++) {
const panelText = await panelTitleElements[i].getVisibleText();
if (panelText === title) {
return panelTitleElements[i];
}
}
throw new Error(`Unable to find panel with title: "${title}"`);
};
const PageObjects = getPageObjects(['dashboard']);
describe('dashboard grid', () => {
before(async () => {
return PageObjects.dashboard.initTests();
});
after(async () => {
// avoids any 'Object with id x not found' errors when switching tests.
await PageObjects.header.clickDashboard();
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.dashboard.loadSavedDashboard('few panels');
await PageObjects.dashboard.clickEdit();
});
describe('move panel', () => {
// Specific test after https://github.com/elastic/kibana/issues/14764 fix
it('Can move panel from bottom to top row', async () => {
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.addVisualizations(VIS_TITLES);
const lastVisTitle = VIS_TITLES[VIS_TITLES.length - 1];
const panelTitleBeforeMove = await getPanelTitleElement(lastVisTitle);
const lastVisTitle = 'Rendering Test: datatable';
const panelTitleBeforeMove = await PageObjects.dashboard.getPanelHeading(lastVisTitle);
const position1 = await panelTitleBeforeMove.getPosition();
remote
@ -54,7 +24,7 @@ export default function ({ getService, getPageObjects }) {
.moveMouseTo(null, -20, -450)
.releaseMouseButton();
const panelTitleAfterMove = await getPanelTitleElement(lastVisTitle);
const panelTitleAfterMove = await PageObjects.dashboard.getPanelHeading(lastVisTitle);
const position2 = await panelTitleAfterMove.getPosition();
expect(position1.y).to.be.greaterThan(position2.y);

View file

@ -0,0 +1,32 @@
import expect from 'expect.js';
export default function ({ getService, getPageObjects }) {
const retry = getService('retry');
const PageObjects = getPageObjects(['dashboard']);
describe('dashboard data-shared attributes', async () => {
let originalTitles = [];
before(async () => {
await PageObjects.dashboard.loadSavedDashboard('few panels');
await PageObjects.dashboard.clickEdit();
originalTitles = await PageObjects.dashboard.getPanelTitles();
});
it('should be able to hide all panel titles', async () => {
await PageObjects.dashboard.checkHideTitle();
await retry.try(async () => {
const titles = await PageObjects.dashboard.getPanelTitles();
expect(titles[0]).to.eql('');
});
});
it('should be able to unhide all panel titles', async () => {
await PageObjects.dashboard.checkHideTitle();
await retry.try(async () => {
const titles = await PageObjects.dashboard.getPanelTitles();
expect(titles[0]).to.eql(originalTitles[0]);
});
});
});
}

View file

@ -1,152 +0,0 @@
import expect from 'expect.js';
import { PIE_CHART_VIS_NAME } from '../../page_objects/dashboard_page';
/**
* Test the querying capabilities of dashboard, and make sure visualizations show the expected results, especially
* with nested queries and filters on the visualizations themselves.
*/
export default function ({ getService, getPageObjects }) {
const dashboardExpect = getService('dashboardExpect');
const dashboardVisualizations = getService('dashboardVisualizations');
const testSubjects = getService('testSubjects');
const PageObjects = getPageObjects(['dashboard', 'header', 'visualize']);
describe('dashboard queries', function describeIndexTests() {
before(async function () {
await PageObjects.dashboard.initTests();
await PageObjects.dashboard.preserveCrossAppState();
});
after(async function () {
// avoids any 'Object with id x not found' errors when switching tests.
await PageObjects.header.clickVisualize();
await PageObjects.visualize.gotoLandingPage();
await PageObjects.header.clickDashboard();
await PageObjects.dashboard.gotoDashboardLandingPage();
});
it('Nested visualization query filters data as expected', async () => {
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.setTimepickerInDataRange();
await PageObjects.dashboard.addVisualizations([PIE_CHART_VIS_NAME]);
await PageObjects.dashboard.clickEditVisualization();
await PageObjects.dashboard.setQuery('memory:<80000');
await PageObjects.dashboard.clickFilterButton();
await PageObjects.header.waitUntilLoadingHasFinished();
await dashboardExpect.pieSliceCount(2);
await PageObjects.visualize.saveVisualization(PIE_CHART_VIS_NAME);
await PageObjects.header.clickDashboard();
await dashboardExpect.pieSliceCount(2);
});
it('Nested visualization filter pills filters data as expected', async () => {
await PageObjects.dashboard.clickEditVisualization();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.waitForRenderComplete();
await PageObjects.dashboard.filterOnPieSlice();
await PageObjects.header.waitUntilLoadingHasFinished();
await dashboardExpect.pieSliceCount(1);
await PageObjects.visualize.saveVisualization(PIE_CHART_VIS_NAME);
await PageObjects.header.clickDashboard();
await dashboardExpect.pieSliceCount(1);
});
it('Pie chart attached to saved search filters data as expected', async () => {
await dashboardVisualizations.createAndAddSavedSearch({
name: 'bytes < 90',
query: 'bytes:<90',
fields: ['bytes']
});
await PageObjects.header.clickDashboard();
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.clickAddVisualization();
await PageObjects.dashboard.clickAddNewVisualizationLink();
await PageObjects.visualize.clickPieChart();
await PageObjects.visualize.selectSearch('bytes < 90');
await PageObjects.visualize.clickBucket('Split Slices');
await PageObjects.visualize.selectAggregation('Terms');
await PageObjects.visualize.selectField('memory');
await PageObjects.visualize.clickGo();
await PageObjects.visualize.saveVisualization('memory with bytes < 90 pie');
await dashboardExpect.pieSliceCount(3);
});
it('Pie chart attached to saved search filters shows no data with conflicting dashboard query', async () => {
await PageObjects.dashboard.setQuery('bytes:>100');
await PageObjects.dashboard.clickFilterButton();
await PageObjects.header.waitUntilLoadingHasFinished();
await dashboardExpect.pieSliceCount(0);
});
describe('visualizations without SearchSource', async function () {
before(async () => {
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.addVisualizations(['Visualization InputControl']);
});
it(`should have filter bar with 'Add a filter'`, async function () {
const hasAddFilter = await testSubjects.exists('addFilter');
expect(hasAddFilter).to.be(true);
});
});
describe('filters', async function () {
before(async () => {
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.dashboard.clickNewDashboard();
});
it('are not selected by default', async function () {
const filters = await PageObjects.dashboard.getFilters(1000);
expect(filters.length).to.equal(0);
});
it('are added when a pie chart slice is clicked', async function () {
await PageObjects.dashboard.addVisualizations([PIE_CHART_VIS_NAME]);
// Click events not added until visualization is finished rendering.
// See https://github.com/elastic/kibana/issues/15480#issuecomment-350195245 for more info on why
// this is necessary.
await PageObjects.dashboard.waitForRenderComplete();
await PageObjects.dashboard.filterOnPieSlice();
const filters = await PageObjects.dashboard.getFilters();
expect(filters.length).to.equal(1);
await dashboardExpect.pieSliceCount(1);
});
it('are preserved after saving a dashboard', async () => {
await PageObjects.dashboard.saveDashboard('with filters');
await PageObjects.header.waitUntilLoadingHasFinished();
const filters = await PageObjects.dashboard.getFilters();
expect(filters.length).to.equal(1);
await dashboardExpect.pieSliceCount(1);
});
it('are preserved after opening a dashboard saved with filters', async () => {
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.dashboard.loadSavedDashboard('with filters');
await PageObjects.header.waitUntilLoadingHasFinished();
const filters = await PageObjects.dashboard.getFilters();
expect(filters.length).to.equal(1);
await dashboardExpect.pieSliceCount(1);
});
});
});
}

View file

@ -10,6 +10,7 @@ export default function ({ getService, getPageObjects }) {
const testSubjects = getService('testSubjects');
const remote = getService('remote');
const retry = getService('retry');
const dashboardAddPanel = getService('dashboardAddPanel');
describe('dashboard state', function describeIndexTests() {
before(async function () {
@ -27,7 +28,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.setTimepickerInDataRange();
await PageObjects.dashboard.addVisualizations([AREA_CHART_VIS_NAME]);
await dashboardAddPanel.addVisualization(AREA_CHART_VIS_NAME);
await PageObjects.dashboard.saveDashboard('Overridden colors');
await PageObjects.dashboard.clickEdit();
@ -59,7 +60,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.header.clickDashboard();
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.addSavedSearch('my search');
await dashboardAddPanel.addSavedSearch('my search');
await PageObjects.dashboard.saveDashboard('No local edits');
const inViewMode = await testSubjects.exists('dashboardEditMode');
@ -103,7 +104,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.setTimepickerInDataRange();
await PageObjects.dashboard.addVisualizations(['Visualization TileMap']);
await dashboardAddPanel.addVisualization('Visualization TileMap');
await PageObjects.dashboard.saveDashboard('No local edits');
await testSubjects.moveMouseTo('dashboardPanel');
@ -129,10 +130,17 @@ export default function ({ getService, getPageObjects }) {
const changedTileMapData = await PageObjects.visualize.getDataTableData();
await testSubjects.moveMouseTo('dashboardPanel');
await PageObjects.visualize.closeSpyPanel();
expect(changedTileMapData.length).to.not.equal(tileMapData.length);
});
it('retains dark theme', async function () {
await PageObjects.dashboard.useDarkTheme(true);
await PageObjects.header.clickVisualize();
await PageObjects.header.clickDashboard();
const isDarkThemeOn = await PageObjects.dashboard.isDarkThemeOn();
expect(isDarkThemeOn).to.equal(true);
});
describe('Directly modifying url updates dashboard state', () => {
it('for query parameter', async function () {
await PageObjects.dashboard.gotoDashboardLandingPage();
@ -150,7 +158,7 @@ export default function ({ getService, getPageObjects }) {
});
it('for panel size parameters', async function () {
await PageObjects.dashboard.addVisualization(PIE_CHART_VIS_NAME);
await dashboardAddPanel.addVisualization(PIE_CHART_VIS_NAME);
const currentUrl = await remote.getCurrentUrl();
const currentPanelDimensions = await PageObjects.dashboard.getPanelDimensions();
const newUrl = currentUrl.replace(`w:${DEFAULT_PANEL_WIDTH}`, `w:${DEFAULT_PANEL_WIDTH * 2}`);
@ -184,7 +192,7 @@ export default function ({ getService, getPageObjects }) {
describe('for embeddable config color parameters on a visualization', () => {
it('updates a pie slice color on a soft refresh', async function () {
await PageObjects.dashboard.addVisualization(PIE_CHART_VIS_NAME);
await dashboardAddPanel.addVisualization(PIE_CHART_VIS_NAME);
await PageObjects.visualize.clickLegendOption('80,000');
await PageObjects.visualize.selectNewLegendColorChoice('#F9D9F9');
const currentUrl = await remote.getCurrentUrl();

View file

@ -8,7 +8,7 @@ const toTime = '2015-09-23 18:31:44.000';
export default function ({ getPageObjects }) {
const PageObjects = getPageObjects(['dashboard', 'header']);
describe('dashboard time', function dashboardSaveWithTime() {
describe('dashboard time', () => {
before(async function () {
await PageObjects.dashboard.initTests();
await PageObjects.dashboard.preserveCrossAppState();
@ -18,15 +18,15 @@ export default function ({ getPageObjects }) {
await PageObjects.dashboard.gotoDashboardLandingPage();
});
describe('dashboard without stored timed', async function () {
it('is saved', async function () {
describe('dashboard without stored timed', () => {
it('is saved', async () => {
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.addVisualizations([PageObjects.dashboard.getTestVisualizationNames()[0]]);
const isDashboardSaved = await PageObjects.dashboard.saveDashboard(dashboardName, { storeTimeWithDashboard: false });
expect(isDashboardSaved).to.eql(true);
});
it('Does not set the time picker on open', async function () {
it('Does not set the time picker on open', async () => {
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.dashboard.loadSavedDashboard(dashboardName);

View file

@ -2,41 +2,40 @@ import expect from 'expect.js';
export default function ({ getService, getPageObjects }) {
const retry = getService('retry');
const log = getService('log');
const dashboardVisualizations = getService('dashboardVisualizations');
const testSubjects = getService('testSubjects');
const PageObjects = getPageObjects(['dashboard']);
const testVisualizationTitles = [PageObjects.dashboard.getTestVisualizationNames()[0], 'saved search'];
const testVisualizationDescriptions = [PageObjects.dashboard.getTestVisualizationDescriptions()[0], ''];
describe('dashboard shared attributes', function describeIndexTests() {
before(async function () {
await PageObjects.dashboard.initTests();
await PageObjects.dashboard.preserveCrossAppState();
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.addVisualizations([PageObjects.dashboard.getTestVisualizationNames()[0]]);
await dashboardVisualizations.createAndAddSavedSearch({ name: 'saved search', fields: ['bytes', 'agent'] });
describe('dashboard data-shared attributes', function describeIndexTests() {
let originalPanelTitles;
before(async () => {
await PageObjects.dashboard.loadSavedDashboard('dashboard with everything');
});
it('should have data-shared-items-count set to the number of visualizations', function checkSavedItemsCount() {
return retry.tryForTime(10000, () => PageObjects.dashboard.getSharedItemsCount())
.then(function (count) {
log.info('data-shared-items-count = ' + count);
expect(count).to.eql(testVisualizationTitles.length);
});
});
it('should have panels with expected data-shared-item title and description', async () => {
it('should have data-shared-items-count set to the number of embeddables on the dashboard', async () => {
await retry.try(async () => {
await PageObjects.dashboard.getPanelSharedItemData()
.then(function (data) {
expect(data.map(item => item.title)).to.eql(testVisualizationTitles);
expect(data.map(item => item.description)).to.eql(testVisualizationDescriptions);
});
const sharedItemsCount = await PageObjects.dashboard.getSharedItemsCount();
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(sharedItemsCount).to.eql(panelCount);
});
});
it('should have panels with expected data-shared-item title', async () => {
await retry.try(async () => {
const sharedData = await PageObjects.dashboard.getPanelSharedItemData();
originalPanelTitles = await PageObjects.dashboard.getPanelTitles();
expect(sharedData.map(item => item.title)).to.eql(originalPanelTitles);
});
});
it('data shared item container data has description and title set', async () => {
const sharedContainerData = await PageObjects.dashboard.getSharedContainerData();
expect(sharedContainerData.title).to.be('dashboard with everything');
expect(sharedContainerData.description).to.be(
'I have one of every visualization type since the last time I was created!');
});
it('data-shared-item title should update a viz when using a custom panel title', async () => {
await PageObjects.dashboard.clickEdit();
const CUSTOM_VIS_TITLE = 'ima custom title for a vis!';
await PageObjects.dashboard.setCustomPanelTitle(CUSTOM_VIS_TITLE);
await retry.try(async () => {
@ -64,7 +63,7 @@ export default function ({ getService, getPageObjects }) {
await retry.try(async () => {
const sharedData = await PageObjects.dashboard.getPanelSharedItemData();
const foundOriginalSharedItemTitle = !!sharedData.find(item => {
return item.title === testVisualizationTitles[0];
return item.title === originalPanelTitles[0];
});
expect(foundOriginalSharedItemTitle).to.be(true);
});
@ -72,25 +71,12 @@ export default function ({ getService, getPageObjects }) {
it('data-shared-item title should update a saved search when using a custom panel title', async () => {
const CUSTOM_SEARCH_TITLE = 'ima custom title for a search!';
const panels = await PageObjects.dashboard.getDashboardPanels();
// The reverse is only to take advantage of the fact that the saved search is last at the time of writing this
// test which speeds things up.
const searchPanel = await Promise.race(panels.map(async panel => {
return new Promise(async resolve => {
const savedSearchPanel = await testSubjects.descendantExists('embeddedSavedSearchDocTable', panel);
if (savedSearchPanel) {
resolve(panel);
}
});
}));
await PageObjects.dashboard.setCustomPanelTitle(CUSTOM_SEARCH_TITLE, searchPanel);
await PageObjects.dashboard.setCustomPanelTitle(CUSTOM_SEARCH_TITLE, 'Rendering Test: saved search');
await retry.try(async () => {
const sharedData = await PageObjects.dashboard.getPanelSharedItemData();
const foundSharedItemTitle = !!sharedData.find(item => {
return item.title === CUSTOM_SEARCH_TITLE;
});
console.log('foundSharedItemTitle: ' + foundSharedItemTitle);
expect(foundSharedItemTitle).to.be(true);
});
});

View file

@ -0,0 +1,41 @@
import expect from 'expect.js';
export default function ({ getService, getPageObjects }) {
const retry = getService('retry');
const PageObjects = getPageObjects(['dashboard', 'common']);
const remote = getService('remote');
describe('embed mode', async () => {
before(async () => {
await PageObjects.dashboard.loadSavedDashboard('few panels');
});
it('hides the chrome', async () => {
let isChromeVisible = await PageObjects.common.isChromeVisible();
expect(isChromeVisible).to.be(true);
const currentUrl = await remote.getCurrentUrl();
const newUrl = currentUrl + '&embed=true';
// Embed parameter only works on a hard refresh.
const useTimeStamp = true;
await remote.get(newUrl.toString(), useTimeStamp);
await retry.try(async () => {
isChromeVisible = await PageObjects.common.isChromeVisible();
expect(isChromeVisible).to.be(false);
});
});
after(async function () {
const currentUrl = await remote.getCurrentUrl();
const newUrl = currentUrl.replace('&embed=true', '');
// First use the timestamp to cause a hard refresh so the new embed parameter works correctly.
let useTimeStamp = true;
await remote.get(newUrl.toString(), useTimeStamp);
// Then get rid of the timestamp so the rest of the tests work with state and app switching.
useTimeStamp = false;
await remote.get(newUrl.toString(), useTimeStamp);
});
});
}

View file

@ -0,0 +1,119 @@
import expect from 'expect.js';
/**
* This tests both that one of each visualization can be added to a dashboard (as opposed to opening an existing
* dashboard with the visualizations already on it), as well as conducts a rough type of snapshot testing by checking
* for various ui components. The downside is these tests are a bit fragile to css changes (though not as fragile as
* actual screenshot snapshot regression testing), and can be difficult to diagnose failures (which visualization
* broke?). The upside is that this offers very good coverage with a minimal time investment.
*/
export default function ({ getService, getPageObjects }) {
const find = getService('find');
const dashboardExpect = getService('dashboardExpect');
const dashboardAddPanel = getService('dashboardAddPanel');
const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'visualize', 'discover']);
describe('dashboard embeddable rendering', function describeIndexTests() {
before(async () => {
await PageObjects.dashboard.clickNewDashboard();
const fromTime = '2018-01-01 00:00:00.000';
const toTime = '2018-04-13 00:00:00.000';
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
});
it('adding visualizations', async () => {
await dashboardAddPanel.addEveryVisualization('"Rendering Test"');
await PageObjects.header.waitUntilLoadingHasFinished();
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.be(26);
});
it('adding saved searches', async () => {
await dashboardAddPanel.addEverySavedSearch('"Rendering Test"');
await dashboardAddPanel.closeAddPanel();
await PageObjects.header.waitUntilLoadingHasFinished();
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.be(27);
await PageObjects.dashboard.waitForRenderComplete();
// Not neccessary but helpful for local debugging.
await PageObjects.dashboard.saveDashboard('embeddable rendering test');
});
it('pie charts rendered', async () => {
await dashboardExpect.pieSliceCount(16);
});
it('area, bar and heatmap charts rendered', async () => {
await dashboardExpect.seriesElementCount(19);
});
it('data tables render', async () => {
await dashboardExpect.dataTableRowCount(5);
});
it('saved searches render', async () => {
await dashboardExpect.savedSearchRowCount(50);
});
it('goal and guage render', async () => {
await dashboardExpect.goalAndGuageLabelsExist(['63%', '56%', '11.915 GB']);
});
it('input controls render', async () => {
await dashboardExpect.inputControlItemCount(5);
});
it('metric vis renders', async () => {
await dashboardExpect.metricValuesExist(['7,544']);
});
it('markdown renders', async () => {
await dashboardExpect.markdownWithValuesExists(['I\'m a markdown!']);
});
it('line charts render', async () => {
await dashboardExpect.lineChartPointsCount(5);
});
it('tag cloud renders', async () => {
await dashboardExpect.tagCloudWithValuesFound(['CN', 'IN', 'US', 'BR', 'ID']);
});
it('timelion chart renders', async () => {
await dashboardExpect.timelionLegendCount(0);
});
it('tsvb guage renders', async () => {
const tsvbGuageExists = await find.existsByCssSelector('.thorHalfGauge');
expect(tsvbGuageExists).to.be(true);
});
it('tsvb metric chart renders', async () => {
await dashboardExpect.tsvbMetricValuesExist(['210,007,889,606']);
});
it('tsvb markdown renders', async () => {
await dashboardExpect.tsvbMarkdownWithValuesExists(['Hi Avg last bytes: 6286.674715909091']);
});
it('tsvb table chart renders', async () => {
await dashboardExpect.tsvbTableCellCount(20);
});
it('tsvb time series renders', async () => {
await dashboardExpect.tsvbTimeSeriesLegendCount(1);
});
it('tsvb top n chart renders', async () => {
await dashboardExpect.tsvbTopNValuesExist(['5,734.79', '6,286.67']);
});
it('vega chart renders', async () => {
const tsvb = await find.existsByCssSelector('.vega-view-container');
expect(tsvb).to.be(true);
});
});
}

View file

@ -0,0 +1,60 @@
import expect from 'expect.js';
export default function ({ getService, getPageObjects }) {
const retry = getService('retry');
const remote = getService('remote');
const PageObjects = getPageObjects(['dashboard', 'common']);
describe('full screen mode', async () => {
before(async () => {
await PageObjects.dashboard.loadSavedDashboard('few panels');
});
it('option not available in edit mode', async () => {
await PageObjects.dashboard.clickEdit();
const exists = await PageObjects.dashboard.fullScreenModeMenuItemExists();
expect(exists).to.be(false);
});
it('available in view mode', async () => {
await PageObjects.dashboard.saveDashboard('full screen test', { saveAsNew: true });
const exists = await PageObjects.dashboard.fullScreenModeMenuItemExists();
expect(exists).to.be(true);
});
it('hides the chrome', async () => {
let isChromeVisible = await PageObjects.common.isChromeVisible();
expect(isChromeVisible).to.be(true);
await PageObjects.dashboard.clickFullScreenMode();
await retry.try(async () => {
isChromeVisible = await PageObjects.common.isChromeVisible();
expect(isChromeVisible).to.be(false);
});
});
it('displays exit full screen logo button', async () => {
const exists = await PageObjects.dashboard.exitFullScreenLogoButtonExists();
expect(exists).to.be(true);
});
it('displays exit full screen logo button when panel is expanded', async () => {
await PageObjects.dashboard.toggleExpandPanel();
const exists = await PageObjects.dashboard.exitFullScreenTextButtonExists();
expect(exists).to.be(true);
});
it('exits when the text button is clicked on', async () => {
const logoButton = await PageObjects.dashboard.getExitFullScreenLogoButton();
await remote.moveMouseTo(logoButton);
await PageObjects.dashboard.clickExitFullScreenTextButton();
await retry.try(async () => {
const isChromeVisible = await PageObjects.common.isChromeVisible();
expect(isChromeVisible).to.be(true);
});
});
});
}

View file

@ -9,6 +9,7 @@ export default function ({ getService, getPageObjects }) {
const testSubjects = getService('testSubjects');
const kibanaServer = getService('kibanaServer');
const remote = getService('remote');
const dashboardAddPanel = getService('dashboardAddPanel');
const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'discover']);
const dashboardName = 'Dashboard Panel Controls Test';
@ -34,7 +35,7 @@ export default function ({ getService, getPageObjects }) {
before(async () => {
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.setTimepickerInDataRange();
await PageObjects.dashboard.addVisualization(PIE_CHART_VIS_NAME);
await dashboardAddPanel.addVisualization(PIE_CHART_VIS_NAME);
});
it('are hidden in view mode', async function () {
@ -126,7 +127,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.discover.saveSearch('my search');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.header.clickDashboard();
await PageObjects.dashboard.addSavedSearch('my search');
await dashboardAddPanel.addSavedSearch('my search');
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.be(1);

View file

@ -0,0 +1,67 @@
import expect from 'expect.js';
export default function ({ getService, getPageObjects }) {
const retry = getService('retry');
const remote = getService('remote');
const PageObjects = getPageObjects(['dashboard', 'visualize', 'header']);
describe('expanding a panel', () => {
before(async () => {
await PageObjects.dashboard.loadSavedDashboard('few panels');
});
it('hides other panels', async () => {
await PageObjects.dashboard.toggleExpandPanel();
await retry.try(async () => {
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(1);
});
});
it('does not show the spy pane toggle if mouse is not hovering', async () => {
// move mouse off the panel.
await PageObjects.header.clickTimepicker();
// no spy pane without hover
const spyToggleExists = await PageObjects.visualize.getSpyToggleExists();
expect(spyToggleExists).to.be(false);
});
it('shows the spy pane toggle on hover', async () => {
const panels = await PageObjects.dashboard.getDashboardPanels();
// Simulate hover
await remote.moveMouseTo(panels[0]);
const spyToggleExists = await PageObjects.visualize.getSpyToggleExists();
expect(spyToggleExists).to.be(true);
});
// This was an actual bug that appeared, where the spy pane appeared on panels after adding them, but
// disappeared when a new dashboard was opened up.
it('shows the spy pane toggle directly after opening a dashboard', async () => {
await PageObjects.dashboard.clickEdit();
await PageObjects.dashboard.saveDashboard('spy pane test', { saveAsNew: true });
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.dashboard.loadSavedDashboard('spy pane test');
const panels = await PageObjects.dashboard.getDashboardPanels();
// Simulate hover
await remote.moveMouseTo(panels[1]);
const spyToggleExists = await PageObjects.visualize.getSpyToggleExists();
expect(spyToggleExists).to.be(true);
});
it('shows other panels after being minimized', async () => {
const panelCount = await PageObjects.dashboard.getPanelCount();
// Panels are all minimized on a fresh open of a dashboard, so we need to re-expand in order to then minimize.
await PageObjects.dashboard.toggleExpandPanel();
await PageObjects.dashboard.toggleExpandPanel();
// Add a retry to fix https://github.com/elastic/kibana/issues/14574. Perhaps the recent changes to this
// being a CSS update is causing the UI to change slower than grabbing the panels?
retry.try(async () => {
const panelCountAfterMaxThenMinimize = await PageObjects.dashboard.getPanelCount();
expect(panelCountAfterMaxThenMinimize).to.be(panelCount);
});
});
});
}

View file

@ -5,6 +5,7 @@ export default function ({ getService, getPageObjects }) {
const kibanaServer = getService('kibanaServer');
const retry = getService('retry');
const remote = getService('remote');
const dashboardAddPanel = getService('dashboardAddPanel');
const PageObjects = getPageObjects(['dashboard', 'header', 'common', 'visualize']);
const dashboardName = 'Dashboard View Edit Test';
@ -28,7 +29,7 @@ export default function ({ getService, getPageObjects }) {
it('create test dashboard', async function () {
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.addVisualizations(PageObjects.dashboard.getTestVisualizationNames());
await dashboardAddPanel.addVisualizations(PageObjects.dashboard.getTestVisualizationNames());
const isDashboardSaved = await PageObjects.dashboard.saveDashboard(dashboardName);
expect(isDashboardSaved).to.eql(true);
});
@ -125,8 +126,8 @@ export default function ({ getService, getPageObjects }) {
});
it('when a new vis is added', async function () {
await PageObjects.dashboard.clickAddVisualization();
await PageObjects.dashboard.clickAddNewVisualizationLink();
await dashboardAddPanel.ensureAddPanelIsShowing();
await dashboardAddPanel.clickAddNewEmbeddableLink();
await PageObjects.visualize.clickAreaChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.visualize.saveVisualization('new viz panel');
@ -142,7 +143,7 @@ export default function ({ getService, getPageObjects }) {
});
it('when an existing vis is added', async function () {
await PageObjects.dashboard.addVisualization('new viz panel');
await dashboardAddPanel.addVisualization('new viz panel');
await PageObjects.dashboard.clickCancelOutOfEditMode();
// confirm lose changes

View file

@ -1,22 +1,56 @@
export default function ({ getService, loadTestFile }) {
export default function ({ getService, loadTestFile, getPageObjects }) {
const remote = getService('remote');
const esArchiver = getService('esArchiver');
const PageObjects = getPageObjects(['dashboard']);
describe('dashboard app', function () {
before(() => remote.setWindowSize(1200, 900));
loadTestFile(require.resolve('./_dashboard_filter_bar'));
loadTestFile(require.resolve('./_dashboard_time_picker'));
loadTestFile(require.resolve('./_dashboard_shared_attributes'));
loadTestFile(require.resolve('./_bwc_shared_urls'));
loadTestFile(require.resolve('./_dashboard_queries'));
loadTestFile(require.resolve('./_dashboard_snapshots'));
loadTestFile(require.resolve('./_dashboard_grid'));
loadTestFile(require.resolve('./_panel_controls'));
loadTestFile(require.resolve('./_view_edit'));
loadTestFile(require.resolve('./_dashboard'));
loadTestFile(require.resolve('./_dashboard_state'));
loadTestFile(require.resolve('./_dashboard_save'));
loadTestFile(require.resolve('./_dashboard_time'));
loadTestFile(require.resolve('./_dashboard_listing'));
loadTestFile(require.resolve('./_dashboard_clone'));
describe('using current data', function () {
before(async () => {
await remote.setWindowSize(1300, 900);
await PageObjects.dashboard.initTests({
kibanaIndex: 'dashboard/current/kibana',
dataIndex: 'dashboard/current/data',
defaultIndex: 'logstash-*'
});
await PageObjects.dashboard.preserveCrossAppState();
});
after(async function () {
await PageObjects.dashboard.clearSavedObjectsFromAppLinks();
await esArchiver.unload('dashboard/current/kibana');
await esArchiver.unload('dashboard/current/data');
});
// This has to be first since the other tests create some embeddables as side affects and our counting assumes
// a fresh index.
loadTestFile(require.resolve('./_embeddable_rendering'));
loadTestFile(require.resolve('./_create_and_add_embeddables'));
loadTestFile(require.resolve('./_dashboard_options'));
loadTestFile(require.resolve('./_data_shared_attributes'));
loadTestFile(require.resolve('./_embed_mode'));
loadTestFile(require.resolve('./_full_screen_mode'));
loadTestFile(require.resolve('./_dashboard_filter_bar'));
loadTestFile(require.resolve('./_dashboard_filtering'));
loadTestFile(require.resolve('./_panel_expand_toggle'));
loadTestFile(require.resolve('./_dashboard_grid'));
});
// Each of these tests call initTests themselves, the way it was originally written. The above tests only load
// the data once to save on time. Eventually, all of these tests should just use current data and we can reserve
// legacy data only for specifically testing BWC situations.
describe('using legacy data', function () {
before(() => remote.setWindowSize(1200, 900));
loadTestFile(require.resolve('./_dashboard_time_picker'));
loadTestFile(require.resolve('./_bwc_shared_urls'));
loadTestFile(require.resolve('./_dashboard_snapshots'));
loadTestFile(require.resolve('./_panel_controls'));
loadTestFile(require.resolve('./_view_edit'));
loadTestFile(require.resolve('./_dashboard_state'));
loadTestFile(require.resolve('./_dashboard_save'));
loadTestFile(require.resolve('./_dashboard_time'));
loadTestFile(require.resolve('./_dashboard_listing'));
loadTestFile(require.resolve('./_dashboard_clone'));
});
});
}

View file

@ -26,6 +26,8 @@ import {
DashboardVisualizationProvider,
DashboardExpectProvider,
FailureDebuggingProvider,
VisualizeListingTableProvider,
DashboardAddPanelProvider,
} from './services';
export default async function ({ readConfigFile }) {
@ -76,6 +78,8 @@ export default async function ({ readConfigFile }) {
dashboardVisualizations: DashboardVisualizationProvider,
dashboardExpect: DashboardExpectProvider,
failureDebugging: FailureDebuggingProvider,
visualizeListingTable: VisualizeListingTableProvider,
dashboardAddPanel: DashboardAddPanelProvider,
},
servers: commonConfig.get('servers'),
apps: {

View file

@ -0,0 +1,617 @@
{
"type": "index",
"value": {
"index": "animals-dogs-2018-01-01",
"settings": {
"index": {
"number_of_shards": "5",
"number_of_replicas": "1"
}
},
"mappings": {
"data": {
"properties": {
"@timestamp": {
"type": "date"
},
"animal": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"sound": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"weightLbs": {
"type": "long"
}
}
}
}
}
}
{
"type": "index",
"value": {
"index": "animals-dogs-2018-04-10",
"settings": {
"index": {
"number_of_shards": "5",
"number_of_replicas": "1"
}
},
"mappings": {
"data": {
"properties": {
"@timestamp": {
"type": "date"
},
"animal": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"sound": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"weightLbs": {
"type": "long"
}
}
}
}
}
}
{
"type": "index",
"value": {
"index": "animals-cats-2018-01-01",
"settings": {
"index": {
"number_of_shards": "5",
"number_of_replicas": "1"
}
},
"mappings": {
"data": {
"properties": {
"@timestamp": {
"type": "date"
},
"animal": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"sound": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"weightLbs": {
"type": "long"
}
}
}
}
}
}
{
"type": "index",
"value": {
"index": "animals-cats-2018-04-10",
"settings": {
"index": {
"number_of_shards": "5",
"number_of_replicas": "1"
}
},
"mappings": {
"data": {
"properties": {
"@timestamp": {
"type": "date"
},
"animal": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"sound": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"weightLbs": {
"type": "long"
}
}
}
}
}
}
{
"type": "index",
"value": {
"index": "dogbreeds",
"settings": {
"index": {
"number_of_shards": "5",
"number_of_replicas": "1"
}
},
"mappings": {
"data": {
"properties": {
"activity level": {
"type": "long"
},
"barking level": {
"type": "long"
},
"breed": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"size": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"trainability": {
"type": "long"
}
}
}
}
}
}
{
"type": "index",
"value": {
"index": "logstash-0",
"settings": {
"index": {
"number_of_shards": "1",
"analysis": {
"analyzer": {
"makelogs_url": {
"type": "standard",
"max_token_length": "1000",
"tokenizer": "uax_url_email"
}
}
},
"number_of_replicas": "0"
}
},
"mappings": {
"doc": {
"dynamic_templates": [
{
"string_fields": {
"match": "*",
"match_mapping_type": "string",
"mapping": {
"fields": {
"raw": {
"type": "keyword"
}
},
"type": "text"
}
}
}
],
"properties": {
"@message": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"@tags": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"@timestamp": {
"type": "date"
},
"agent": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"bytes": {
"type": "long"
},
"clientip": {
"type": "ip"
},
"extension": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"geo": {
"properties": {
"coordinates": {
"type": "geo_point"
},
"dest": {
"type": "keyword"
},
"src": {
"type": "keyword"
},
"srcdest": {
"type": "keyword"
}
}
},
"headings": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"host": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"id": {
"type": "integer"
},
"index": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"ip": {
"type": "ip"
},
"links": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"machine": {
"properties": {
"os": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"ram": {
"type": "long"
}
}
},
"memory": {
"type": "double"
},
"meta": {
"properties": {
"char": {
"type": "keyword"
},
"related": {
"type": "text"
},
"user": {
"properties": {
"firstname": {
"type": "text"
},
"lastname": {
"type": "integer"
}
}
}
}
},
"phpmemory": {
"type": "long"
},
"referer": {
"type": "keyword"
},
"relatedContent": {
"properties": {
"article:modified_time": {
"type": "date"
},
"article:published_time": {
"type": "date"
},
"article:section": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"article:tag": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"og:description": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"og:image": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"og:image:height": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"og:image:width": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"og:site_name": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"og:title": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"og:type": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"og:url": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"twitter:card": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"twitter:description": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"twitter:image": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"twitter:site": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"twitter:title": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"url": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
}
}
},
"request": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"response": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"spaces": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"url": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"utc_time": {
"type": "date"
},
"xss": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
}
}
}
}
}
}

View file

@ -0,0 +1,274 @@
{
"type": "index",
"value": {
"index": ".kibana",
"settings": {
"index": {
"number_of_shards": "1",
"auto_expand_replicas": "0-1",
"number_of_replicas": "0"
}
},
"mappings": {
"doc": {
"dynamic": "strict",
"properties": {
"config": {
"dynamic": "true",
"properties": {
"buildNum": {
"type": "keyword"
},
"dateFormat:tz": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"defaultIndex": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"notifications:lifetime:banner": {
"type": "long"
},
"notifications:lifetime:error": {
"type": "long"
},
"notifications:lifetime:info": {
"type": "long"
},
"notifications:lifetime:warning": {
"type": "long"
}
}
},
"dashboard": {
"properties": {
"description": {
"type": "text"
},
"hits": {
"type": "integer"
},
"kibanaSavedObjectMeta": {
"properties": {
"searchSourceJSON": {
"type": "text"
}
}
},
"optionsJSON": {
"type": "text"
},
"panelsJSON": {
"type": "text"
},
"refreshInterval": {
"properties": {
"display": {
"type": "keyword"
},
"pause": {
"type": "boolean"
},
"section": {
"type": "integer"
},
"value": {
"type": "integer"
}
}
},
"timeFrom": {
"type": "keyword"
},
"timeRestore": {
"type": "boolean"
},
"timeTo": {
"type": "keyword"
},
"title": {
"type": "text"
},
"uiStateJSON": {
"type": "text"
},
"version": {
"type": "integer"
}
}
},
"index-pattern": {
"properties": {
"fieldFormatMap": {
"type": "text"
},
"fields": {
"type": "text"
},
"intervalName": {
"type": "keyword"
},
"notExpandable": {
"type": "boolean"
},
"sourceFilters": {
"type": "text"
},
"timeFieldName": {
"type": "keyword"
},
"title": {
"type": "text"
}
}
},
"search": {
"properties": {
"columns": {
"type": "keyword"
},
"description": {
"type": "text"
},
"hits": {
"type": "integer"
},
"kibanaSavedObjectMeta": {
"properties": {
"searchSourceJSON": {
"type": "text"
}
}
},
"sort": {
"type": "keyword"
},
"title": {
"type": "text"
},
"version": {
"type": "integer"
}
}
},
"server": {
"properties": {
"uuid": {
"type": "keyword"
}
}
},
"timelion-sheet": {
"properties": {
"description": {
"type": "text"
},
"hits": {
"type": "integer"
},
"kibanaSavedObjectMeta": {
"properties": {
"searchSourceJSON": {
"type": "text"
}
}
},
"timelion_chart_height": {
"type": "integer"
},
"timelion_columns": {
"type": "integer"
},
"timelion_interval": {
"type": "keyword"
},
"timelion_other_interval": {
"type": "keyword"
},
"timelion_rows": {
"type": "integer"
},
"timelion_sheet": {
"type": "text"
},
"title": {
"type": "text"
},
"version": {
"type": "integer"
}
}
},
"type": {
"type": "keyword"
},
"updated_at": {
"type": "date"
},
"url": {
"properties": {
"accessCount": {
"type": "long"
},
"accessDate": {
"type": "date"
},
"createDate": {
"type": "date"
},
"url": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 2048
}
}
}
}
},
"visualization": {
"properties": {
"description": {
"type": "text"
},
"kibanaSavedObjectMeta": {
"properties": {
"searchSourceJSON": {
"type": "text"
}
}
},
"savedSearchId": {
"type": "keyword"
},
"title": {
"type": "text"
},
"uiStateJSON": {
"type": "text"
},
"version": {
"type": "integer"
},
"visState": {
"type": "text"
}
}
}
}
}
}
}
}

View file

@ -250,6 +250,10 @@ export function CommonPageProvider({ getService, getPageObjects }) {
return await testSubjects.getVisibleText('breadcrumbPageTitle');
}
async getTopNavText() {
return await testSubjects.getVisibleText('top-nav');
}
async doesCssSelectorExist(selector) {
log.debug(`doesCssSelectorExist ${selector}`);

View file

@ -14,23 +14,28 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const testSubjects = getService('testSubjects');
const PageObjects = getPageObjects(['common', 'header']);
const dashboardAddPanel = getService('dashboardAddPanel');
const PageObjects = getPageObjects(['common', 'header', 'settings', 'visualize']);
const defaultFindTimeout = config.get('timeouts.find');
class DashboardPage {
async initTests() {
async initTests({
kibanaIndex = 'dashboard/legacy',
dataIndex = 'logstash_functional',
defaultIndex = 'logstash-*',
} = {}) {
log.debug('load kibana index with visualizations and log data');
await Promise.all([
esArchiver.load('dashboard'),
esArchiver.loadIfNeeded('logstash_functional')
esArchiver.load(kibanaIndex),
esArchiver.loadIfNeeded(dataIndex)
]);
await kibanaServer.uiSettings.replace({
'dateFormat:tz': 'UTC',
'defaultIndex': 'logstash-*'
'defaultIndex': defaultIndex
});
await this.selectDefaultIndex(defaultIndex);
await kibanaServer.uiSettings.disableToastAutohide();
await PageObjects.common.navigateToApp('dashboard');
}
@ -41,6 +46,13 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
await PageObjects.header.waitUntilLoadingHasFinished();
}
async selectDefaultIndex(indexName) {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaIndices();
await PageObjects.settings.clickLinkText(indexName);
await PageObjects.settings.clickDefaultIndexButton();
}
async clickEditVisualization() {
log.debug('clickEditVisualization');
@ -211,14 +223,6 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
await testSubjects.click('deleteSelectedDashboards');
}
async clickAddVisualization() {
await testSubjects.click('dashboardAddPanelButton');
}
async clickAddNewVisualizationLink() {
await testSubjects.click('addNewSavedObjectLink');
}
async clickOptions() {
await testSubjects.click('dashboardOptionsButton');
}
@ -236,6 +240,14 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
}
}
// avoids any 'Object with id x not found' errors when switching tests.
async clearSavedObjectsFromAppLinks() {
await PageObjects.header.clickVisualize();
await PageObjects.visualize.gotoLandingPage();
await PageObjects.header.clickDashboard();
await this.gotoDashboardLandingPage();
}
async isDarkThemeOn() {
log.debug('isDarkThemeOn');
await this.openOptions();
@ -266,48 +278,11 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
}
}
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 filterEmbeddableNames(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.filterEmbeddableNames(searchName);
await find.clickByPartialLinkText(searchName);
await testSubjects.exists('addSavedSearchToDashboardSuccess');
await this.clickAddVisualization();
}
async addVisualization(vizName) {
await this.clickAddVisualization();
log.debug('filter visualization (' + vizName + ')');
await this.filterEmbeddableNames(vizName);
await this.clickVizNameLink(vizName);
// 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');
@ -438,8 +413,8 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
return Promise.all(getTitlePromises);
}
async getDashboardPanels() {
return await testSubjects.findAll('dashboardPanel');
async getPanelHeading(title) {
return await testSubjects.find(`dashboardPanelHeading-${title.replace(/\s/g, '')}`);
}
async getPanelDimensions() {
@ -458,7 +433,7 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
async getPanelCount() {
log.debug('getPanelCount');
const panels = await find.allByCssSelector('.react-grid-item');
const panels = await testSubjects.findAll('dashboardPanel');
return panels.length;
}
@ -496,6 +471,10 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
});
}
async getDashboardPanels() {
return await testSubjects.findAll('dashboardPanel');
}
async clickDashboardPanelEditLink() {
await this.showPanelEditControlsDropdownMenu();
await testSubjects.click('dashboardPanelEditLink');
@ -507,9 +486,7 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
}
async addVisualizations(visualizations) {
for (const vizName of visualizations) {
await this.addVisualization(vizName);
}
await dashboardAddPanel.addVisualizations(visualizations);
}
async setTimepickerInDataRange() {
@ -518,6 +495,12 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
}
async setTimepickerIn63DataRange() {
const fromTime = '2018-01-01 00:00:00.000';
const toTime = '2018-04-13 00:00:00.000';
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
}
async setSaveAsNewCheckBox(checked) {
log.debug('saveAsNewCheckbox: ' + checked);
const saveAsNewCheckbox = await testSubjects.find('saveAsNewCheckbox');
@ -549,59 +532,73 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
return _.map(filters, async (filter) => await filter.getVisibleText());
}
async getPieSliceCount() {
async getPieSliceCount(timeout) {
log.debug('getPieSliceCount');
return await retry.try(async () => {
const slices = await find.allByCssSelector('svg > g > g.arcs > path.slice');
const slices = await find.allByCssSelector('svg > g > g.arcs > path.slice', timeout);
return slices.length;
});
}
async filterOnPieSlice() {
log.debug('Filtering on a pie slice');
await retry.try(async () => {
const slices = await find.allByCssSelector('svg > g > g.arcs > path.slice');
log.debug('Slices found:' + slices.length);
return slices[0].click();
});
async filterOnPieSlice(sliceValue) {
log.debug(`Filtering on a pie slice with optional value ${sliceValue}`);
if (sliceValue) {
await testSubjects.click(`pieSlice-${sliceValue}`);
} else {
await retry.try(async () => {
const slices = await find.allByCssSelector('svg > g > g.arcs > path.slice');
log.debug('Slices found:' + slices.length);
return slices[0].click();
});
}
}
async arePanelMainMenuOptionsOpen(panel) {
async arePanelMainMenuOptionsOpen(parent) {
log.debug('arePanelMainMenuOptionsOpen');
// Sub menu used arbitrarily - any option on the main menu panel would do.
return panel ?
await testSubjects.descendantExists('dashboardPanelOptionsSubMenuLink', panel) :
return parent ?
await testSubjects.descendantExists('dashboardPanelOptionsSubMenuLink', parent) :
await testSubjects.exists('dashboardPanelOptionsSubMenuLink');
}
async openPanelOptions(panel) {
async openPanelOptions(parent) {
log.debug('openPanelOptions');
const panelOpen = await this.arePanelMainMenuOptionsOpen(panel);
const panelOpen = await this.arePanelMainMenuOptionsOpen(parent);
if (!panelOpen) {
await retry.try(async () => {
await (panel ? remote.moveMouseTo(panel) : testSubjects.moveMouseTo('dashboardPanelTitle'));
const toggleMenuItem = panel ?
await testSubjects.findDescendant('dashboardPanelToggleMenuIcon', panel) :
await (parent ? remote.moveMouseTo(parent) : testSubjects.moveMouseTo('dashboardPanelTitle'));
const toggleMenuItem = parent ?
await testSubjects.findDescendant('dashboardPanelToggleMenuIcon', parent) :
await testSubjects.find('dashboardPanelToggleMenuIcon');
await toggleMenuItem.click();
const panelOpen = await this.arePanelMainMenuOptionsOpen(panel);
const panelOpen = await this.arePanelMainMenuOptionsOpen(parent);
if (!panelOpen) { throw new Error('Panel menu still not open'); }
});
}
}
async toggleExpandPanel(panel) {
await (panel ? remote.moveMouseTo(panel) : testSubjects.moveMouseTo('dashboardPanelTitle'));
async toggleExpandPanel(parent) {
await (parent ? remote.moveMouseTo(parent) : testSubjects.moveMouseTo('dashboardPanelTitle'));
const expandShown = await testSubjects.exists('dashboardPanelExpandIcon');
if (!expandShown) {
await this.openPanelOptions(panel);
await this.openPanelOptions(parent);
}
await testSubjects.click('dashboardPanelExpandIcon');
}
async setCustomPanelTitle(customTitle, panel) {
log.debug(`setCustomPanelTitle(${customTitle}, ${panel})`);
await this.openPanelOptions(panel);
/**
*
* @param customTitle
* @param originalTitle - optional to specify which panel to change the title on.
* @return {Promise<void>}
*/
async setCustomPanelTitle(customTitle, originalTitle) {
log.debug(`setCustomPanelTitle(${customTitle}, ${originalTitle})`);
let panelOptions = null;
if (originalTitle) {
panelOptions = await this.getPanelHeading(originalTitle);
}
await this.openPanelOptions(panelOptions);
await testSubjects.click('dashboardPanelOptionsSubMenuLink');
await testSubjects.setValue('customDashboardPanelTitleInput', customTitle);
}
@ -640,6 +637,15 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
});
}
async getSharedContainerData() {
log.debug('getSharedContainerData');
const sharedContainer = await find.byCssSelector('[data-shared-items-container]');
return {
title: await sharedContainer.getAttribute('data-title'),
description: await sharedContainer.getAttribute('data-description')
};
}
async getPanelSharedItemData() {
log.debug('in getPanelSharedItemData');
const sharedItems = await find.allByCssSelector('[data-shared-item]');

View file

@ -16,6 +16,15 @@ export function HeaderPageProvider({ getService, getPageObjects }) {
await retry.try(async () => await remote.findByCssSelector(selector).click());
}
async confirmTopNavTextContains(text) {
await retry.try(async () => {
const topNavText = await PageObjects.common.getTopNavText();
if (topNavText.toLowerCase().indexOf(text.toLowerCase()) < 0) {
throw new Error(`Top nav text ${topNavText} does not contain ${text} (case insensitive)`);
}
});
}
async clickDiscover() {
log.debug('click Discover tab');
await this.clickSelector('a[href*=\'discover\']');
@ -27,6 +36,7 @@ export function HeaderPageProvider({ getService, getPageObjects }) {
log.debug('click Visualize tab');
await this.clickSelector('a[href*=\'visualize\']');
await PageObjects.common.waitForTopNavToBeVisible();
await this.confirmTopNavTextContains('visualize');
await this.isGlobalLoadingIndicatorHidden();
}
@ -34,6 +44,7 @@ export function HeaderPageProvider({ getService, getPageObjects }) {
log.debug('click Dashboard tab');
await this.clickSelector('a[href*=\'dashboard\']');
await PageObjects.common.waitForTopNavToBeVisible();
await this.confirmTopNavTextContains('dashboard');
await this.isGlobalLoadingIndicatorHidden();
}

View file

@ -558,11 +558,14 @@ export function VisualizePageProvider({ getService, getPageObjects }) {
});
}
async saveVisualization(vizName) {
async saveVisualization(vizName, { saveAsNew = false } = {}) {
await this.ensureSavePanelOpen();
await testSubjects.setValue('visTitleInput', vizName);
log.debug('click submit button');
await testSubjects.click('saveVisualizationButton');
if (saveAsNew) {
await testSubjects.click('saveAsNewCheckbox');
}
await PageObjects.header.waitUntilLoadingHasFinished();
return await testSubjects.exists('saveVisualizationSuccess');
}
@ -753,9 +756,13 @@ export function VisualizePageProvider({ getService, getPageObjects }) {
return await dataTable.getVisibleText();
}
async getDataTableHeaders() {
async getDataTableHeaders(parent) {
const dataTableHeader = await retry.try(
async () => testSubjects.find('paginated-table-header'));
async () => (
parent ?
testSubjects.findDescendant('paginated-table-header', parent) :
testSubjects.find('paginated-table-header')
));
return await dataTableHeader.getVisibleText();
}

View file

@ -0,0 +1,144 @@
export function DashboardAddPanelProvider({ getService, getPageObjects }) {
const log = getService('log');
const retry = getService('retry');
const testSubjects = getService('testSubjects');
const find = getService('find');
const PageObjects = getPageObjects(['header']);
return new class DashboardAddPanel {
async clickOpenAddPanel() {
log.debug('DashboardAddPanel.clickOpenAddPanel');
await testSubjects.click('dashboardAddPanelButton');
}
async clickAddNewEmbeddableLink() {
await testSubjects.click('addNewSavedObjectLink');
}
async closeAddVizualizationPanel() {
log.debug('closeAddVizualizationPanel');
await find.clickByCssSelector('i.fa fa-chevron-up');
}
async clickSavedSearchTab() {
await testSubjects.click('addSavedSearchTab');
}
async addEveryEmbeddableOnCurrentPage() {
log.debug('addEveryEmbeddableOnCurrentPage');
const embeddableRows = await find.allByCssSelector('.list-group-menu-item');
for (let i = 0; i < embeddableRows.length; i++) {
await embeddableRows[i].click();
}
log.debug(`Added ${embeddableRows.length} embeddables`);
}
async clickPagerNextButton() {
const pagerNextButtonExists = await testSubjects.exists('paginateNext');
if (pagerNextButtonExists) {
await testSubjects.click('paginateNext');
await PageObjects.header.waitUntilLoadingHasFinished();
}
return pagerNextButtonExists;
}
async isAddPanelOpen() {
log.debug('DashboardAddPanel.isAddPanelOpen');
return await testSubjects.exists('dashboardAddPanel');
}
async ensureAddPanelIsShowing() {
log.debug('DashboardAddPanel.ensureAddPanelIsShowing');
const isOpen = await this.isAddPanelOpen();
if (!isOpen) {
await retry.try(async () => {
await this.clickOpenAddPanel();
const isOpen = await this.isAddPanelOpen();
if (!isOpen) {
throw new Error('Add panel still not open, trying again.');
}
});
}
}
async closeAddPanel() {
log.debug('closeAddPanel');
const isOpen = await this.isAddPanelOpen();
if (isOpen) {
await retry.try(async () => {
await this.clickOpenAddPanel();
const isOpen = await this.isAddPanelOpen();
if (isOpen) {
throw new Error('Add panel still open, trying again.');
}
});
}
}
async addEveryVisualization(filter) {
log.debug('DashboardAddPanel.addEveryVisualization');
await this.ensureAddPanelIsShowing();
if (filter) {
await this.filterEmbeddableNames(filter.replace('-', ' '));
}
let morePages = true;
while (morePages) {
await this.addEveryEmbeddableOnCurrentPage();
morePages = await this.clickPagerNextButton();
}
}
async addEverySavedSearch(filter) {
log.debug('DashboardAddPanel.addEverySavedSearch');
await this.ensureAddPanelIsShowing();
await this.clickSavedSearchTab();
if (filter) {
await this.filterEmbeddableNames(filter.replace('-', ' '));
}
let morePages = true;
while (morePages) {
await this.addEveryEmbeddableOnCurrentPage();
morePages = await this.clickPagerNextButton();
}
}
async addSavedSearch(searchName) {
log.debug(`addSavedSearch(${searchName})`);
await this.ensureAddPanelIsShowing();
await this.clickSavedSearchTab();
await this.filterEmbeddableNames(searchName);
await find.clickByPartialLinkText(searchName);
await testSubjects.exists('addSavedSearchToDashboardSuccess');
await this.closeAddPanel();
}
async addSavedSearches(searches) {
for (const name of searches) {
await this.addSavedSearch(name);
}
}
async addVisualizations(visualizations) {
log.debug('DashboardAddPanel.addVisualizations');
for (const vizName of visualizations) {
await this.addVisualization(vizName);
}
}
async addVisualization(vizName) {
log.debug(`DashboardAddPanel.addVisualization(${vizName})`);
await this.ensureAddPanelIsShowing();
await this.filterEmbeddableNames(`"${vizName.replace('-', ' ')}"`);
await find.clickByPartialLinkText(vizName);
await this.closeAddPanel();
}
async filterEmbeddableNames(name) {
await testSubjects.setValue('savedObjectFinderSearchInput', name);
await PageObjects.header.waitUntilLoadingHasFinished();
}
};
}

View file

@ -4,8 +4,9 @@ export function DashboardExpectProvider({ getService, getPageObjects }) {
const log = getService('log');
const retry = getService('retry');
const testSubjects = getService('testSubjects');
const find = getService('find');
const filterBar = getService('filterBar');
const PageObjects = getPageObjects(['dashboard']);
const PageObjects = getPageObjects(['dashboard', 'visualize']);
return new class DashboardExpect {
async pieSliceCount(expectedCount) {
@ -35,8 +36,15 @@ export function DashboardExpectProvider({ getService, getPageObjects }) {
async docTableFieldCount(expectedCount) {
log.debug(`DashboardExpect.docTableFieldCount(${expectedCount})`);
await retry.try(async () => {
const docTableCellCounts = await testSubjects.findAll(`docTableField`);
expect(docTableCellCounts.length).to.be(expectedCount);
const docTableCells = await testSubjects.findAll('docTableField');
expect(docTableCells.length).to.be(expectedCount);
});
}
async tsvbTimeSeriesLegendCount(expectedCount) {
await retry.try(async () => {
const tsvbLegendItems = await testSubjects.findAll('tsvbLegendItem');
expect(tsvbLegendItems.length).to.be(expectedCount);
});
}
@ -44,5 +52,164 @@ export function DashboardExpectProvider({ getService, getPageObjects }) {
const indexPatterns = await filterBar.getFilterFieldIndexPatterns();
expect(indexPatterns).to.eql(expectedIndexPatterns);
}
async legendValuesToExist(legendValues) {
await Promise.all(legendValues.map(async legend => {
await retry.try(async () => {
const legendValueExists = await testSubjects.exists(`legend-${legend}`);
expect(legendValueExists).to.be(true);
});
}));
}
async textWithinElementsExists(texts, getElementsFn) {
await retry.try(async () => {
const elements = await getElementsFn();
const elementTexts = [];
await Promise.all(elements.map(async element => {
elementTexts.push(await element.getVisibleText());
}));
log.debug(`Found ${elements.length} elements with values: ${JSON.stringify(elementTexts)}`);
texts.forEach(value => {
const indexOfValue = elementTexts.indexOf(value);
expect(indexOfValue).to.be.greaterThan(-1);
elementTexts.splice(indexOfValue, 1);
});
});
}
async textWithinTestSubjectsExists(texts, selector) {
log.debug(`textWithinTestSubjectsExists:(${JSON.stringify(texts)},${selector})`);
await this.textWithinElementsExists(texts, async () => await testSubjects.findAll(selector));
}
async textWithinCssElementExists(texts, selector) {
log.debug(`textWithinCssElementExists:(${JSON.stringify(texts)},${selector})`);
await this.textWithinElementsExists(texts, async () => await find.allByCssSelector(selector));
}
async textWithinElementsDoNotExist(texts, getElementsFn) {
await retry.try(async () => {
const elements = await getElementsFn();
const elementTexts = [];
await Promise.all(elements.map(async element => {
elementTexts.push(await element.getVisibleText());
}));
log.debug(`Found ${elements.length} elements with values: ${JSON.stringify(elementTexts)}`);
texts.forEach(value => {
const indexOfValue = elementTexts.indexOf(value);
expect(indexOfValue).to.be(-1);
});
});
}
async textWithinCssElementDoNotExist(texts, selector) {
log.debug(`textWithinCssElementExists:(${JSON.stringify(texts)},${selector})`);
await this.textWithinElementsDoNotExist(texts, async () => await find.allByCssSelector(selector));
}
async timelionLegendCount(expectedCount) {
await retry.try(async () => {
const flotLegendLabels = await testSubjects.findAll('flotLegendLabel');
expect(flotLegendLabels.length).to.be(expectedCount);
});
}
async emptyTagCloudFound() {
const tagCloudVisualizations = await testSubjects.findAll('tagCloudVisualization');
const tagCloudsHaveContent = await Promise.all(tagCloudVisualizations.map(async tagCloud => {
return await find.descendantExistsByCssSelector('text', tagCloud);
}));
expect(tagCloudsHaveContent.indexOf(false)).to.be.greaterThan(-1);
}
async tagCloudWithValuesFound(values) {
const tagCloudVisualizations = await testSubjects.findAll('tagCloudVisualization');
const matches = await Promise.all(tagCloudVisualizations.map(async tagCloud => {
for (let i = 0; i < values.length; i++) {
const valueExists = await testSubjects.descendantExists(values[i], tagCloud);
if (!valueExists) {
return false;
}
}
return true;
}));
expect(matches.indexOf(true)).to.be.greaterThan(-1);
}
async goalAndGuageLabelsExist(labels) {
await this.textWithinCssElementExists(labels, '.chart-label');
}
async metricValuesExist(values) {
await this.textWithinCssElementExists(values, '.metric-value');
}
async tsvbMetricValuesExist(values) {
await this.textWithinTestSubjectsExists(values, 'tsvbMetricValue');
}
async tsvbTopNValuesExist(values) {
await this.textWithinTestSubjectsExists(values, 'tsvbTopNValue');
}
async vegaTextsExist(values) {
await this.textWithinCssElementExists(values, '.vega-view-container text');
}
async vegaTextsDoNotExist(values) {
await this.textWithinCssElementDoNotExist(values, '.vega-view-container text');
}
async tsvbMarkdownWithValuesExists(values) {
await this.textWithinTestSubjectsExists(values, 'tsvbMarkdown');
}
async markdownWithValuesExists(values) {
await this.textWithinTestSubjectsExists(values, 'markdownBody');
}
async savedSearchRowCount(expectedCount) {
await retry.try(async () => {
const savedSearchRows = await testSubjects.findAll('docTableExpandToggleColumn');
expect(savedSearchRows.length).to.be(expectedCount);
});
}
async dataTableRowCount(expectedCount) {
await retry.try(async () => {
const dataTableRows =
await find.allByCssSelector('[data-test-subj="paginated-table-body"] [data-cell-content]');
expect(dataTableRows.length).to.be(expectedCount);
});
}
async seriesElementCount(expectedCount) {
await retry.try(async () => {
const seriesElements = await find.allByCssSelector('.series');
expect(seriesElements.length).to.be(expectedCount);
});
}
async inputControlItemCount(expectedCount) {
await retry.try(async () => {
const inputControlItems = await testSubjects.findAll('inputControlItem');
expect(inputControlItems.length).to.be(expectedCount);
});
}
async lineChartPointsCount(expectedCount) {
await retry.try(async () => {
const points = await find.allByCssSelector('.points');
expect(points.length).to.be(expectedCount);
});
}
async tsvbTableCellCount(expectedCount) {
await retry.try(async () => {
const tableCells = await find.allByCssSelector('.tsvb-table__value');
expect(tableCells.length).to.be(expectedCount);
});
}
};
}

View file

@ -1,2 +1,3 @@
export { DashboardVisualizationProvider } from './visualizations';
export { DashboardExpectProvider } from './expectations';
export { DashboardAddPanelProvider } from './add_panel';

View file

@ -2,6 +2,7 @@
export function DashboardVisualizationProvider({ getService, getPageObjects }) {
const log = getService('log');
const testSubjects = getService('testSubjects');
const dashboardAddPanel = getService('dashboardAddPanel');
const PageObjects = getPageObjects(['dashboard', 'visualize', 'header', 'discover']);
return new class DashboardVisualizations {
@ -11,8 +12,8 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }) {
if (inViewMode) {
await PageObjects.dashboard.clickEdit();
}
await PageObjects.dashboard.clickAddVisualization();
await PageObjects.dashboard.clickAddNewVisualizationLink();
await dashboardAddPanel.ensureAddPanelIsShowing();
await dashboardAddPanel.clickAddNewEmbeddableLink();
await PageObjects.visualize.clickVisualBuilder();
await PageObjects.visualize.saveVisualization(name);
}
@ -49,7 +50,7 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }) {
if (inViewMode) {
await PageObjects.dashboard.clickEdit();
}
await PageObjects.dashboard.addSavedSearch(name);
await dashboardAddPanel.addSavedSearch(name);
}
async createAndAddMarkdown({ name, markdown }) {
@ -58,8 +59,8 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }) {
if (inViewMode) {
await PageObjects.dashboard.clickEdit();
}
await PageObjects.dashboard.clickAddVisualization();
await PageObjects.dashboard.clickAddNewVisualizationLink();
await dashboardAddPanel.ensureAddPanelIsShowing();
await dashboardAddPanel.clickAddNewEmbeddableLink();
await PageObjects.visualize.clickMarkdownWidget();
await PageObjects.visualize.setMarkdownTxt(markdown);
await PageObjects.visualize.clickGo();

View file

@ -23,7 +23,7 @@ export function FilterBarProvider({ getService }) {
await testSubjects.click(`filter & filter-key-${key} disableFilter-${key}`);
}
async addFilter(field, operator, value) {
async addFilter(field, operator, value, inputCssClass = 'ui-select-search') {
await testSubjects.click('addFilter');
let input = await find.byCssSelector(`filter-field-select input.ui-select-search`);
await input.type(field);
@ -31,7 +31,7 @@ export function FilterBarProvider({ getService }) {
input = await find.byCssSelector(`filter-operator-select input.ui-select-search`);
await input.type(operator);
await remote.pressKeys('\uE006');
input = await find.byCssSelector(`filter-params-editor input.ui-select-search`);
input = await find.byCssSelector(`filter-params-editor input.${inputCssClass}`);
await input.type(value);
await remote.pressKeys('\uE006');
await testSubjects.click('saveFilter');
@ -48,6 +48,13 @@ export function FilterBarProvider({ getService }) {
return await Promise.all(spans.map(el => el.getVisibleText()));
}
async ensureFieldEditorModalIsClosed() {
const closeFilterEditorModalButtonExists = await testSubjects.exists('filterEditorModalCloseButton');
if (closeFilterEditorModalButtonExists) {
await testSubjects.click('filterEditorModalCloseButton');
}
}
async getFilterFieldIndexPatterns() {
const indexPatterns = [];
const groups = await find.allByCssSelector('.ui-select-choices-group-label');

View file

@ -6,4 +6,6 @@ export { RemoteProvider } from './remote';
export { DocTableProvider } from './doc_table';
export { ScreenshotsProvider } from './screenshots';
export { FailureDebuggingProvider } from './failure_debugging';
export { VisualizeListingTableProvider } from './visualize_listing_table';
export * from './dashboard';

View file

@ -48,9 +48,9 @@ export function TestSubjectsProvider({ getService }) {
return await find.byCssSelector(testSubjSelector(selector), timeout);
}
async findAll(selector) {
async findAll(selector, timeout) {
log.debug(`TestSubjects.findAll(${selector})`);
const all = await find.allByCssSelector(testSubjSelector(selector));
const all = await find.allByCssSelector(testSubjSelector(selector), timeout);
return await filterAsync(all, el => el.isDisplayed());
}

View file

@ -0,0 +1,36 @@
export function VisualizeListingTableProvider({ getService, getPageObjects }) {
const testSubjects = getService('testSubjects');
const find = getService('find');
const log = getService('log');
const PageObjects = getPageObjects(['dashboard', 'visualize', 'header', 'discover']);
class VisualizeListingTable {
async getAllVisualizationNamesOnCurrentPage() {
const visualizationNames = [];
const links = await find.allByCssSelector('.kuiLink');
for (let i = 0; i < links.length; i++) {
visualizationNames.push(await links[i].getVisibleText());
}
log.debug(`Found ${visualizationNames.length} visualizations on current page`);
return visualizationNames;
}
async getAllVisualizationNames() {
log.debug('VisualizeListingTable.getAllVisualizationNames');
let morePages = true;
let visualizationNames = [];
while (morePages) {
visualizationNames = visualizationNames.concat(await this.getAllVisualizationNamesOnCurrentPage());
const pagerNextButton = await testSubjects.find('pagerNextButton');
morePages = !(await pagerNextButton.getProperty('disabled'));
if (morePages) {
await testSubjects.click('pagerNextButton');
await PageObjects.header.waitUntilLoadingHasFinished();
}
}
return visualizationNames;
}
}
return new VisualizeListingTable();
}