mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Support Cloud Kibana UI testing (#8880)
* Support Cloud Kibana testing * Add xpack file * Use kibana for kibana server user, Don't log password
This commit is contained in:
parent
b28772ed58
commit
4a38a3f070
12 changed files with 154 additions and 49 deletions
|
@ -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=<your password here>
|
||||
|
||||
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=<your password here>
|
||||
npm run test:ui:runner
|
||||
```
|
||||
|
||||
##### Browser Automation Notes
|
||||
|
||||
- Using Page Objects pattern (https://theintern.github.io/intern/#writing-functional-test)
|
||||
|
|
|
@ -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'
|
||||
]);
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
36
test/functional/apps/xpack/index.js
Normal file
36
test/functional/apps/xpack/index.js
Normal file
|
@ -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();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -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',
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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'
|
||||
};
|
||||
|
|
|
@ -25,6 +25,8 @@ import {
|
|||
esClient
|
||||
} from '../index';
|
||||
|
||||
import PageObjects from './index';
|
||||
|
||||
import {
|
||||
Log,
|
||||
Try
|
||||
|
@ -114,31 +116,22 @@ export default class Common {
|
|||
if (testStatusPage !== false) {
|
||||
self.debug('self.checkForKibanaApp()');
|
||||
return self.checkForKibanaApp()
|
||||
.then(function (kibanaLoaded) {
|
||||
self.debug('kibanaLoaded = ' + kibanaLoaded);
|
||||
if (!kibanaLoaded) {
|
||||
var msg = 'Kibana is not loaded, retrying';
|
||||
self.debug(msg);
|
||||
throw new Error(msg);
|
||||
.then(function (kibanaApp) {
|
||||
self.debug('kibanaApp = ' + kibanaApp);
|
||||
if (kibanaApp === 'login') {
|
||||
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();
|
||||
});
|
||||
} else {
|
||||
return self.remote.getCurrentUrl();
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
return self.remote.getCurrentUrl();
|
||||
})
|
||||
.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)
|
||||
.then(function () {
|
||||
return self.remote.getCurrentUrl();
|
||||
});
|
||||
} else {
|
||||
return currentUrl;
|
||||
return self.remote.getCurrentUrl();
|
||||
}
|
||||
})
|
||||
.then(function (currentUrl) {
|
||||
|
@ -154,7 +147,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) {
|
||||
|
@ -236,7 +233,7 @@ export default class Common {
|
|||
.then(function (app) {
|
||||
var appId = app.id;
|
||||
self.debug('current application: ' + appId);
|
||||
return appId === 'kibana';
|
||||
return appId;
|
||||
})
|
||||
.catch(function (err) {
|
||||
self.debug('kibana check failed');
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
35
test/support/page_objects/monitoring_page.js
Normal file
35
test/support/page_objects/monitoring_page.js
Normal file
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue