diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dcc1eb56193ca..20030f9edea74 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -258,6 +258,24 @@ To execute the front-end browser tests, enter the following. This requires the s npm run test:ui:runner ``` +To run these browser tests against against some other Elasticsearch and Kibana instance you can set these environment variables and then run the test runner. +Here's an example to run against an Elastic Cloud instance (note that you should run the same branch of tests as the version of Kibana you're testing); + +```bash +export TEST_KIBANA_PROTOCOL=https +export TEST_KIBANA_HOSTNAME=9249d04b1186b3e7bbe11ea60df4f963.us-east-1.aws.found.io +export TEST_KIBANA_PORT=443 +export TEST_KIBANA_USER=elastic +export TEST_KIBANA_PASS= + +export TEST_ES_PROTOCOL=http +export TEST_ES_HOSTNAME=aaa5d22032d76805fcce724ed9d9f5a2.us-east-1.aws.found.io +export TEST_ES_PORT=9200 +export TEST_ES_USER=elastic +export TEST_ES_PASS= +npm run test:ui:runner +``` + ##### Browser Automation Notes - Using Page Objects pattern (https://theintern.github.io/intern/#writing-functional-test) diff --git a/tasks/test.js b/tasks/test.js index 185efffdb00d0..0489693d701dd 100644 --- a/tasks/test.js +++ b/tasks/test.js @@ -62,12 +62,12 @@ module.exports = function (grunt) { grunt.registerTask('test:ui:server', [ 'esvm:ui', - 'run:testUIDevServer', - 'run:devChromeDriver:keepalive' + 'run:testUIDevServer:keepalive' ]); grunt.registerTask('test:ui:runner', [ 'clean:screenshots', + 'run:devChromeDriver', 'intern:dev' ]); diff --git a/test/functional/apps/discover/_shared_links.js b/test/functional/apps/discover/_shared_links.js index 875a40efe2b3a..899d050fbd286 100644 --- a/test/functional/apps/discover/_shared_links.js +++ b/test/functional/apps/discover/_shared_links.js @@ -20,6 +20,11 @@ bdd.describe('shared links', function describeIndexTests() { bdd.before(function () { baseUrl = PageObjects.common.getHostPort(); + PageObjects.common.debug('baseUrl = ' + baseUrl); + // browsers don't show the ':port' if it's 80 or 443 so we have to + // remove that part so we can get a match in the tests. + baseUrl = baseUrl.replace(':80','').replace(':443',''); + PageObjects.common.debug('New baseUrl = ' + baseUrl); var fromTime = '2015-09-19 06:31:44.000'; var toTime = '2015-09-23 18:31:44.000'; diff --git a/test/functional/apps/visualize/_data_table.js b/test/functional/apps/visualize/_data_table.js index 1cd815e11a090..1ec25f0006b2f 100644 --- a/test/functional/apps/visualize/_data_table.js +++ b/test/functional/apps/visualize/_data_table.js @@ -77,11 +77,13 @@ bdd.describe('visualize app', function describeIndexTests() { '8,000 2,863', '10,000 147', '12,000 148', '14,000 129', '16,000 161', '18,000 137' ]; - return PageObjects.visualize.getDataTableData() - .then(function showData(data) { - PageObjects.common.debug(data.split('\n')); - PageObjects.common.saveScreenshot('Visualize-data-table'); - expect(data.split('\n')).to.eql(expectedChartData); + return PageObjects.common.try(function () { + return PageObjects.visualize.getDataTableData() + .then(function showData(data) { + PageObjects.common.debug(data.split('\n')); + PageObjects.common.saveScreenshot('Visualize-data-table'); + expect(data.split('\n')).to.eql(expectedChartData); + }); }); }); diff --git a/test/functional/apps/xpack/index.js b/test/functional/apps/xpack/index.js new file mode 100644 index 0000000000000..1865577e34c54 --- /dev/null +++ b/test/functional/apps/xpack/index.js @@ -0,0 +1,36 @@ +import { + bdd, + remote, + defaultTimeout + } from '../../../support'; + +import PageObjects from '../../../support/page_objects'; + +bdd.describe('dismiss x-pack', function () { + this.timeout = defaultTimeout; + + // Putting everything here in 'before' so it doesn't count as a test + // since x-pack may or may not be installed. We just want the banner closed. + bdd.before(function () { + PageObjects.common.debug('check for X-Pack welcome, opt-out, and dismiss it'); + // find class toaster and see if there's any list items in it? + return PageObjects.settings.navigateTo() + .then(() => { + return PageObjects.monitoring.getToasterContents(); + }) + .then((contents) => { + // Welcome to X-Pack! + // Sharing your cluster statistics with us helps us improve. Your data is never shared with anyone. Not interested? Opt out here. + // Dismiss + PageObjects.common.debug('Toast banner contents = ' + contents); + if (contents.includes('X-Pack')) { + return PageObjects.monitoring.clickOptOut() + .then(() => { + return PageObjects.monitoring.dismissWelcome(); + }); + } + }); + + }); + +}); diff --git a/test/functional/index.js b/test/functional/index.js index 985ac56dae834..65bf202538814 100644 --- a/test/functional/index.js +++ b/test/functional/index.js @@ -29,6 +29,7 @@ define(function (require) { }); const apps = [ + 'intern/dojo/node!./apps/xpack', 'intern/dojo/node!./apps/discover', 'intern/dojo/node!./apps/management', 'intern/dojo/node!./apps/visualize', diff --git a/test/server_config.js b/test/server_config.js index e49a567b7ee31..97ecf228f0914 100644 --- a/test/server_config.js +++ b/test/server_config.js @@ -5,21 +5,25 @@ var kibanaURL = '/app/kibana'; module.exports = { servers: { webdriver: { - protocol: process.env.TEST_UI_WEBDRIVER_PROTOCOL || 'http', - hostname: process.env.TEST_UI_WEBDRIVER_HOSTNAME || 'localhost', - port: parseInt(process.env.TEST_UI_WEBDRIVER_PORT, 10) || 4444 + protocol: process.env.TEST_WEBDRIVER_PROTOCOL || 'http', + hostname: process.env.TEST_WEBDRIVER_HOSTNAME || 'localhost', + port: parseInt(process.env.TEST_WEBDRIVER_PORT, 10) || 4444 }, kibana: { - protocol: process.env.TEST_UI_KIBANA_PROTOCOL || 'http', - hostname: process.env.TEST_UI_KIBANA_HOSTNAME || 'localhost', - port: parseInt(process.env.TEST_UI_KIBANA_PORT, 10) || 5620, - auth: shield.kibanaUser.username + ':' + shield.kibanaUser.password + protocol: process.env.TEST_KIBANA_PROTOCOL || 'http', + hostname: process.env.TEST_KIBANA_HOSTNAME || 'localhost', + port: parseInt(process.env.TEST_KIBANA_PORT, 10) || 5620, + auth: shield.kibanaUser.username + ':' + shield.kibanaUser.password, + username: shield.kibanaUser.username, + password: shield.kibanaUser.password }, elasticsearch: { - protocol: process.env.TEST_UI_ES_PROTOCOL || 'http', - hostname: process.env.TEST_UI_ES_HOSTNAME || 'localhost', - port: parseInt(process.env.TEST_UI_ES_PORT, 10) || 9220, - auth: shield.admin.username + ':' + shield.admin.password + protocol: process.env.TEST_ES_PROTOCOL || 'http', + hostname: process.env.TEST_ES_HOSTNAME || 'localhost', + port: parseInt(process.env.TEST_ES_PORT, 10) || 9220, + auth: shield.admin.username + ':' + shield.admin.password, + username: shield.admin.username, + password: shield.admin.password } }, apps: { diff --git a/test/shield.js b/test/shield.js index 826945be5a22a..cdf3d4ef5ff17 100644 --- a/test/shield.js +++ b/test/shield.js @@ -1,16 +1,16 @@ const env = process.env; exports.kibanaUser = { - username: env.SHIELD_KIBANA_USER || 'user', - password: env.SHIELD_KIBANA_USER_PASS || 'notsecure' + username: env.TEST_KIBANA_USER || 'elastic', + password: env.TEST_KIBANA_PASS || 'changeme' }; exports.kibanaServer = { - username: env.SHIELD_KIBANA_SERVER || 'kibana', - password: env.SHIELD_KIBANA_SERVER_PASS || 'notsecure' + username: env.TEST_KIBANA_SERVER_USER || 'kibana', + password: env.TEST_KIBANA_SERVER_PASS || 'changeme' }; exports.admin = { - username: env.SHIELD_ADMIN || 'admin', - password: env.SHIELD_ADMIN_PASS || 'notsecure' + username: env.TEST_ES_USER || 'elastic', + password: env.TEST_ES_PASS || 'changeme' }; diff --git a/test/support/page_objects/common.js b/test/support/page_objects/common.js index 60becb24663eb..a2c11ab47859e 100644 --- a/test/support/page_objects/common.js +++ b/test/support/page_objects/common.js @@ -26,6 +26,8 @@ import { esClient } from '../index'; +import PageObjects from './index'; + import { Log, Try @@ -116,10 +118,10 @@ export default class Common { .then(function (currentUrl) { var loginPage = new RegExp('login').test(currentUrl); if (loginPage) { - self.debug('Found loginPage = ' + loginPage + ', username = ' - + config.servers.kibana.shield.username); - return shieldPage.login(config.servers.kibana.shield.username, - config.servers.kibana.shield.password) + self.debug('Found loginPage username = ' + + config.servers.kibana.username); + return PageObjects.shield.login(config.servers.kibana.username, + config.servers.kibana.password) .then(function () { return self.remote.getCurrentUrl(); }); @@ -140,7 +142,11 @@ export default class Common { // Navigating to Settings when there is a default index pattern has a URL length of 196 // (from debug output). Some other tabs may also be long. But a rather simple configured // visualization is about 1000 chars long. So at least we catch that case. - var navSuccessful = new RegExp(appUrl + '.{0,' + maxAdditionalLengthOnNavUrl + '}$') + + // Browsers don't show the ':port' if it's 80 or 443 so we have to + // remove that part so we can get a match in the tests. + var navSuccessful = new RegExp(appUrl.replace(':80','').replace(':443','') + + '.{0,' + maxAdditionalLengthOnNavUrl + '}$') .test(currentUrl); if (!navSuccessful) { diff --git a/test/support/page_objects/header_page.js b/test/support/page_objects/header_page.js index b3d6ab340ac06..3065f7b1c13f2 100644 --- a/test/support/page_objects/header_page.js +++ b/test/support/page_objects/header_page.js @@ -47,7 +47,7 @@ export default class HeaderPage { } isTimepickerOpen() { - return this.remote.setFindTimeout(defaultFindTimeout) + return this.remote.setFindTimeout(2000) .findDisplayedByCssSelector('.kbn-timepicker') .then(() => true) .catch(() => false); diff --git a/test/support/page_objects/index.js b/test/support/page_objects/index.js index 09ec8b689f47a..50f88057df38f 100644 --- a/test/support/page_objects/index.js +++ b/test/support/page_objects/index.js @@ -7,6 +7,7 @@ import HeaderPage from './header_page'; import SettingsPage from './settings_page'; import ShieldPage from './shield_page'; import VisualizePage from './visualize_page'; +import MonitoringPage from './monitoring_page'; const common = new Common(); const consolePage = new ConsolePage(); @@ -16,6 +17,7 @@ const headerPage = new HeaderPage(); const settingsPage = new SettingsPage(); const shieldPage = new ShieldPage(); const visualizePage = new VisualizePage(); +const monitoringPage = new MonitoringPage(); class PageObjects { @@ -35,6 +37,7 @@ class PageObjects { settingsPage.init(remote); shieldPage.init(remote); visualizePage.init(remote); + monitoringPage.init(remote); } assertInitialized() { @@ -76,6 +79,10 @@ class PageObjects { return this.assertInitialized() && visualizePage; } + get monitoring() { + return this.assertInitialized() && monitoringPage; + } + } export default new PageObjects(); diff --git a/test/support/page_objects/monitoring_page.js b/test/support/page_objects/monitoring_page.js new file mode 100644 index 0000000000000..f06952348419e --- /dev/null +++ b/test/support/page_objects/monitoring_page.js @@ -0,0 +1,35 @@ + +import { + defaultFindTimeout, +} from '../'; + +export default class MonitoringPage { + + init(remote) { + this.remote = remote; + this.findTimeout = this.remote.setFindTimeout(defaultFindTimeout); + } + + getWelcome() { + return this.findTimeout + .findDisplayedByCssSelector('render-directive') + .getVisibleText(); + } + + dismissWelcome() { + return this.remote.setFindTimeout(3000) + .findDisplayedByCssSelector('button.btn-banner') + .click(); + } + + getToasterContents() { + return this.findTimeout + .findByCssSelector('div.toaster-container.ng-isolate-scope') + .getVisibleText(); + } + + clickOptOut() { + return this.findTimeout.findByLinkText('Opt out here').click(); + } + +}