From e55ac10684e6c019faacfd9b003d6524a54ab003 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Fri, 28 Jul 2017 13:31:46 -0400 Subject: [PATCH] Stabilize and bring back flaky tests (#13034) (#13160) * Stabalize tests and bring back flaky test * Try to fix flaky map radius visualize test * fix flaky tile map zoom test * Try to fix flaky zoom test by avoiding zooming in and out with spy panel open * Remove duplicate wrapped retry around find methods The retry was moved internally, around a check for stale elements as well, so this should no longer be neccessary. --- .../apps/dashboard/_dashboard_save.js | 10 +- .../apps/dashboard/_dashboard_time.js | 4 +- test/functional/apps/dashboard/_view_edit.js | 2 +- test/functional/apps/discover/_discover.js | 2 +- test/functional/apps/discover/_field_data.js | 27 +- test/functional/apps/visualize/_area_chart.js | 6 +- test/functional/apps/visualize/_data_table.js | 2 +- .../apps/visualize/_heatmap_chart.js | 15 +- test/functional/apps/visualize/_line_chart.js | 2 +- test/functional/apps/visualize/_pie_chart.js | 2 +- .../apps/visualize/_point_series_options.js | 36 +- test/functional/apps/visualize/_tag_cloud.js | 14 +- test/functional/apps/visualize/_tile_map.js | 65 +- .../apps/visualize/_vertical_bar_chart.js | 13 +- test/functional/page_objects/common_page.js | 14 +- .../functional/page_objects/dashboard_page.js | 18 +- test/functional/page_objects/discover_page.js | 25 +- test/functional/page_objects/header_page.js | 7 +- .../functional/page_objects/visualize_page.js | 1015 ++++++----------- test/functional/services/find.js | 124 +- 20 files changed, 527 insertions(+), 876 deletions(-) diff --git a/test/functional/apps/dashboard/_dashboard_save.js b/test/functional/apps/dashboard/_dashboard_save.js index ada14e4240a8..d8fb658df976 100644 --- a/test/functional/apps/dashboard/_dashboard_save.js +++ b/test/functional/apps/dashboard/_dashboard_save.js @@ -1,6 +1,7 @@ import expect from 'expect.js'; -export default function ({ getPageObjects }) { +export default function ({ getService, getPageObjects }) { + const retry = getService('retry'); const PageObjects = getPageObjects(['dashboard', 'header', 'common']); describe('dashboard save', function describeIndexTests() { @@ -88,8 +89,11 @@ export default function ({ getPageObjects }) { await PageObjects.dashboard.clickEdit(); await PageObjects.dashboard.enterDashboardTitleAndClickSave(dashboardName.toUpperCase()); - const isConfirmOpen = await PageObjects.common.isConfirmModalOpen(); - expect(isConfirmOpen).to.equal(true); + // We expect isConfirmModalOpen to be open, hence the retry if not found. + await retry.try(async () => { + const isConfirmOpen = await PageObjects.common.isConfirmModalOpen(); + expect(isConfirmOpen).to.equal(true); + }); await PageObjects.common.clickCancelOnModal(); }); diff --git a/test/functional/apps/dashboard/_dashboard_time.js b/test/functional/apps/dashboard/_dashboard_time.js index 3ce108ebd602..955e8baf7cc1 100644 --- a/test/functional/apps/dashboard/_dashboard_time.js +++ b/test/functional/apps/dashboard/_dashboard_time.js @@ -29,7 +29,7 @@ export default function ({ getPageObjects }) { await PageObjects.header.clickToastOK(); }); - it.skip('Does not set the time picker on open', async function () { + it('Does not set the time picker on open', async function () { await PageObjects.header.setAbsoluteRange(fromTime, toTime); await PageObjects.dashboard.loadSavedDashboard(dashboardName); @@ -81,7 +81,7 @@ export default function ({ getPageObjects }) { // when it's opened. However, if the user then changes the time, navigates to visualize, then navigates // back to dashboard, the overridden time should be preserved. The time is *only* reset on open, not // during navigation or page refreshes. - describe.skip('time changes', function () { + describe('time changes', function () { it('preserved during navigation', async function () { await PageObjects.header.setQuickTime('Today'); await PageObjects.header.clickVisualize(); diff --git a/test/functional/apps/dashboard/_view_edit.js b/test/functional/apps/dashboard/_view_edit.js index 81d26ccc8c2b..9d476691be90 100644 --- a/test/functional/apps/dashboard/_view_edit.js +++ b/test/functional/apps/dashboard/_view_edit.js @@ -164,7 +164,7 @@ export default function ({ getService, getPageObjects }) { expect(query).to.equal(originalQuery); }); - it.skip('when a filter is deleted', async function () { + it('when a filter is deleted', async function () { await PageObjects.dashboard.setTimepickerInDataRange(); await PageObjects.dashboard.filterOnPieSlice(); await PageObjects.dashboard.saveDashboard(dashboardName); diff --git a/test/functional/apps/discover/_discover.js b/test/functional/apps/discover/_discover.js index ea58affcddbd..aa70c7fe2f24 100644 --- a/test/functional/apps/discover/_discover.js +++ b/test/functional/apps/discover/_discover.js @@ -230,7 +230,7 @@ export default function ({ getService, getPageObjects }) { }); describe('data-shared-item', function () { - it.skip('should have correct data-shared-item title and description', async () => { + it('should have correct data-shared-item title and description', async () => { const expected = { title: 'A Saved Search', description: 'A Saved Search Description' diff --git a/test/functional/apps/discover/_field_data.js b/test/functional/apps/discover/_field_data.js index 5cdd153f7c55..5b58f540fd47 100644 --- a/test/functional/apps/discover/_field_data.js +++ b/test/functional/apps/discover/_field_data.js @@ -1,7 +1,6 @@ import expect from 'expect.js'; export default function ({ getService, getPageObjects }) { - const log = getService('log'); const retry = getService('retry'); const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); @@ -9,31 +8,21 @@ export default function ({ getService, getPageObjects }) { const PageObjects = getPageObjects(['common', 'header', 'discover']); describe('discover app', function describeIndexTests() { - before(function () { + before(async function () { const fromTime = '2015-09-19 06:31:44.000'; const toTime = '2015-09-23 18:31:44.000'; + await esArchiver.loadIfNeeded('logstash_functional'); + await esArchiver.load('discover'); + await kibanaServer.waitForStabilization(); // delete .kibana index and update configDoc - return kibanaServer.uiSettings.replace({ + await kibanaServer.uiSettings.replace({ 'dateFormat:tz': 'UTC', 'defaultIndex': 'logstash-*' - }) - .then(function loadkibanaIndexPattern() { - log.debug('load kibana index with default index pattern'); - return esArchiver.load('discover'); - }) - // and load a set of makelogs data - .then(function loadIfEmptyMakelogs() { - return esArchiver.loadIfNeeded('logstash_functional'); - }) - .then(function () { - log.debug('discover'); - return PageObjects.common.navigateToApp('discover'); - }) - .then(function () { - log.debug('setAbsoluteRange'); - return PageObjects.header.setAbsoluteRange(fromTime, toTime); }); + + await PageObjects.common.navigateToApp('discover'); + await PageObjects.header.setAbsoluteRange(fromTime, toTime); }); diff --git a/test/functional/apps/visualize/_area_chart.js b/test/functional/apps/visualize/_area_chart.js index 1087f1a615c1..fa878130d1bc 100644 --- a/test/functional/apps/visualize/_area_chart.js +++ b/test/functional/apps/visualize/_area_chart.js @@ -68,7 +68,7 @@ export default function ({ getService, getPageObjects }) { expect(message).to.be(`Visualization Editor: Saved Visualization "${vizNamewithSpecialChars}"`); }) .then(function testVisualizeWaitForToastMessageGone() { - return PageObjects.visualize.waitForToastMessageGone(); + return PageObjects.header.waitForToastMessageGone(); }); }); @@ -79,7 +79,7 @@ export default function ({ getService, getPageObjects }) { log.debug(`Saved viz message with umlaut = ${message}`); expect(message).to.be(`Visualization Editor: Saved Visualization "${vizNamewithSpecialChars}"`); - await PageObjects.visualize.waitForToastMessageGone(); + await PageObjects.header.waitForToastMessageGone(); }); it('should save and load', function () { @@ -90,7 +90,7 @@ export default function ({ getService, getPageObjects }) { expect(message).to.be('Visualization Editor: Saved Visualization \"' + vizName1 + '\"'); }) .then(function testVisualizeWaitForToastMessageGone() { - return PageObjects.visualize.waitForToastMessageGone(); + return PageObjects.header.waitForToastMessageGone(); }) .then(function loadSavedVisualization() { return PageObjects.visualize.loadSavedVisualization(vizName1); diff --git a/test/functional/apps/visualize/_data_table.js b/test/functional/apps/visualize/_data_table.js index 06d1c4ff1854..6445817b19de 100644 --- a/test/functional/apps/visualize/_data_table.js +++ b/test/functional/apps/visualize/_data_table.js @@ -59,7 +59,7 @@ export default function ({ getService, getPageObjects }) { expect(message).to.be('Visualization Editor: Saved Visualization \"' + vizName1 + '\"'); }) .then(function testVisualizeWaitForToastMessageGone() { - return PageObjects.visualize.waitForToastMessageGone(); + return PageObjects.header.waitForToastMessageGone(); }) .then(function () { return PageObjects.visualize.loadSavedVisualization(vizName1); diff --git a/test/functional/apps/visualize/_heatmap_chart.js b/test/functional/apps/visualize/_heatmap_chart.js index 9ea369741775..c3315ecaa14b 100644 --- a/test/functional/apps/visualize/_heatmap_chart.js +++ b/test/functional/apps/visualize/_heatmap_chart.js @@ -3,6 +3,7 @@ import expect from 'expect.js'; export default function ({ getService, getPageObjects }) { const log = getService('log'); const screenshots = getService('screenshots'); + const retry = getService('retry'); const PageObjects = getPageObjects(['common', 'visualize', 'header']); describe('visualize app', function describeIndexTests() { @@ -57,7 +58,7 @@ export default function ({ getService, getPageObjects }) { expect(message).to.be('Visualization Editor: Saved Visualization \"' + vizName1 + '\"'); }) .then(function testVisualizeWaitForToastMessageGone() { - return PageObjects.visualize.waitForToastMessageGone(); + return PageObjects.header.waitForToastMessageGone(); }) .then(function () { return PageObjects.visualize.loadSavedVisualization(vizName1); @@ -70,20 +71,14 @@ export default function ({ getService, getPageObjects }) { }); }); - it('should show correct chart, take screenshot', function () { + it('should show correct chart, take screenshot', async function () { const expectedChartValues = ['0 - 400', '0 - 400', '400 - 800', '1,200 - 1,600', '1,200 - 1,600', '400 - 800', '0 - 400', '0 - 400', '0 - 400', '0 - 400', '400 - 800', '1,200 - 1,600', '1,200 - 1,600', '400 - 800', '0 - 400', '0 - 400', '0 - 400', '0 - 400', '400 - 800', '1,200 - 1,600', '1,200 - 1,600', '400 - 800', '0 - 400', '0 - 400' ]; - // Most recent failure on Jenkins usually indicates the bar chart is still being drawn? - // return arguments[0].getAttribute(arguments[1]);","args":[{"ELEMENT":"592"},"fill"]}] arguments[0].getAttribute is not a function - // try sleeping a bit before getting that data - return PageObjects.common.sleep(5000) - .then(function () { - return PageObjects.visualize.getHeatmapData(); - }) - .then(function showData(data) { + await retry.try(async () => { + const data = await PageObjects.visualize.getHeatmapData(); log.debug('data=' + data); log.debug('data.length=' + data.length); screenshots.take('Visualize-heatmap-chart'); diff --git a/test/functional/apps/visualize/_line_chart.js b/test/functional/apps/visualize/_line_chart.js index 7d2634fbd840..6669764c6fdf 100644 --- a/test/functional/apps/visualize/_line_chart.js +++ b/test/functional/apps/visualize/_line_chart.js @@ -131,7 +131,7 @@ export default function ({ getService, getPageObjects }) { expect(message).to.be('Visualization Editor: Saved Visualization \"' + vizName1 + '\"'); }) .then(function testVisualizeWaitForToastMessageGone() { - return PageObjects.visualize.waitForToastMessageGone(); + return PageObjects.header.waitForToastMessageGone(); }) .then(function () { return PageObjects.visualize.loadSavedVisualization(vizName1); diff --git a/test/functional/apps/visualize/_pie_chart.js b/test/functional/apps/visualize/_pie_chart.js index 7b624653643f..a0c2950ef8a6 100644 --- a/test/functional/apps/visualize/_pie_chart.js +++ b/test/functional/apps/visualize/_pie_chart.js @@ -65,7 +65,7 @@ export default function ({ getService, getPageObjects }) { expect(message).to.be('Visualization Editor: Saved Visualization \"' + vizName1 + '\"'); }) .then(function testVisualizeWaitForToastMessageGone() { - return PageObjects.visualize.waitForToastMessageGone(); + return PageObjects.header.waitForToastMessageGone(); }) .then(function () { return PageObjects.visualize.loadSavedVisualization(vizName1); diff --git a/test/functional/apps/visualize/_point_series_options.js b/test/functional/apps/visualize/_point_series_options.js index 5203f531bfc1..1ba9a87d063f 100644 --- a/test/functional/apps/visualize/_point_series_options.js +++ b/test/functional/apps/visualize/_point_series_options.js @@ -2,6 +2,7 @@ import expect from 'expect.js'; export default function ({ getService, getPageObjects }) { const log = getService('log'); + const retry = getService('retry'); const screenshots = getService('screenshots'); const PageObjects = getPageObjects(['common', 'visualize', 'header', 'pointSeries']); const pointSeriesVis = PageObjects.pointSeries; @@ -82,7 +83,7 @@ export default function ({ getService, getPageObjects }) { describe('secondary value axis', function () { - it('should show correct chart, take screenshot', function () { + it('should show correct chart, take screenshot', async function () { const expectedChartValues = [ [ 37, 202, 740, 1437, 1371, 751, 188, 31, 42, 202, 683, 1361, 1415, 707, 177, 27, 32, 175, 707, 1408, 1355, 726, 201, 29 ], @@ -92,27 +93,18 @@ export default function ({ getService, getPageObjects }) { 12807319386, 13375732998, 13190755620, 12627508458, 12731510199, 13153337344 ], ]; - // Most recent failure on Jenkins usually indicates the bar chart is still being drawn? - // return arguments[0].getAttribute(arguments[1]);","args":[{"ELEMENT":"592"},"fill"]}] arguments[0].getAttribute is not a function - // try sleeping a bit before getting that data - return PageObjects.common.sleep(2000) - .then(function () { - return PageObjects.visualize.getLineChartData('fill="#00a69b"'); - }) - .then(function showData(data) { - log.debug('count data=' + data); - log.debug('data.length=' + data.length); - screenshots.take('Visualize-secondary-value-axis'); - expect(data).to.eql(expectedChartValues[0]); - }) - .then(function () { - return PageObjects.visualize.getLineChartData('fill="#57c17b"', 'ValueAxis-2'); - }) - .then(function showData(data) { - log.debug('average memory data=' + data); - log.debug('data.length=' + data.length); - expect(data).to.eql(expectedChartValues[1]); - }); + await retry.try(async () => { + const data = await PageObjects.visualize.getLineChartData('fill="#00a69b"'); + log.debug('count data=' + data); + log.debug('data.length=' + data.length); + screenshots.take('Visualize-secondary-value-axis'); + expect(data).to.eql(expectedChartValues[0]); + + const avgMemoryData = await PageObjects.visualize.getLineChartData('fill="#57c17b"', 'ValueAxis-2'); + log.debug('average memory data=' + avgMemoryData); + log.debug('data.length=' + avgMemoryData.length); + expect(avgMemoryData).to.eql(expectedChartValues[1]); + }); }); it('should put secondary axis on the right', function () { diff --git a/test/functional/apps/visualize/_tag_cloud.js b/test/functional/apps/visualize/_tag_cloud.js index 7a935073cf46..b323a79be710 100644 --- a/test/functional/apps/visualize/_tag_cloud.js +++ b/test/functional/apps/visualize/_tag_cloud.js @@ -53,14 +53,10 @@ export default function ({ getService, getPageObjects }) { describe('tile cloud chart', function indexPatternCreation() { const vizName1 = 'Visualization tagCloud'; - it('should show correct tag cloud data', function () { - return PageObjects.common.sleep(2000) - .then(function () { - return PageObjects.visualize.getTextTag().then(function (results) { - log.debug(results); - expect(results).to.eql([ '32212254720', '21474836480','20401094656','19327352832','18253611008' ]); - }); - }); + it('should show correct tag cloud data', async function () { + const data = await PageObjects.visualize.getTextTag(); + log.debug(data); + expect(data).to.eql([ '32212254720', '21474836480','20401094656','19327352832','18253611008' ]); }); @@ -71,7 +67,7 @@ export default function ({ getService, getPageObjects }) { expect(message).to.be('Visualization Editor: Saved Visualization \"' + vizName1 + '\"'); }) .then(function testVisualizeWaitForToastMessageGone() { - return PageObjects.visualize.waitForToastMessageGone(); + return PageObjects.header.waitForToastMessageGone(); }) .then(function () { return PageObjects.visualize.loadSavedVisualization(vizName1); diff --git a/test/functional/apps/visualize/_tile_map.js b/test/functional/apps/visualize/_tile_map.js index 0e4ef11a9d73..9983a7a1fabe 100644 --- a/test/functional/apps/visualize/_tile_map.js +++ b/test/functional/apps/visualize/_tile_map.js @@ -90,54 +90,34 @@ export default function ({ getService, getPageObjects }) { describe('tile map chart', function indexPatternCreation() { - it('should show correct tile map data on default zoom level', function () { + it('should show correct tile map data on default zoom level', async function () { const expectedTableData = ['9 5,787 { "lat": 37.22448418632405, "lon": -103.01935195013255 }', 'd 5,600 { "lat": 37.44271478370398, "lon": -81.72692197253595 }', 'c 1,319 { "lat": 47.72720855392425, "lon": -109.84745063951028 }', 'b 999 { "lat": 62.04130042948433, "lon": -155.28087269195967 }', 'f 187 { "lat": 45.656166475784175, "lon": -82.45831044201545 }', '8 108 { "lat": 18.85260305600241, "lon": -156.5148810390383 }']; + //level 1 + await PageObjects.visualize.clickMapZoomOut(); + //level 0 + await PageObjects.visualize.clickMapZoomOut(); - return PageObjects.visualize.openSpyPanel() - .then(function () { - //level 1 - return PageObjects.visualize.clickMapZoomOut(); - }) - .then(function () { - //level 0 - return PageObjects.visualize.clickMapZoomOut(); - }) - .then(function () { - return PageObjects.settings.setPageSize('All'); - }) - .then(function getDataTableData() { - return PageObjects.visualize.getDataTableData() - .then(function showData(actualTableData) { - compareTableData(expectedTableData, actualTableData.trim().split('\n')); - return PageObjects.visualize.closeSpyPanel(); - }); - }); + await PageObjects.visualize.openSpyPanel(); + await PageObjects.settings.setPageSize('All'); + const actualTableData = await PageObjects.visualize.getDataTableData(); + compareTableData(expectedTableData, actualTableData.trim().split('\n')); + await PageObjects.visualize.closeSpyPanel(); }); - it('should not be able to zoom out beyond 0', function () { - return PageObjects.visualize.getMapZoomOutEnabled() - // we can tell we're at level 1 because zoom out is disabled - .then(function () { - return retry.try(function tryingForTime() { - return PageObjects.visualize.getMapZoomOutEnabled() - .then(function (enabled) { - //should be able to zoom more as current config has 0 as min level. - expect(enabled).to.be(false); - }); - }); - }) - .then(function takeScreenshot() { - log.debug('Take screenshot (success)'); - screenshots.take('map-at-zoom-0'); - }); + it('should not be able to zoom out beyond 0', async function () { + await PageObjects.visualize.zoomAllTheWayOut(); + const enabled = await PageObjects.visualize.getMapZoomOutEnabled(); + expect(enabled).to.be(false); + screenshots.take('map-at-zoom-0'); }); - it('Fit data bounds should zoom to level 3', function () { + // See https://github.com/elastic/kibana/issues/13137 if this test starts failing intermittently + it('Fit data bounds should zoom to level 3', async function () { const expectedPrecision2ZoomCircles = [ { color: '#750000', radius: 192 }, { color: '#750000', radius: 191 }, @@ -187,12 +167,11 @@ export default function ({ getService, getPageObjects }) { { color: '#b99939', radius: 9 } ]; - return PageObjects.visualize.clickMapFitDataBounds() - .then(function () { - return PageObjects.visualize.getTileMapData(); - }) - .then(function (data) { + await retry.try(async() => { + await PageObjects.visualize.clickMapFitDataBounds(); + const data = await PageObjects.visualize.getTileMapData(); expect(data).to.eql(expectedPrecision2ZoomCircles); + screenshots.take('map-at-zoom-3'); }); }); @@ -238,7 +217,7 @@ export default function ({ getService, getPageObjects }) { expect(message).to.be('Visualization Editor: Saved Visualization \"' + vizName1 + '\"'); }) .then(function testVisualizeWaitForToastMessageGone() { - return PageObjects.visualize.waitForToastMessageGone(); + return PageObjects.header.waitForToastMessageGone(); }) .then(function () { return PageObjects.visualize.openSpyPanel(); diff --git a/test/functional/apps/visualize/_vertical_bar_chart.js b/test/functional/apps/visualize/_vertical_bar_chart.js index f0be93c5540f..5fbe56c81b85 100644 --- a/test/functional/apps/visualize/_vertical_bar_chart.js +++ b/test/functional/apps/visualize/_vertical_bar_chart.js @@ -2,6 +2,7 @@ import expect from 'expect.js'; export default function ({ getService, getPageObjects }) { const log = getService('log'); + const retry = getService('retry'); const screenshots = getService('screenshots'); const PageObjects = getPageObjects(['common', 'visualize', 'header']); @@ -57,7 +58,7 @@ export default function ({ getService, getPageObjects }) { expect(message).to.be('Visualization Editor: Saved Visualization \"' + vizName1 + '\"'); }) .then(function testVisualizeWaitForToastMessageGone() { - return PageObjects.visualize.waitForToastMessageGone(); + return PageObjects.header.waitForToastMessageGone(); }) .then(function () { return PageObjects.visualize.loadSavedVisualization(vizName1); @@ -70,7 +71,7 @@ export default function ({ getService, getPageObjects }) { }); }); - it('should show correct chart, take screenshot', function () { + it('should show correct chart, take screenshot', async function () { const expectedChartValues = [37, 202, 740, 1437, 1371, 751, 188, 31, 42, 202, 683, 1361, 1415, 707, 177, 27, 32, 175, 707, 1408, 1355, 726, 201, 29 ]; @@ -78,11 +79,8 @@ export default function ({ getService, getPageObjects }) { // Most recent failure on Jenkins usually indicates the bar chart is still being drawn? // return arguments[0].getAttribute(arguments[1]);","args":[{"ELEMENT":"592"},"fill"]}] arguments[0].getAttribute is not a function // try sleeping a bit before getting that data - return PageObjects.common.sleep(50000) - .then(function () { - return PageObjects.visualize.getBarChartData(); - }) - .then(function showData(data) { + await retry.try(async() => { + const data = await PageObjects.visualize.getBarChartData(); log.debug('data=' + data); log.debug('data.length=' + data.length); screenshots.take('Visualize-vertical-bar-chart'); @@ -90,7 +88,6 @@ export default function ({ getService, getPageObjects }) { }); }); - it('should show correct data', function () { // this is only the first page of the tabular data. const expectedChartData = [ '2015-09-20 00:00', '37', diff --git a/test/functional/page_objects/common_page.js b/test/functional/page_objects/common_page.js index 7d700c8fcd23..56714a95b304 100644 --- a/test/functional/page_objects/common_page.js +++ b/test/functional/page_objects/common_page.js @@ -7,6 +7,7 @@ export function CommonPageProvider({ getService, getPageObjects }) { const config = getService('config'); const remote = getService('remote'); const retry = getService('retry'); + const find = getService('find'); const testSubjects = getService('testSubjects'); const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['shield']); @@ -200,10 +201,7 @@ export function CommonPageProvider({ getService, getPageObjects }) { } async getSharedItemTitleAndDescription() { - const element = await remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('[data-shared-item]'); - + const element = await find.byCssSelector('[data-shared-item]'); return { title: await element.getAttribute('data-title'), description: await element.getAttribute('data-description') @@ -239,12 +237,8 @@ export function CommonPageProvider({ getService, getPageObjects }) { } async isConfirmModalOpen() { - const isOpen = await testSubjects - .find('confirmModalCancelButton', 2000) - .then(() => true, () => false); - - await remote.setFindTimeout(defaultFindTimeout); - return isOpen; + log.debug('isConfirmModalOpen'); + return await testSubjects.exists('confirmModalCancelButton', 2000); } async doesCssSelectorExist(selector) { diff --git a/test/functional/page_objects/dashboard_page.js b/test/functional/page_objects/dashboard_page.js index a38c0f24aa46..dc0b537a2a03 100644 --- a/test/functional/page_objects/dashboard_page.js +++ b/test/functional/page_objects/dashboard_page.js @@ -59,9 +59,14 @@ export function DashboardPageProvider({ getService, getPageObjects }) { } } + async getQueryInputElement() { + return retry.try(() => testSubjects.find('queryInput')); + } + async getQuery() { - const queryObject = await testSubjects.find('queryInput'); - return await queryObject.getProperty('value'); + log.debug(`getQuery`); + const queryInputElement = await this.getQueryInputElement(); + return await queryInputElement.getProperty('value'); } async setQuery(query) { @@ -104,7 +109,7 @@ export function DashboardPageProvider({ getService, getPageObjects }) { } clickCancelOutOfEditMode() { - log.debug('Clicking cancel'); + log.debug('clickCancelOutOfEditMode'); return testSubjects.click('dashboardViewOnlyMode'); } @@ -229,12 +234,7 @@ export function DashboardPageProvider({ getService, getPageObjects }) { // verify that green message at the top of the page. // it's only there for about 5 seconds - await retry.try(() => { - log.debug('verify toast-message for saved dashboard'); - return getRemote() - .findByCssSelector('kbn-truncated.toast-message.ng-isolate-scope') - .getVisibleText(); - }); + return await PageObjects.header.getToastMessage(); } /** diff --git a/test/functional/page_objects/discover_page.js b/test/functional/page_objects/discover_page.js index 00cc15f9bb43..d41d7ef97136 100644 --- a/test/functional/page_objects/discover_page.js +++ b/test/functional/page_objects/discover_page.js @@ -45,26 +45,23 @@ export function DiscoverPageProvider({ getService, getPageObjects }) { }); } - loadSavedSearch(searchName) { - return this.clickLoadSavedSearchButton() - .then(() => { - getRemote().findByPartialLinkText(searchName).click(); - }) - .then(() => { - return PageObjects.header.waitUntilLoadingHasFinished(); - }); + async loadSavedSearch(searchName) { + await this.clickLoadSavedSearchButton(); + const searchLink = await find.byPartialLinkText(searchName); + await searchLink.click(); + await PageObjects.header.waitUntilLoadingHasFinished(); } - clickNewSearchButton() { - return testSubjects.click('discoverNewButton'); + async clickNewSearchButton() { + await testSubjects.click('discoverNewButton'); } - clickSaveSearchButton() { - return testSubjects.click('discoverSaveButton'); + async clickSaveSearchButton() { + await testSubjects.click('discoverSaveButton'); } - clickLoadSavedSearchButton() { - return testSubjects.click('discoverOpenButton'); + async clickLoadSavedSearchButton() { + await testSubjects.click('discoverOpenButton'); } async getCurrentQueryName() { diff --git a/test/functional/page_objects/header_page.js b/test/functional/page_objects/header_page.js index 6b36fa74f1a2..e66666749ed0 100644 --- a/test/functional/page_objects/header_page.js +++ b/test/functional/page_objects/header_page.js @@ -201,12 +201,13 @@ export function HeaderPageProvider({ getService, getPageObjects }) { } async isGlobalLoadingIndicatorVisible() { - return await testSubjects.find('globalLoadingIndicator', defaultFindTimeout / 5); + log.debug('isGlobalLoadingIndicatorVisible'); + return await testSubjects.exists('globalLoadingIndicator'); } async isGlobalLoadingIndicatorHidden() { - remote.setFindTimeout(defaultFindTimeout * 10); - return await remote.findByCssSelector('[data-test-subj="globalLoadingIndicator"].ng-hide'); + log.debug('isGlobalLoadingIndicatorHidden'); + return await find.byCssSelector('[data-test-subj="globalLoadingIndicator"].ng-hide', defaultFindTimeout * 10); } async getPrettyDuration() { diff --git a/test/functional/page_objects/visualize_page.js b/test/functional/page_objects/visualize_page.js index 50a65f660b82..0682358bdf60 100644 --- a/test/functional/page_objects/visualize_page.js +++ b/test/functional/page_objects/visualize_page.js @@ -3,202 +3,129 @@ export function VisualizePageProvider({ getService, getPageObjects }) { const config = getService('config'); const testSubjects = getService('testSubjects'); const retry = getService('retry'); + const find = getService('find'); const log = getService('log'); const PageObjects = getPageObjects(['common', 'header']); const defaultFindTimeout = config.get('timeouts.find'); class VisualizePage { - clickAreaChart() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByPartialLinkText('Area') - .click(); - } - clickDataTable() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByPartialLinkText('Data Table') - .click(); + async clickAreaChart() { + await find.clickByPartialLinkText('Area'); } - clickLineChart() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByPartialLinkText('Line') - .click(); + async clickDataTable() { + await find.clickByPartialLinkText('Data Table'); } - - clickRegionMap() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByPartialLinkText('Region Map') - .click(); + async clickLineChart() { + await find.clickByPartialLinkText('Line'); } - getVectorMapData() { - return remote - .setFindTimeout(defaultFindTimeout) - .findAllByCssSelector('path.leaflet-clickable') - .then((chartTypes) => { - - - function getChartType(chart) { - let color; - return chart.getAttribute('fill') - .then((stroke) => { - color = stroke; - }) - .then(() => { - return { color: color }; - }); - } - - const getChartTypesPromises = chartTypes.map(getChartType); - return Promise.all(getChartTypesPromises); - }) - .then((data) => { - data = data.filter((country) => { - //filter empty colors - return country.color !== 'rgb(200,200,200)'; - }); - return data; - }); + async clickRegionMap() { + await find.clickByPartialLinkText('Region Map'); } - clickMarkdownWidget() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByPartialLinkText('Markdown') - .click(); + async clickMarkdownWidget() { + await find.clickByPartialLinkText('Markdown'); } - clickAddMetric() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('[group-name="metrics"] [data-test-subj="visualizeEditorAddAggregationButton"]') - .click(); + async clickAddMetric() { + await find.clickByCssSelector('[group-name="metrics"] [data-test-subj="visualizeEditorAddAggregationButton"]'); } - clickMetric() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByPartialLinkText('Metric') - .click(); + async clickMetric() { + await find.clickByPartialLinkText('Metric'); } - clickGauge() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByPartialLinkText('Gauge') - .click(); + async clickGauge() { + await find.clickByPartialLinkText('Gauge'); } - clickPieChart() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByPartialLinkText('Pie') - .click(); + async clickPieChart() { + await find.clickByPartialLinkText('Pie'); } - clickTileMap() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByPartialLinkText('Coordinate Map') - .click(); + async clickTileMap() { + await find.clickByPartialLinkText('Coordinate Map'); } - clickTagCloud() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByPartialLinkText('Tag Cloud') - .click(); + async clickTagCloud() { + await find.clickByPartialLinkText('Tag Cloud'); } - getTextTag() { - return remote - .setFindTimeout(defaultFindTimeout) - .findAllByCssSelector('text').getVisibleText(); + async getTextTag() { + const elements = await find.allByCssSelector('text'); + return await Promise.all(elements.map(async element => await element.getVisibleText())); } + async getVectorMapData() { + const chartTypes = await find.allByCssSelector('path.leaflet-clickable'); - getTextSizes() { - return remote - .setFindTimeout(defaultFindTimeout) - .findAllByCssSelector('text') - .then(function (tags) { - function returnTagSize(tag) { - return tag.getAttribute('style') - .then(function (style) { - return style.match(/font-size: ([^;]*);/)[1]; - }); - } - return Promise.all(tags.map(returnTagSize)); + async function getChartColors(chart) { + const stroke = await chart.getAttribute('fill'); + return { color: stroke }; + } + + let colorData = await Promise.all(chartTypes.map(getChartColors)); + colorData = colorData.filter((country) => { + //filter empty colors + return country.color !== 'rgb(200,200,200)'; }); + return colorData; } + async getTextSizes() { + const tags = await find.allByCssSelector('text'); + async function returnTagSize(tag) { + const style = await tag.getAttribute('style'); + return style.match(/font-size: ([^;]*);/)[1]; + } + return await Promise.all(tags.map(returnTagSize)); + } - clickVerticalBarChart() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByPartialLinkText('Vertical Bar') - .click(); + async clickVerticalBarChart() { + await find.clickByPartialLinkText('Vertical Bar'); } - clickHeatmapChart() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByPartialLinkText('Heat Map') - .click(); + async clickHeatmapChart() { + await find.clickByPartialLinkText('Heat Map'); } - getChartTypeCount() { - return remote - .setFindTimeout(defaultFindTimeout) - .findAllByCssSelector('a.wizard-vis-type.ng-scope') - .length; + async getChartTypeCount() { + const tags = await find.allByCssSelector('a.wizard-vis-type.ng-scope'); + return tags.length; } - getChartTypes() { - return testSubjects.findAll('visualizeWizardChartTypeTitle') - .then(function (chartTypes) { - function getChartType(chart) { - return chart.getVisibleText(); - } - const getChartTypesPromises = chartTypes.map(getChartType); - return Promise.all(getChartTypesPromises); - }) - .then(function (texts) { - return texts; - }); + async getChartTypes() { + const chartTypes = await testSubjects.findAll('visualizeWizardChartTypeTitle'); + async function getChartType(chart) { + return await chart.getVisibleText(); + } + const getChartTypesPromises = chartTypes.map(getChartType); + return await Promise.all(getChartTypesPromises); } - clickAbsoluteButton() { - return remote - .setFindTimeout(defaultFindTimeout * 2) - .findByCssSelector('ul.nav.nav-pills.nav-stacked.kbn-timepicker-modes:contains("absolute")') - .click(); + async clickAbsoluteButton() { + await find.clickByCssSelector( + 'ul.nav.nav-pills.nav-stacked.kbn-timepicker-modes:contains("absolute")', + defaultFindTimeout * 2); } - setFromTime(timeString) { - return remote - .setFindTimeout(defaultFindTimeout * 2) - .findByCssSelector('input[ng-model="absolute.from"]') - .clearValue() - .type(timeString); + async setFromTime(timeString) { + const input = await find.byCssSelector('input[ng-model="absolute.from"]', defaultFindTimeout * 2); + await input.clearValue(); + await input.type(timeString); } - setToTime(timeString) { - return remote - .setFindTimeout(defaultFindTimeout * 2) - .findByCssSelector('input[ng-model="absolute.to"]') - .clearValue() - .type(timeString); + async setToTime(timeString) { + const input = await find.byCssSelector('input[ng-model="absolute.to"]', defaultFindTimeout * 2); + await input.clearValue(); + await input.type(timeString); } - clickGoButton() { - return testSubjects.click('timepickerGoButton'); + async clickGoButton() { + await testSubjects.click('timepickerGoButton'); } async getSpyToggleExists() { @@ -230,259 +157,156 @@ export function VisualizePageProvider({ getService, getPageObjects }) { } } - toggleSpyPanel() { - return testSubjects.click('spyToggleButton'); + async toggleSpyPanel() { + await testSubjects.click('spyToggleButton'); } - getMetric() { - return remote - .setFindTimeout(2000) - .findByCssSelector('div[ng-controller="KbnMetricVisController"]') - .getVisibleText(); + async getMetric() { + const metricElement = await find.byCssSelector('div[ng-controller="KbnMetricVisController"]'); + return await metricElement.getVisibleText(); } - getGaugeValue() { - return remote - .setFindTimeout(2000) - .findAllByCssSelector('visualize .chart svg') - .getVisibleText(); + async getGaugeValue() { + const elements = await find.allByCssSelector('visualize .chart svg'); + return await Promise.all(elements.map(async element => await element.getVisibleText())); } - clickMetricEditor() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('button[aria-label="Open Editor"]') - .click(); + async clickMetricEditor() { + await find.clickByCssSelector('button[aria-label="Open Editor"]'); } - clickNewSearch() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('.list-group-item a') - .click(); + async clickNewSearch() { + await find.clickByCssSelector('.list-group-item a'); } - setValue(newValue) { - return remote - .setFindTimeout(defaultFindTimeout * 2) - .findByCssSelector('button[ng-click="numberListCntr.add()"]') - .click() - .then(() => { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('input[ng-model="numberListCntr.getList()[$index]"]') - .clearValue(); - }) - .then(() => { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('input[ng-model="numberListCntr.getList()[$index]"]') - .type(newValue); - }); + async setValue(newValue) { + await find.clickByCssSelector('button[ng-click="numberListCntr.add()"]', defaultFindTimeout * 2); + const input = await find.byCssSelector('input[ng-model="numberListCntr.getList()[$index]"]'); + await input.clearValue(); + await input.type(newValue); } - clickSavedSearch() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('li[ng-click="stepTwoMode=\'saved\'"]') - .click(); + async clickSavedSearch() { + await find.clickByCssSelector('li[ng-click="stepTwoMode=\'saved\'"]'); } - selectSearch(searchName) { - return remote - .setFindTimeout(defaultFindTimeout) - .findByLinkText(searchName) - .click(); + async selectSearch(searchName) { + await find.clickByLinkText(searchName); } - - getErrorMessage() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('.item>h4') - .getVisibleText(); + async getErrorMessage() { + const element = await find.byCssSelector('.item>h4'); + return await element.getVisibleText(); } // clickBucket(bucketType) 'X-Axis', 'Split Area', 'Split Chart' - clickBucket(bucketName) { - return remote - .setFindTimeout(defaultFindTimeout) - .findAllByCssSelector('li.list-group-item.list-group-menu-item.ng-binding.ng-scope') - .then(chartTypes => { - log.debug('found bucket types ' + chartTypes.length); - - function getChartType(chart) { - return chart - .getVisibleText() - .then(chartString => { - //log.debug(chartString); - if (chartString === bucketName) { - chart.click(); - } - }); + async clickBucket(bucketName) { + const chartTypes = await retry.try( + async () => await find.allByCssSelector('li.list-group-item.list-group-menu-item.ng-binding.ng-scope')); + log.debug('found bucket types ' + chartTypes.length); + + async function getChartType(chart) { + const chartString = await chart.getVisibleText(); + if (chartString === bucketName) { + await chart.click(); } - const getChartTypesPromises = chartTypes.map(getChartType); - return Promise.all(getChartTypesPromises); - }); + } + const getChartTypesPromises = chartTypes.map(getChartType); + await Promise.all(getChartTypesPromises); } - selectAggregation(myString) { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('vis-editor-agg-params:not(.ng-hide) option[label="' + myString + '"]') - .click(); + async selectAggregation(myString) { + return find.clickByCssSelector('vis-editor-agg-params:not(.ng-hide) option[label="' + myString + '"]'); } - getField() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('.ng-valid-required[name="field"] .ui-select-match-text') - .getVisibleText(); + async getField() { + const field = await retry.try( + async () => await find.byCssSelector('.ng-valid-required[name="field"] .ui-select-match-text')); + return await field.getVisibleText(); } - selectField(fieldValue, groupName = 'buckets') { - return retry.try(function tryingForTime() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector(`[group-name="${groupName}"] .ui-select-container`) - .click() - .then(() => { - return remote - .findByCssSelector(`[group-name="${groupName}"] input.ui-select-search`) - .type(fieldValue) - .pressKeys('\uE006'); - }); + async selectField(fieldValue, groupName = 'buckets') { + await retry.try(async () => { + await find.clickByCssSelector(`[group-name="${groupName}"] .ui-select-container`); + const input = await find.byCssSelector(`[group-name="${groupName}"] input.ui-select-search`); + await input.type(fieldValue); + await remote.pressKeys('\uE006'); }); } - selectFieldById(fieldValue, id) { - return retry.try(function tryingForTime() { - return remote - .setFindTimeout(defaultFindTimeout) - // the css below should be more selective - .findByCssSelector(`#${id} > option[label="${fieldValue}"]`) - .click(); - }); + async selectFieldById(fieldValue, id) { + await find.clickByCssSelector(`#${id} > option[label="${fieldValue}"]`); } - orderBy(fieldValue) { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('select.form-control.ng-pristine.ng-valid.ng-untouched.ng-valid-required[ng-model="agg.params.orderBy"] ' + - 'option.ng-binding.ng-scope:contains("' + fieldValue + '")' - ) - .click(); + async orderBy(fieldValue) { + await find.clickByCssSelector( + 'select.form-control.ng-pristine.ng-valid.ng-untouched.ng-valid-required[ng-model="agg.params.orderBy"] ' + + 'option.ng-binding.ng-scope:contains("' + fieldValue + '")'); } - selectOrderBy(fieldValue) { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('select[name="orderBy"] > option[value="' + fieldValue + '"]') - .click(); + async selectOrderBy(fieldValue) { + await find.clickByCssSelector('select[name="orderBy"] > option[value="' + fieldValue + '"]'); } - - getInterval() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('select[ng-model="agg.params.interval"]') - .getProperty('selectedIndex') - .then(selectedIndex => { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('select[ng-model="agg.params.interval"] option:nth-child(' + (selectedIndex + 1) + ')') - .getProperty('label'); - }); + async getInterval() { + const select = await find.byCssSelector('select[ng-model="agg.params.interval"]'); + const selectedIndex = await select.getProperty('selectedIndex'); + const intervalElement = await find.byCssSelector( + `select[ng-model="agg.params.interval"] option:nth-child(${(selectedIndex + 1)})`); + return await intervalElement.getProperty('label'); } - setInterval(newValue) { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('select[ng-model="agg.params.interval"]') - .type(newValue); + async setInterval(newValue) { + const input = await find.byCssSelector('select[ng-model="agg.params.interval"]'); + await input.type(newValue); } - setNumericInterval(newValue) { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('input[name="interval"]') - .type(newValue); + async setNumericInterval(newValue) { + const input = await find.byCssSelector('input[name="interval"]'); + await input.type(newValue); } - clickGo() { - return testSubjects.click('visualizeEditorRenderButton') - .then(function () { - return PageObjects.header.waitUntilLoadingHasFinished(); - }); + async clickGo() { + await testSubjects.click('visualizeEditorRenderButton'); + await PageObjects.header.waitUntilLoadingHasFinished(); } - clickOptions() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByPartialLinkText('Options') - .click(); + async clickOptions() { + await find.clickByPartialLinkText('Options'); } - selectWMS() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('input[name="wms.enabled"]') - .click(); + async selectWMS() { + await find.clickByCssSelector('input[name="wms.enabled"]'); } + async saveVisualization(vizName) { + await testSubjects.click('visualizeSaveButton'); + log.debug('saveButton button clicked'); + const visTitle = await find.byName('visTitle'); + await visTitle.type(vizName); + log.debug('click submit button'); + await testSubjects.click('saveVisualizationButton'); + await PageObjects.header.waitUntilLoadingHasFinished(); - saveVisualization(vizName) { - return testSubjects.click('visualizeSaveButton') - .then(() => { - return PageObjects.common.sleep(1000); - }) - .then(() => { - log.debug('saveButton button clicked'); - return remote - .setFindTimeout(defaultFindTimeout) - .findByName('visTitle') - .type(vizName); - }) - // // click save button - .then(() => { - log.debug('click submit button'); - return testSubjects.click('saveVisualizationButton'); - }) - .then(function () { - return PageObjects.header.waitUntilLoadingHasFinished(); - }) - // verify that green message at the top of the page. - // it's only there for about 5 seconds - .then(() => { - return retry.try(function tryingForTime() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('kbn-truncated.toast-message.ng-isolate-scope') - .getVisibleText(); - }); - }); + return await PageObjects.header.getToastMessage(); } - clickLoadSavedVisButton() { + async clickLoadSavedVisButton() { // TODO: Use a test subject selector once we rewrite breadcrumbs to accept each breadcrumb // element as a child instead of building the breadcrumbs dynamically. - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('[href="#/visualize"]') - .click(); + await find.clickByCssSelector('[href="#/visualize"]'); } - filterVisByName(vizName) { - return remote - .findByCssSelector('input[name="filter"]') - .click() + async filterVisByName(vizName) { + const input = await find.byCssSelector('input[name="filter"]'); + await input.click(); // can't uses dashes in saved visualizations when filtering // or extended character sets // https://github.com/elastic/kibana/issues/6300 - .type(vizName.replace('-',' ')); + await input.type(vizName.replace('-',' ')); } - clickVisualizationByName(vizName) { + async clickVisualizationByName(vizName) { log.debug('clickVisualizationByLinkText(' + vizName + ')'); return retry.try(function tryingForTime() { @@ -495,404 +319,233 @@ export function VisualizePageProvider({ getService, getPageObjects }) { // this starts by clicking the Load Saved Viz button, not from the // bottom half of the "Create a new visualization Step 1" page - loadSavedVisualization(vizName) { - return this.clickLoadSavedVisButton() - .then(() => this.openSavedVisualization(vizName)); + async loadSavedVisualization(vizName) { + await this.clickLoadSavedVisButton(); + await this.openSavedVisualization(vizName); } - openSavedVisualization(vizName) { - return this.clickVisualizationByName(vizName); + async openSavedVisualization(vizName) { + await this.clickVisualizationByName(vizName); } - getXAxisLabels() { - return remote - .setFindTimeout(defaultFindTimeout) - .findAllByCssSelector('.x > g') - .then(chartTypes => { - function getChartType(chart) { - return chart - .getVisibleText(); - } - const getChartTypesPromises = chartTypes.map(getChartType); - return Promise.all(getChartTypesPromises); - }) - .then(texts => { - // log.debug('returning types array ' + texts + ' array length =' + texts.length); - return texts; - }); + async getXAxisLabels() { + const chartTypes = await find.allByCssSelector('.x > g'); + async function getChartType(chart) { + return await chart.getVisibleText(); + } + const getChartTypesPromises = chartTypes.map(getChartType); + return await Promise.all(getChartTypesPromises); } - - getYAxisLabels() { - return remote - .setFindTimeout(defaultFindTimeout) - .findAllByCssSelector('.y > g') - .then(chartTypes => { - function getChartType(chart) { - return chart - .getVisibleText(); - } - const getChartTypesPromises = chartTypes.map(getChartType); - return Promise.all(getChartTypesPromises); - }) - .then(texts => { - // log.debug('returning types array ' + texts + ' array length =' + texts.length); - return texts; - }); + async getYAxisLabels() { + const chartTypes = await find.allByCssSelector('.y > g'); + const getChartTypesPromises = chartTypes.map(async chart => await chart.getVisibleText()); + return await Promise.all(getChartTypesPromises); } - /* ** This method gets the chart data and scales it based on chart height and label. ** Returns an array of height values */ - getAreaChartData(aggregateName) { - const chartData = []; - let tempArray = []; - let chartSections = 0; - let yAxisLabel = 0; - let yAxisHeight = 0; - + async getAreaChartData(aggregateName) { // 1). get the maximim chart Y-Axis marker value - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('div.y-axis-div-wrapper > div > svg > g > g:last-of-type') - .getVisibleText() - .then(function (yLabel) { - // since we're going to use the y-axis 'last' (top) label as a number to - // scale the chart pixel data, we need to clean out commas and % marks. - yAxisLabel = yLabel.replace(/(%|,)/g, ''); - log.debug('yAxisLabel = ' + yAxisLabel); - return yLabel; - }) - // 2). find and save the y-axis pixel size (the chart height) - .then(function () { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('rect.background') // different here - .getAttribute('height'); - }) - .then(function (chartH) { - yAxisHeight = chartH; - log.debug('height --------- ' + yAxisHeight); - }) - .then(function () { - return remote.setFindTimeout(defaultFindTimeout * 2) - .findByCssSelector('path[data-label="' + aggregateName + '"]') - .getAttribute('d'); - }) - .then(function (data) { - log.debug(data); - // This area chart data starts with a 'M'ove to a x,y location, followed - // by a bunch of 'L'ines from that point to the next. Those points are - // the values we're going to use to calculate the data values we're testing. - // So git rid of the one 'M' and split the rest on the 'L's. - tempArray = data.replace('M','').split('L'); - chartSections = tempArray.length / 2; - log.debug('chartSections = ' + chartSections + ' height = ' + yAxisHeight + ' yAxisLabel = ' + yAxisLabel); - for (let i = 0; i < chartSections; i++) { - chartData[i] = Math.round((yAxisHeight - tempArray[i].split(',')[1]) / yAxisHeight * yAxisLabel); - log.debug('chartData[i] =' + chartData[i]); - } - return chartData; - }); + const maxChartYAxisElement = await retry.try( + async () => await find.byCssSelector('div.y-axis-div-wrapper > div > svg > g > g:last-of-type')); + + const yLabel = await maxChartYAxisElement.getVisibleText(); + // since we're going to use the y-axis 'last' (top) label as a number to + // scale the chart pixel data, we need to clean out commas and % marks. + const yAxisLabel = yLabel.replace(/(%|,)/g, ''); + log.debug('yAxisLabel = ' + yAxisLabel); + + const rectangle = await find.byCssSelector('rect.background'); + const yAxisHeight = await rectangle.getAttribute('height'); + log.debug(`height --------- ${yAxisHeight}`); + + const path = await retry.try( + async () => await find.byCssSelector(`path[data-label="${aggregateName}"]`, defaultFindTimeout * 2)); + const data = await path.getAttribute('d'); + log.debug(data); + // This area chart data starts with a 'M'ove to a x,y location, followed + // by a bunch of 'L'ines from that point to the next. Those points are + // the values we're going to use to calculate the data values we're testing. + // So git rid of the one 'M' and split the rest on the 'L's. + const tempArray = data.replace('M','').split('L'); + const chartSections = tempArray.length / 2; + log.debug('chartSections = ' + chartSections + ' height = ' + yAxisHeight + ' yAxisLabel = ' + yAxisLabel); + const chartData = []; + for (let i = 0; i < chartSections; i++) { + chartData[i] = Math.round((yAxisHeight - tempArray[i].split(',')[1]) / yAxisHeight * yAxisLabel); + log.debug('chartData[i] =' + chartData[i]); + } + return chartData; } - // The current test shows dots, not a line. This function gets the dots and normalizes their height. - getLineChartData(cssPart, axis = 'ValueAxis-1') { - let yAxisLabel = 0; - let yAxisHeight; - + async getLineChartData(cssPart, axis = 'ValueAxis-1') { // 1). get the maximim chart Y-Axis marker value - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector(`div.y-axis-div-wrapper > div > svg > g.${axis} > g:last-of-type`) - .getVisibleText() - .then(function (yLabel) { - yAxisLabel = yLabel.replace(/,/g, ''); - log.debug('yAxisLabel = ' + yAxisLabel); - return yLabel; - }) + const maxYAxisMarker = await retry.try( + async () => await find.byCssSelector(`div.y-axis-div-wrapper > div > svg > g.${axis} > g:last-of-type`)); + const yLabel = await maxYAxisMarker.getVisibleText(); + const yAxisLabel = yLabel.replace(/,/g, ''); + // 2). find and save the y-axis pixel size (the chart height) - .then(function getRect() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('clipPath rect') - .getAttribute('height') - .then(function (theHeight) { - yAxisHeight = theHeight; - log.debug('theHeight = ' + theHeight); - return theHeight; - }); - }) - // 3). get the chart-wrapper elements - .then(function getChartWrapper() { - return remote - .setFindTimeout(defaultFindTimeout * 2) - .findAllByCssSelector(`.chart-wrapper circle[${cssPart}]`) - .then(function (chartTypes) { - - // 5). for each chart element, find the green circle, then the cy position - function getChartType(chart) { - return chart - .getAttribute('cy') - .then(function (cy) { - // log.debug(' yAxisHeight=' + yAxisHeight + ' yAxisLabel=' + yAxisLabel + ' cy=' + cy + - // ' ((yAxisHeight - cy)/yAxisHeight * yAxisLabel)=' + ((yAxisHeight - cy) / yAxisHeight * yAxisLabel)); - return Math.round((yAxisHeight - cy) / yAxisHeight * yAxisLabel); - }); - } - - // 4). pass the chartTypes to the getChartType function - const getChartTypesPromises = chartTypes.map(getChartType); - return Promise.all(getChartTypesPromises); - }); - }) - - .then(function (yCoords) { - return yCoords; - }); - } + const rectangle = await find.byCssSelector('clipPath rect'); + const theHeight = await rectangle.getAttribute('height'); + const yAxisHeight = theHeight; + log.debug('theHeight = ' + theHeight); + // 3). get the chart-wrapper elements + const chartTypes = await retry.try( + async () => await find.allByCssSelector(`.chart-wrapper circle[${cssPart}]`, defaultFindTimeout * 2)); - // this is ALMOST identical to DiscoverPage.getBarChartData - getBarChartData() { - let yAxisLabel = 0; - let yAxisHeight; + // 5). for each chart element, find the green circle, then the cy position + async function getChartType(chart) { + const cy = await chart.getAttribute('cy'); + return Math.round((yAxisHeight - cy) / yAxisHeight * yAxisLabel); + } + + // 4). pass the chartTypes to the getChartType function + const getChartTypesPromises = chartTypes.map(getChartType); + return await Promise.all(getChartTypesPromises); + } + // this is ALMOST identical to DiscoverPage.getBarChartData + async getBarChartData() { // 1). get the maximim chart Y-Axis marker value - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('div.y-axis-div-wrapper > div > svg > g > g:last-of-type') - .then(function setYAxisLabel(y) { - return y - .getVisibleText() - .then(function (yLabel) { - yAxisLabel = yLabel.replace(',', ''); - log.debug('yAxisLabel = ' + yAxisLabel); - return yLabel; - }); - }) + const maxYAxisChartMarker = await retry.try( + async () => await find.byCssSelector('div.y-axis-div-wrapper > div > svg > g > g:last-of-type')); + + const yLabel = await maxYAxisChartMarker.getVisibleText(); + const yAxisLabel = yLabel.replace(',', ''); + log.debug('yAxisLabel = ' + yAxisLabel); + // 2). find and save the y-axis pixel size (the chart height) - .then(function getRect() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('rect.background') - .then(function getRectHeight(chartAreaObj) { - return chartAreaObj - .getAttribute('height') - .then(function (theHeight) { - yAxisHeight = theHeight; - log.debug('theHeight = ' + theHeight); - return theHeight; - }); - }); - }) + const chartAreaObj = await find.byCssSelector('rect.background'); + const yAxisHeight = await chartAreaObj.getAttribute('height'); + // 3). get the chart-wrapper elements - .then(function () { - return remote - .setFindTimeout(defaultFindTimeout * 2) - // #kibana-body > div.content > div > div > div > div.vis-editor-canvas > visualize > div.visualize-chart > div > div.vis-col-wrapper > div.chart-wrapper > div > svg > g > g.series.\30 > rect:nth-child(1) - .findAllByCssSelector('svg > g > g.series > rect') // rect - .then(function (chartTypes) { - function getChartType(chart) { - return chart - .getAttribute('fill') - .then(function (fillColor) { - // we're getting the default count color from defaults.js - if (fillColor === '#00a69b') { - return chart - .getAttribute('height') - .then(function (barHeight) { - return Math.round(barHeight / yAxisHeight * yAxisLabel); - }); - } - }); - } - const getChartTypesPromises = chartTypes.map(getChartType); - return Promise.all(getChartTypesPromises); - }) - .then(function (bars) { - return bars; - }); - }); + const chartTypes = await find.allByCssSelector('svg > g > g.series > rect'); + async function getChartType(chart) { + const fillColor = await chart.getAttribute('fill'); + + // we're getting the default count color from defaults.js + if (fillColor === '#00a69b') { + const barHeight = await chart.getAttribute('height'); + return Math.round(barHeight / yAxisHeight * yAxisLabel); + } + } + const getChartTypesPromises = chartTypes.map(getChartType); + return await Promise.all(getChartTypesPromises); } - getHeatmapData() { - // 1). get the maximim chart Y-Axis marker value - return remote - .setFindTimeout(defaultFindTimeout * 2) - // #kibana-body > div.content > div > div > div > div.vis-editor-canvas > visualize > div.visualize-chart > div > div.vis-col-wrapper > div.chart-wrapper > div > svg > g > g.series.\30 > rect:nth-child(1) - .findAllByCssSelector('svg > g > g.series rect') // rect - .then(function (chartTypes) { - log.debug('rects=' + chartTypes); - function getChartType(chart) { - return chart - .getAttribute('data-label'); - } - const getChartTypesPromises = chartTypes.map(getChartType); - return Promise.all(getChartTypesPromises); - }) - .then(function (labels) { - log.debug('labels=' + labels); - return labels; - }); + async getHeatmapData() { + const chartTypes = await retry.try( + async () => await find.allByCssSelector('svg > g > g.series rect', defaultFindTimeout * 2)); + log.debug('rects=' + chartTypes); + async function getChartType(chart) { + return await chart.getAttribute('data-label'); + } + const getChartTypesPromises = chartTypes.map(getChartType); + return await Promise.all(getChartTypesPromises); } - getPieChartData() { - // 1). get the maximim chart Y-Axis marker value - return remote - .setFindTimeout(defaultFindTimeout * 2) - // path.slice:nth-child(11) - .findAllByCssSelector('path.slice') - .then(function (chartTypes) { - function getChartType(chart) { - return chart - .getAttribute('d') - .then(function (slice) { - return slice; - }); - } - const getChartTypesPromises = chartTypes.map(getChartType); - return Promise.all(getChartTypesPromises); - }) - .then(function (slices) { - log.debug('slices=' + slices); - return slices; - }); + async getPieChartData() { + const chartTypes = await find.allByCssSelector('path.slice', defaultFindTimeout * 2); + + const getChartTypesPromises = chartTypes.map(async chart => await chart.getAttribute('d')); + return await Promise.all(getChartTypesPromises); + } + + async getChartAreaWidth() { + const rect = await retry.try(async () => find.byCssSelector('clipPath rect')); + return await rect.getAttribute('width'); } - getChartAreaWidth() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('clipPath rect') - .getAttribute('width'); + async getChartAreaHeight() { + const rect = await retry.try(async () => find.byCssSelector('clipPath rect')); + return await rect.getAttribute('height'); } - getChartAreaHeight() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('clipPath rect') - .getAttribute('height'); + + async getDataTableData() { + const dataTable = await retry.try( + async () => find.byCssSelector('table.table.table-condensed tbody', defaultFindTimeout * 2)); + return await dataTable.getVisibleText(); } - getDataTableData() { - return remote - .setFindTimeout(defaultFindTimeout * 2) - .findByCssSelector('table.table.table-condensed tbody') - .getVisibleText(); + async getMarkdownData() { + const markdown = await retry.try(async () => find.byCssSelector('visualize.ng-isolate-scope')); + return await markdown.getVisibleText(); } - getMarkdownData() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('visualize.ng-isolate-scope') - .getVisibleText(); + async clickColumns() { + await find.clickByCssSelector('div.schemaEditors.ng-scope > div > div > button:nth-child(2)'); } - clickColumns() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('div.schemaEditors.ng-scope > div > div > button:nth-child(2)') - .click(); + async waitForVisualization() { + return await find.byCssSelector('visualization'); } - waitForToastMessageGone() { - return retry.try(function tryingForTime() { - return remote - .setFindTimeout(100) - .findAllByCssSelector('kbn-truncated.toast-message.ng-isolate-scope') - .then(function toastMessage(messages) { - if (messages.length > 0) { - throw new Error('waiting for toast message to clear'); - } else { - log.debug('now messages = 0 "' + messages + '"'); - return messages; - } - }); - }); + async getZoomSelectors(zoomSelector) { + return await find.allByCssSelector(zoomSelector); } - waitForVisualization() { - return remote - .setFindTimeout(defaultFindTimeout) - .findByCssSelector('visualization'); + async clickMapButton(zoomSelector) { + const zooms = await this.getZoomSelectors(zoomSelector); + await Promise.all(zooms.map(async zoom => await zoom.click())); + await PageObjects.header.waitUntilLoadingHasFinished(); } - clickMapButton(zoomSelector) { - return remote - .setFindTimeout(defaultFindTimeout) - .findAllByCssSelector(zoomSelector) - .click() - .then(() => { - return PageObjects.common.sleep(1000); - }) - .then(() => { - return PageObjects.header.waitUntilLoadingHasFinished(); - }); + async clickMapZoomIn() { + await this.clickMapButton('a.leaflet-control-zoom-in'); } - clickMapZoomIn() { - return this.clickMapButton('a.leaflet-control-zoom-in'); + async clickMapZoomOut() { + await this.clickMapButton('a.leaflet-control-zoom-out'); } - clickMapZoomOut() { - return this.clickMapButton('a.leaflet-control-zoom-out'); + async getMapZoomEnabled(zoomSelector) { + const zooms = await this.getZoomSelectors(zoomSelector); + const classAttributes = await Promise.all(zooms.map(async zoom => await zoom.getAttribute('class'))); + return !classAttributes.join('').includes('leaflet-disabled'); } - getMapZoomEnabled(zoomSelector) { - return remote - .setFindTimeout(defaultFindTimeout) - .findAllByCssSelector(zoomSelector) - .getAttribute('class') - .then((element) => { - return !element.toString().includes('leaflet-disabled'); + async zoomAllTheWayOut() { + // we can tell we're at level 1 because zoom out is disabled + return await retry.try(async () => { + await this.clickMapZoomOut(); + const enabled = await this.getMapZoomOutEnabled(); + //should be able to zoom more as current config has 0 as min level. + if (enabled) { + throw new Error('Not fully zoomed out yet'); + } }); } - getMapZoomInEnabled() { - return this.getMapZoomEnabled('a.leaflet-control-zoom-in'); + async getMapZoomInEnabled() { + return await this.getMapZoomEnabled('a.leaflet-control-zoom-in'); } - getMapZoomOutEnabled() { - return this.getMapZoomEnabled('a.leaflet-control-zoom-out'); + async getMapZoomOutEnabled() { + return await this.getMapZoomEnabled('a.leaflet-control-zoom-out'); } - clickMapFitDataBounds() { - return this.clickMapButton('a.fa-crop'); + async clickMapFitDataBounds() { + return await this.clickMapButton('a.fa-crop'); } - getTileMapData() { - return remote - .setFindTimeout(defaultFindTimeout) - .findAllByCssSelector('path.leaflet-clickable') - .then((chartTypes) => { - - function getChartType(chart) { - let color; - let radius; - return chart.getAttribute('stroke') - .then((stroke) => { - color = stroke; - }) - .then(() => { - return chart.getAttribute('d'); - }) - .then((d) => { - // scale the radius up (sometimes less than 1) and then round to int - radius = d.replace(/.*A(\d+\.\d+),.*/,'$1') * 10; - radius = Math.round(radius); - }) - .then(() => { - return { color: color, radius: radius }; - }); - } - const getChartTypesPromises = chartTypes.map(getChartType); - return Promise.all(getChartTypesPromises); - }) - .then((circles) => { - return circles; - }); + async getTileMapData() { + const chartTypes = await find.allByCssSelector('path.leaflet-clickable'); + async function getChartType(chart) { + const color = await chart.getAttribute('stroke'); + const d = await chart.getAttribute('d'); + // scale the radius up (sometimes less than 1) and then round to int + let radius = d.replace(/.*A(\d+\.\d+),.*/,'$1') * 10; + radius = Math.round(radius); + return { color, radius }; + } + const getChartTypesPromises = chartTypes.map(getChartType); + return await Promise.all(getChartTypesPromises); } } diff --git a/test/functional/services/find.js b/test/functional/services/find.js index a93b4e7dd644..8b5cb453dea2 100644 --- a/test/functional/services/find.js +++ b/test/functional/services/find.js @@ -2,64 +2,118 @@ export function FindProvider({ getService }) { const log = getService('log'); const config = getService('config'); const remote = getService('remote'); + const retry = getService('retry'); const defaultFindTimeout = config.get('timeouts.find'); class Find { + async withTimeout(timeout, block) { + try { + const remoteWithTimeout = remote.setFindTimeout(timeout); + return await block(remoteWithTimeout); + } finally { + remote.setFindTimeout(defaultFindTimeout); + } + } + + async ensureElementWithTimeout(timeout, getElementFunction) { + try { + const remoteWithTimeout = remote.setFindTimeout(timeout); + return await retry.try(async () => { + const element = await getElementFunction(remoteWithTimeout); + // Calling any method forces a staleness check + element.isEnabled(); + return element; + }); + } finally { + remote.setFindTimeout(defaultFindTimeout); + } + } + + async byName(selector, timeout = defaultFindTimeout) { + log.debug(`find.byName(${selector})`); + return await this.ensureElementWithTimeout(timeout, async remote => { + return await remote.findByName(selector); + }); + } + async byCssSelector(selector, timeout = defaultFindTimeout) { log.debug(`findByCssSelector ${selector}`); - const remoteWithTimeout = remote.setFindTimeout(timeout); - const element = await remoteWithTimeout.findByCssSelector(selector); - remoteWithTimeout.setFindTimeout(defaultFindTimeout); - return element; + return await this.ensureElementWithTimeout(timeout, async remote => { + return await remote.findByCssSelector(selector); + }); } async allByCssSelector(selector, timeout = defaultFindTimeout) { log.debug('in findAllByCssSelector: ' + selector); - const remoteWithTimeout = remote.setFindTimeout(timeout); - let elements = await remoteWithTimeout.findAllByCssSelector(selector); - remoteWithTimeout.setFindTimeout(defaultFindTimeout); - if (!elements) elements = []; - log.debug(`Found ${elements.length} for selector ${selector}`); - return elements; + return await this.withTimeout(timeout, async remote => { + return await retry.try(async () => { + let elements = await remote.findAllByCssSelector(selector); + if (!elements) elements = []; + // Force isStale checks for all the retrieved elements. + await Promise.all(elements.map(async element => await element.isEnabled())); + log.debug(`Found ${elements.length} for selector ${selector}`); + return elements; + }); + }); } async displayedByCssSelector(selector, timeout = defaultFindTimeout) { log.debug('in displayedByCssSelector: ' + selector); - const remoteWithTimeout = remote.setFindTimeout(timeout); - const element = await remoteWithTimeout.findDisplayedByCssSelector(selector); - remoteWithTimeout.setFindTimeout(defaultFindTimeout); - return element; + return await this.ensureElementWithTimeout(timeout, async remote => { + return await remote.findDisplayedByCssSelector(selector); + }); + } + + async byPartialLinkText(partialLinkText, timeout = defaultFindTimeout) { + log.debug(`find.byPartialLinkText(${partialLinkText})`); + return await this.ensureElementWithTimeout(timeout, async remote => { + return await remote.findByPartialLinkText(partialLinkText); + }); } - async existsByLinkText(linkText) { + async exists(findFunction, timeout = 1000) { + return await this.withTimeout(timeout, async remote => { + try { + await findFunction(remote); + return true; + } catch (error) { + return false; + } + }); + } + + async existsByLinkText(linkText, timeout = 1000) { log.debug(`existsByLinkText ${linkText}`); - const remoteWithTimeout = remote.setFindTimeout(1000); - const exists = await remoteWithTimeout.findDisplayedByLinkText(linkText) - .then(() => true) - .catch(() => false); - remoteWithTimeout.setFindTimeout(defaultFindTimeout); - return exists; + return await this.exists(async remote => await remote.findDisplayedByLinkText(linkText), timeout); } - async existsByDisplayedByCssSelector(selector) { + async existsByDisplayedByCssSelector(selector, timeout = 1000) { log.debug(`existsByDisplayedByCssSelector ${selector}`); - const remoteWithTimeout = remote.setFindTimeout(1000); - const exists = await remoteWithTimeout.findDisplayedByCssSelector(selector) - .then(() => true) - .catch(() => false); - remoteWithTimeout.setFindTimeout(defaultFindTimeout); - return exists; + return await this.exists(async remote => await remote.findDisplayedByCssSelector(selector), timeout); } - async existsByCssSelector(selector) { + async existsByCssSelector(selector, timeout = 1000) { log.debug(`existsByCssSelector ${selector}`); - const remoteWithTimeout = remote.setFindTimeout(1000); - const exists = await remoteWithTimeout.findByCssSelector(selector) - .then(() => true) - .catch(() => false); - remoteWithTimeout.setFindTimeout(defaultFindTimeout); - return exists; + return await this.exists(async remote => await remote.findByCssSelector(selector), timeout); + } + + async clickByPartialLinkText(linkText, timeout = defaultFindTimeout) { + log.debug(`clickByPartialLinkText(${linkText})`); + const element = await retry.try(async () => await this.byPartialLinkText(linkText, timeout)); + await element.click(); + } + + async clickByLinkText(linkText, timeout = defaultFindTimeout) { + log.debug(`clickByLinkText(${linkText})`); + const element = await this.byLinkText(linkText, timeout); + await element.click(); + } + + async clickByCssSelector(selector, timeout = defaultFindTimeout) { + log.debug(`clickByCssSelector(${selector})`); + const element = await this.byCssSelector(selector, timeout); + await element.click(); } }