mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Merge branch 'master' of github.com:elastic/kibana into fix/relativePathsInKibana
This commit is contained in:
commit
4e7ae9fafd
61 changed files with 1935 additions and 135 deletions
|
@ -1,2 +1,3 @@
|
|||
src/fixtures
|
||||
test/fixtures/scenarios
|
||||
optimize
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -10,6 +10,7 @@ target
|
|||
.idea
|
||||
*.iml
|
||||
*.log
|
||||
/test/output
|
||||
/esvm
|
||||
.htpasswd
|
||||
.eslintcache
|
||||
|
@ -19,3 +20,4 @@ webpackstats.json
|
|||
config/kibana.dev.yml
|
||||
coverage
|
||||
selenium
|
||||
.babelcache.json
|
||||
|
|
1
.npmrc
Normal file
1
.npmrc
Normal file
|
@ -0,0 +1 @@
|
|||
save-exact=true
|
|
@ -102,6 +102,41 @@ The standard `npm run test` task runs several sub tasks and can take several min
|
|||
</dd>
|
||||
</dl>
|
||||
|
||||
### Functional UI Testing
|
||||
|
||||
#### Handy references
|
||||
|
||||
- https://theintern.github.io/
|
||||
- https://theintern.github.io/leadfoot/Element.html
|
||||
|
||||
#### Running tests using npm task:
|
||||
|
||||
*The Selenium server that is started currently only runs the tests in Firefox*
|
||||
|
||||
To runt the functional UI tests, execute the following command:
|
||||
|
||||
`npm run test:ui`
|
||||
|
||||
The task above takes a little time to start the servers. You can also start the servers and leave them running, and then run the tests separately:
|
||||
|
||||
`npm run test:ui:server` will start the server required to run the selenium tests, leave this open
|
||||
|
||||
`npm run test:ui:runner` will run the frontend tests and close when complete
|
||||
|
||||
#### Running tests locally with your existing (and already running) ElasticSearch, Kibana, and Selenium Server:
|
||||
|
||||
Set your es and kibana ports in `test/intern.js` to 9220 and 5620, respecitively. You can configure your Selenium server to run the tests on Chrome,IE, or other browsers here.
|
||||
|
||||
Once you've got the services running, execute the following:
|
||||
|
||||
`npm run test:ui:runner`
|
||||
|
||||
#### General notes:
|
||||
|
||||
- Using Page Objects pattern (https://theintern.github.io/intern/#writing-functional-test)
|
||||
- At least the initial tests for the Settings, Discover, and Visualize tabs all depend on a very specific set of logstash-type data (generated with makelogs). Since that is a static set of data, all the Discover and Visualize tests use a specific Absolute time range. This gaurantees the same results each run.
|
||||
- These tests have been developed and tested with Chrome and Firefox browser. In theory, they should work on all browsers (that's the benefit of Intern using Leadfoot).
|
||||
- These tests should also work with an external testing service like https://saucelabs.com/ or https://www.browserstack.com/ but that has not been tested.
|
||||
|
||||
### Submit a pull request
|
||||
|
||||
|
|
|
@ -43,8 +43,10 @@ module.exports = function (grunt) {
|
|||
lintThese: [
|
||||
'Gruntfile.js',
|
||||
'<%= root %>/tasks/**/*.js',
|
||||
'<%= root %>/test/**/*.js',
|
||||
'<%= src %>/**/*.js',
|
||||
'!<%= src %>/fixtures/**/*.js'
|
||||
'!<%= src %>/fixtures/**/*.js',
|
||||
'!<%= root %>/test/fixtures/scenarios/**/*.js'
|
||||
],
|
||||
deepModules: {
|
||||
'caniuse-db': '1.0.30000265',
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
# The host to bind the server to.
|
||||
# server.host: "0.0.0.0"
|
||||
|
||||
# A value to use as a XSRF token. This token is sent back to the server on each request
|
||||
# and required if you want to execute requests from other clients (like curl).
|
||||
# server.xsrf.token: ""
|
||||
|
||||
# If you are running kibana behind a proxy, and want to mount it at a path,
|
||||
# specify that path here. The basePath can't end in a slash.
|
||||
# server.basePath: ""
|
||||
|
@ -26,8 +30,8 @@
|
|||
# used by the Kibana server to perform maintenance on the kibana_index at startup. Your Kibana
|
||||
# users will still need to authenticate with Elasticsearch (which is proxied through
|
||||
# the Kibana server)
|
||||
# elasticsearch.username: user
|
||||
# elasticsearch.password: pass
|
||||
# elasticsearch.username: "user"
|
||||
# elasticsearch.password: "pass"
|
||||
|
||||
# SSL for outgoing requests from the Kibana Server to the browser (PEM formatted)
|
||||
# server.ssl.cert: /path/to/your/server.crt
|
||||
|
|
0
optimize/.empty
Normal file
0
optimize/.empty
Normal file
|
@ -172,7 +172,7 @@
|
|||
"npm": "2.11.0",
|
||||
"portscanner": "1.0.0",
|
||||
"simple-git": "1.8.0",
|
||||
"sinon": "1.16.1",
|
||||
"sinon": "1.17.2",
|
||||
"source-map": "0.4.4",
|
||||
"wreck": "6.2.0"
|
||||
},
|
||||
|
|
|
@ -1,2 +1,5 @@
|
|||
require('babel/register')(require('../optimize/babelOptions').node);
|
||||
// load the babel options seperately so that they can modify the process.env
|
||||
// before calling babel/register
|
||||
const babelOptions = require('../optimize/babelOptions').node;
|
||||
require('babel/register')(babelOptions);
|
||||
require('./cli');
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
var fromRoot = require('requirefrom')('src/utils')('fromRoot');
|
||||
var cloneDeep = require('lodash').cloneDeep;
|
||||
var fromRoot = require('path').resolve.bind(null, __dirname, '../../');
|
||||
|
||||
if (!process.env.BABEL_CACHE_PATH) {
|
||||
process.env.BABEL_CACHE_PATH = fromRoot('optimize/.babelcache.json');
|
||||
}
|
||||
|
||||
exports.webpack = {
|
||||
stage: 1,
|
||||
|
@ -6,7 +11,7 @@ exports.webpack = {
|
|||
optional: ['runtime']
|
||||
};
|
||||
|
||||
exports.node = Object.assign({
|
||||
exports.node = cloneDeep({
|
||||
ignore: [
|
||||
fromRoot('src'),
|
||||
/[\\\/](node_modules|bower_components)[\\\/]/
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
var cloneDeep = require('lodash').cloneDeep;
|
||||
var fromRoot = require('path').resolve.bind(null, __dirname, '../../');
|
||||
|
||||
if (!process.env.BABEL_CACHE_PATH) {
|
||||
process.env.BABEL_CACHE_PATH = fromRoot('optimize/.babelcache.json');
|
||||
}
|
||||
|
||||
exports.webpack = {
|
||||
stage: 1,
|
||||
nonStandard: false,
|
||||
optional: ['runtime']
|
||||
};
|
||||
|
||||
exports.node = Object.assign({}, exports.webpack);
|
||||
exports.node = cloneDeep(exports.webpack);
|
||||
|
|
|
@ -13,7 +13,12 @@ describe('plugins/elasticsearch', function () {
|
|||
|
||||
before(function () {
|
||||
kbnServer = new KbnServer({
|
||||
server: { autoListen: false },
|
||||
server: {
|
||||
autoListen: false,
|
||||
xsrf: {
|
||||
disableProtection: true
|
||||
}
|
||||
},
|
||||
logging: { quiet: true },
|
||||
plugins: {
|
||||
scanDirs: [
|
||||
|
@ -104,5 +109,3 @@ describe('plugins/elasticsearch', function () {
|
|||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@
|
|||
<h1>No results found <i aria-hidden="true" class="fa fa-meh-o"></i></h1>
|
||||
|
||||
<p>
|
||||
Unfortunately I could not find any results matching your search. I tried really hard. I looked all over the place and frankly, I just couldn't find anything good. Help me, help you. Here's some ideas:
|
||||
Unfortunately I could not find any results matching your search. I tried really hard. I looked all over the place and frankly, I just couldn't find anything good. Help me, help you. Here are some ideas:
|
||||
</p>
|
||||
|
||||
<div class="shard-failures" ng-show="failures">
|
||||
|
|
|
@ -5,8 +5,9 @@ let path = require('path');
|
|||
|
||||
let utils = require('requirefrom')('src/utils');
|
||||
let fromRoot = utils('fromRoot');
|
||||
const randomBytes = require('crypto').randomBytes;
|
||||
|
||||
module.exports = Joi.object({
|
||||
module.exports = () => Joi.object({
|
||||
pkg: Joi.object({
|
||||
version: Joi.string().default(Joi.ref('$version')),
|
||||
buildNum: Joi.number().default(Joi.ref('$buildNum')),
|
||||
|
@ -40,7 +41,11 @@ module.exports = Joi.object({
|
|||
origin: ['*://localhost:9876'] // karma test server
|
||||
}),
|
||||
otherwise: Joi.boolean().default(false)
|
||||
})
|
||||
}),
|
||||
xsrf: Joi.object({
|
||||
token: Joi.string().default(randomBytes(32).toString('hex')),
|
||||
disableProtection: Joi.boolean().default(false),
|
||||
}).default(),
|
||||
}).default(),
|
||||
|
||||
logging: Joi.object().keys({
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module.exports = function (kbnServer) {
|
||||
let Config = require('./Config');
|
||||
let schema = require('./schema');
|
||||
let schema = require('./schema')();
|
||||
|
||||
kbnServer.config = new Config(schema, kbnServer.settings || {});
|
||||
};
|
||||
|
|
145
src/server/http/__tests__/xsrf.js
Normal file
145
src/server/http/__tests__/xsrf.js
Normal file
|
@ -0,0 +1,145 @@
|
|||
import expect from 'expect.js';
|
||||
import { fromNode as fn } from 'bluebird';
|
||||
import { resolve } from 'path';
|
||||
|
||||
import KbnServer from '../../KbnServer';
|
||||
|
||||
const nonDestructiveMethods = ['GET'];
|
||||
const destructiveMethods = ['POST', 'PUT', 'DELETE'];
|
||||
const src = resolve.bind(null, __dirname, '../../../../src');
|
||||
|
||||
describe('xsrf request filter', function () {
|
||||
function inject(kbnServer, opts) {
|
||||
return fn(cb => {
|
||||
kbnServer.server.inject(opts, (resp) => {
|
||||
cb(null, resp);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const makeServer = async function (token) {
|
||||
const kbnServer = new KbnServer({
|
||||
server: { autoListen: false, xsrf: { token } },
|
||||
plugins: { scanDirs: [src('plugins')] },
|
||||
logging: { quiet: true },
|
||||
optimize: { enabled: false },
|
||||
});
|
||||
|
||||
await kbnServer.ready();
|
||||
|
||||
kbnServer.server.route({
|
||||
path: '/xsrf/test/route',
|
||||
method: [...nonDestructiveMethods, ...destructiveMethods],
|
||||
handler: function (req, reply) {
|
||||
reply(null, 'ok');
|
||||
}
|
||||
});
|
||||
|
||||
return kbnServer;
|
||||
};
|
||||
|
||||
describe('issuing tokens', function () {
|
||||
const token = 'secur3';
|
||||
let kbnServer;
|
||||
beforeEach(async () => kbnServer = await makeServer(token));
|
||||
afterEach(async () => await kbnServer.close());
|
||||
|
||||
it('sends a token when rendering an app', async function () {
|
||||
var resp = await inject(kbnServer, {
|
||||
method: 'GET',
|
||||
url: '/app/kibana',
|
||||
});
|
||||
|
||||
expect(resp.payload).to.contain(`"xsrfToken":"${token}"`);
|
||||
});
|
||||
});
|
||||
|
||||
context('without configured token', function () {
|
||||
let kbnServer;
|
||||
beforeEach(async () => kbnServer = await makeServer());
|
||||
afterEach(async () => await kbnServer.close());
|
||||
|
||||
it('responds with a random token', async function () {
|
||||
var resp = await inject(kbnServer, {
|
||||
method: 'GET',
|
||||
url: '/app/kibana',
|
||||
});
|
||||
|
||||
expect(resp.payload).to.match(/"xsrfToken":".{64}"/);
|
||||
});
|
||||
});
|
||||
|
||||
context('with configured token', function () {
|
||||
const token = 'mytoken';
|
||||
let kbnServer;
|
||||
beforeEach(async () => kbnServer = await makeServer(token));
|
||||
afterEach(async () => await kbnServer.close());
|
||||
|
||||
for (const method of nonDestructiveMethods) {
|
||||
context(`nonDestructiveMethod: ${method}`, function () { // eslint-disable-line no-loop-func
|
||||
it('accepts requests without a token', async function () {
|
||||
const resp = await inject(kbnServer, {
|
||||
url: '/xsrf/test/route',
|
||||
method: method
|
||||
});
|
||||
|
||||
expect(resp.statusCode).to.be(200);
|
||||
expect(resp.payload).to.be('ok');
|
||||
});
|
||||
|
||||
it('ignores invalid tokens', async function () {
|
||||
const resp = await inject(kbnServer, {
|
||||
url: '/xsrf/test/route',
|
||||
method: method,
|
||||
headers: {
|
||||
'kbn-xsrf-token': `invalid:${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
expect(resp.statusCode).to.be(200);
|
||||
expect(resp.headers).to.not.have.property('kbn-xsrf-token');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
for (const method of destructiveMethods) {
|
||||
context(`destructiveMethod: ${method}`, function () { // eslint-disable-line no-loop-func
|
||||
it('accepts requests with the correct token', async function () {
|
||||
const resp = await inject(kbnServer, {
|
||||
url: '/xsrf/test/route',
|
||||
method: method,
|
||||
headers: {
|
||||
'kbn-xsrf-token': token,
|
||||
},
|
||||
});
|
||||
|
||||
expect(resp.statusCode).to.be(200);
|
||||
expect(resp.payload).to.be('ok');
|
||||
});
|
||||
|
||||
it('rejects requests without a token', async function () {
|
||||
const resp = await inject(kbnServer, {
|
||||
url: '/xsrf/test/route',
|
||||
method: method
|
||||
});
|
||||
|
||||
expect(resp.statusCode).to.be(403);
|
||||
expect(resp.payload).to.match(/"Missing XSRF token"/);
|
||||
});
|
||||
|
||||
it('rejects requests with an invalid token', async function () {
|
||||
const resp = await inject(kbnServer, {
|
||||
url: '/xsrf/test/route',
|
||||
method: method,
|
||||
headers: {
|
||||
'kbn-xsrf-token': `invalid:${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
expect(resp.statusCode).to.be(403);
|
||||
expect(resp.payload).to.match(/"Invalid XSRF token"/);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
|
@ -121,4 +121,6 @@ module.exports = function (kbnServer, server, config) {
|
|||
.permanent(true);
|
||||
}
|
||||
});
|
||||
|
||||
return kbnServer.mixin(require('./xsrf'));
|
||||
};
|
||||
|
|
20
src/server/http/xsrf.js
Normal file
20
src/server/http/xsrf.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { forbidden } from 'boom';
|
||||
|
||||
export default function (kbnServer, server, config) {
|
||||
const token = config.get('server.xsrf.token');
|
||||
const disabled = config.get('server.xsrf.disableProtection');
|
||||
|
||||
server.decorate('reply', 'issueXsrfToken', function () {
|
||||
return token;
|
||||
});
|
||||
|
||||
server.ext('onPostAuth', function (req, reply) {
|
||||
if (disabled || req.method === 'get') return reply.continue();
|
||||
|
||||
const attempt = req.headers['kbn-xsrf-token'];
|
||||
if (!attempt) return reply(forbidden('Missing XSRF token'));
|
||||
if (attempt !== token) return reply(forbidden('Invalid XSRF token'));
|
||||
|
||||
return reply.continue();
|
||||
});
|
||||
}
|
|
@ -21,12 +21,13 @@ module.exports = class KbnLogger {
|
|||
}
|
||||
|
||||
init(readstream, emitter, callback) {
|
||||
readstream
|
||||
.pipe(this.squeeze)
|
||||
.pipe(this.format)
|
||||
.pipe(this.dest);
|
||||
|
||||
emitter.on('stop', _.noop);
|
||||
this.output = readstream.pipe(this.squeeze).pipe(this.format);
|
||||
this.output.pipe(this.dest);
|
||||
|
||||
emitter.on('stop', () => {
|
||||
this.output.unpipe(this.dest);
|
||||
});
|
||||
|
||||
callback();
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ module.exports = async function (kbnServer, server, config) {
|
|||
|
||||
|
||||
let path = [];
|
||||
async function initialize(id) {
|
||||
const initialize = async function (id) {
|
||||
let plugin = plugins.byId[id];
|
||||
|
||||
if (includes(path, id)) {
|
||||
|
|
|
@ -69,7 +69,7 @@ module.exports = async (kbnServer, server, config) => {
|
|||
}
|
||||
|
||||
server.decorate('reply', 'renderApp', function (app) {
|
||||
let payload = {
|
||||
const payload = {
|
||||
app: app,
|
||||
nav: uiExports.apps,
|
||||
version: kbnServer.version,
|
||||
|
@ -77,6 +77,7 @@ module.exports = async (kbnServer, server, config) => {
|
|||
buildSha: config.get('pkg.buildSha'),
|
||||
basePath: config.get('server.basePath'),
|
||||
vars: defaults(app.getInjectedVars(), defaultInjectedVars),
|
||||
xsrfToken: this.issueXsrfToken(),
|
||||
};
|
||||
|
||||
return this.view(app.templateName, {
|
||||
|
|
132
src/ui/public/chrome/api/__tests__/xsrf.js
Normal file
132
src/ui/public/chrome/api/__tests__/xsrf.js
Normal file
|
@ -0,0 +1,132 @@
|
|||
import $ from 'jquery';
|
||||
import expect from 'expect.js';
|
||||
import { stub } from 'auto-release-sinon';
|
||||
import ngMock from 'ngMock';
|
||||
|
||||
import xsrfChromeApi from '../xsrf';
|
||||
|
||||
const xsrfHeader = 'kbn-xsrf-token';
|
||||
const xsrfToken = 'xsrfToken';
|
||||
|
||||
describe('chrome xsrf apis', function () {
|
||||
describe('#getXsrfToken()', function () {
|
||||
it('exposes the token', function () {
|
||||
const chrome = {};
|
||||
xsrfChromeApi(chrome, { xsrfToken });
|
||||
expect(chrome.getXsrfToken()).to.be(xsrfToken);
|
||||
});
|
||||
});
|
||||
|
||||
context('jQuery support', function () {
|
||||
it('adds a global jQuery prefilter', function () {
|
||||
stub($, 'ajaxPrefilter');
|
||||
xsrfChromeApi({}, {});
|
||||
expect($.ajaxPrefilter.callCount).to.be(1);
|
||||
});
|
||||
|
||||
context('jQuery prefilter', function () {
|
||||
let prefilter;
|
||||
const xsrfToken = 'xsrfToken';
|
||||
|
||||
beforeEach(function () {
|
||||
stub($, 'ajaxPrefilter');
|
||||
xsrfChromeApi({}, { xsrfToken });
|
||||
prefilter = $.ajaxPrefilter.args[0][0];
|
||||
});
|
||||
|
||||
it('sets the kbn-xsrf-token header', function () {
|
||||
const setHeader = stub();
|
||||
prefilter({}, {}, { setRequestHeader: setHeader });
|
||||
|
||||
expect(setHeader.callCount).to.be(1);
|
||||
expect(setHeader.args[0]).to.eql([
|
||||
xsrfHeader,
|
||||
xsrfToken
|
||||
]);
|
||||
});
|
||||
|
||||
it('can be canceled by setting the kbnXsrfToken option', function () {
|
||||
const setHeader = stub();
|
||||
prefilter({ kbnXsrfToken: false }, {}, { setRequestHeader: setHeader });
|
||||
expect(setHeader.callCount).to.be(0);
|
||||
});
|
||||
});
|
||||
|
||||
context('Angular support', function () {
|
||||
|
||||
let $http;
|
||||
let $httpBackend;
|
||||
|
||||
beforeEach(function () {
|
||||
stub($, 'ajaxPrefilter');
|
||||
const chrome = {};
|
||||
xsrfChromeApi(chrome, { xsrfToken });
|
||||
ngMock.module(chrome.$setupXsrfRequestInterceptor);
|
||||
});
|
||||
|
||||
beforeEach(ngMock.inject(function ($injector) {
|
||||
$http = $injector.get('$http');
|
||||
$httpBackend = $injector.get('$httpBackend');
|
||||
|
||||
$httpBackend
|
||||
.when('POST', '/api/test')
|
||||
.respond('ok');
|
||||
}));
|
||||
|
||||
afterEach(function () {
|
||||
$httpBackend.verifyNoOutstandingExpectation();
|
||||
$httpBackend.verifyNoOutstandingRequest();
|
||||
});
|
||||
|
||||
it('injects a kbn-xsrf-token header on every request', function () {
|
||||
$httpBackend.expectPOST('/api/test', undefined, function (headers) {
|
||||
return headers[xsrfHeader] === xsrfToken;
|
||||
}).respond(200, '');
|
||||
|
||||
$http.post('/api/test');
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
it('skips requests with the kbnXsrfToken set falsey', function () {
|
||||
$httpBackend.expectPOST('/api/test', undefined, function (headers) {
|
||||
return !(xsrfHeader in headers);
|
||||
}).respond(200, '');
|
||||
|
||||
$http({
|
||||
method: 'POST',
|
||||
url: '/api/test',
|
||||
kbnXsrfToken: 0
|
||||
});
|
||||
|
||||
$http({
|
||||
method: 'POST',
|
||||
url: '/api/test',
|
||||
kbnXsrfToken: ''
|
||||
});
|
||||
|
||||
$http({
|
||||
method: 'POST',
|
||||
url: '/api/test',
|
||||
kbnXsrfToken: false
|
||||
});
|
||||
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
it('accepts alternate tokens to use', function () {
|
||||
const customToken = `custom:${xsrfToken}`;
|
||||
$httpBackend.expectPOST('/api/test', undefined, function (headers) {
|
||||
return headers[xsrfHeader] === customToken;
|
||||
}).respond(200, '');
|
||||
|
||||
$http({
|
||||
method: 'POST',
|
||||
url: '/api/test',
|
||||
kbnXsrfToken: customToken
|
||||
});
|
||||
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
3
src/ui/public/chrome/api/angular.js
vendored
3
src/ui/public/chrome/api/angular.js
vendored
|
@ -24,6 +24,7 @@ module.exports = function (chrome, internals) {
|
|||
a.href = chrome.addBasePath('/elasticsearch');
|
||||
return a.href;
|
||||
}()))
|
||||
.config(chrome.$setupXsrfRequestInterceptor)
|
||||
.directive('kbnChrome', function ($rootScope) {
|
||||
return {
|
||||
template: function ($el) {
|
||||
|
@ -45,7 +46,7 @@ module.exports = function (chrome, internals) {
|
|||
controller: function ($scope, $rootScope, $location, $http) {
|
||||
|
||||
// are we showing the embedded version of the chrome?
|
||||
chrome.setVisible(!Boolean($location.search().embed));
|
||||
internals.setVisibleDefault(!$location.search().embed);
|
||||
|
||||
// listen for route changes, propogate to tabs
|
||||
var onRouteChange = function () {
|
||||
|
|
5
src/ui/public/chrome/api/controls.js
vendored
5
src/ui/public/chrome/api/controls.js
vendored
|
@ -10,6 +10,9 @@ module.exports = function (chrome, internals) {
|
|||
* determines if the Kibana chrome should be displayed
|
||||
*/
|
||||
|
||||
var def = true;
|
||||
internals.setVisibleDefault = (_def) => def = Boolean(_def);
|
||||
|
||||
/**
|
||||
* @param {boolean} display - should the chrome be displayed
|
||||
* @return {chrome}
|
||||
|
@ -23,7 +26,7 @@ module.exports = function (chrome, internals) {
|
|||
* @return {boolean} - display state of the chrome
|
||||
*/
|
||||
chrome.getVisible = function () {
|
||||
if (_.isUndefined(internals.visible)) return true;
|
||||
if (_.isUndefined(internals.visible)) return def;
|
||||
return internals.visible;
|
||||
};
|
||||
};
|
||||
|
|
29
src/ui/public/chrome/api/xsrf.js
Normal file
29
src/ui/public/chrome/api/xsrf.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
import $ from 'jquery';
|
||||
import { set } from 'lodash';
|
||||
|
||||
export default function (chrome, internals) {
|
||||
|
||||
chrome.getXsrfToken = function () {
|
||||
return internals.xsrfToken;
|
||||
};
|
||||
|
||||
$.ajaxPrefilter(function ({ kbnXsrfToken = internals.xsrfToken }, originalOptions, jqXHR) {
|
||||
if (kbnXsrfToken) {
|
||||
jqXHR.setRequestHeader('kbn-xsrf-token', kbnXsrfToken);
|
||||
}
|
||||
});
|
||||
|
||||
chrome.$setupXsrfRequestInterceptor = function ($httpProvider) {
|
||||
$httpProvider.interceptors.push(function () {
|
||||
return {
|
||||
request: function (opts) {
|
||||
const { kbnXsrfToken = internals.xsrfToken } = opts;
|
||||
if (kbnXsrfToken) {
|
||||
set(opts, ['headers', 'kbn-xsrf-token'], kbnXsrfToken);
|
||||
}
|
||||
return opts;
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
<div class="content" chrome-context >
|
||||
<nav
|
||||
ng-style="::{ background: chrome.getNavBackground() }"
|
||||
ng-class="::{ show: chrome.getVisible() }"
|
||||
ng-class="{ show: chrome.getVisible() }"
|
||||
class="hide navbar navbar-inverse navbar-static-top">
|
||||
|
||||
<!-- Mobile navbar -->
|
||||
|
|
|
@ -18,6 +18,7 @@ var internals = _.defaults(
|
|||
rootController: null,
|
||||
rootTemplate: null,
|
||||
showAppsLink: null,
|
||||
xsrfToken: null,
|
||||
brand: null,
|
||||
nav: [],
|
||||
applicationClasses: []
|
||||
|
@ -30,6 +31,7 @@ $('<link>').attr({
|
|||
}).appendTo('head');
|
||||
|
||||
require('./api/apps')(chrome, internals);
|
||||
require('./api/xsrf')(chrome, internals);
|
||||
require('./api/nav')(chrome, internals);
|
||||
require('./api/angular')(chrome, internals);
|
||||
require('./api/controls')(chrome, internals);
|
||||
|
|
|
@ -20,7 +20,7 @@ describe('ui/courier/fetch/strategy/search', () => {
|
|||
index: ['logstash-123'],
|
||||
type: 'blah',
|
||||
search_type: 'blah2',
|
||||
body: 'hm this is the body'
|
||||
body: { foo: 'bar', $foo: 'bar' }
|
||||
}
|
||||
];
|
||||
}));
|
||||
|
@ -32,6 +32,14 @@ describe('ui/courier/fetch/strategy/search', () => {
|
|||
});
|
||||
|
||||
describe('#reqsFetchParamsToBody()', () => {
|
||||
it('filters out any body properties that begin with $', () => {
|
||||
let value;
|
||||
search.reqsFetchParamsToBody(reqsFetchParams).then(val => value = val);
|
||||
$rootScope.$apply();
|
||||
expect(_.includes(value, 'foo')).to.be(true);
|
||||
expect(_.includes(value, '$foo')).to.be(false);
|
||||
});
|
||||
|
||||
context('when indexList is not empty', () => {
|
||||
it('includes the index', () => {
|
||||
let value;
|
||||
|
|
|
@ -2,6 +2,7 @@ define(function (require) {
|
|||
return function FetchStrategyForSearch(Private, Promise, timefilter) {
|
||||
var _ = require('lodash');
|
||||
var angular = require('angular');
|
||||
var toJson = require('ui/utils/aggressive_parse').toJson;
|
||||
|
||||
return {
|
||||
clientMethod: 'msearch',
|
||||
|
@ -42,7 +43,7 @@ define(function (require) {
|
|||
ignore_unavailable: true
|
||||
})
|
||||
+ '\n'
|
||||
+ angular.toJson(fetchParams.body || {});
|
||||
+ toJson(fetchParams.body || {}, angular.toJson);
|
||||
});
|
||||
})
|
||||
.then(function (requests) {
|
||||
|
|
|
@ -3,6 +3,7 @@ describe('ui/index_patterns/_calculate_indices', () => {
|
|||
const sinon = require('auto-release-sinon');
|
||||
const expect = require('expect.js');
|
||||
const ngMock = require('ngMock');
|
||||
const moment = require('moment');
|
||||
|
||||
let Promise;
|
||||
let $rootScope;
|
||||
|
@ -38,7 +39,7 @@ describe('ui/index_patterns/_calculate_indices', () => {
|
|||
function run({ start = undefined, stop = undefined } = {}) {
|
||||
calculateIndices('wat-*-no', '@something', start, stop);
|
||||
$rootScope.$apply();
|
||||
config = _.first(es.fieldStats.firstCall.args);
|
||||
config = _.first(es.fieldStats.lastCall.args);
|
||||
constraints = config.body.index_constraints;
|
||||
}
|
||||
|
||||
|
@ -68,6 +69,14 @@ describe('ui/index_patterns/_calculate_indices', () => {
|
|||
it('max_value is gte', () => {
|
||||
expect(constraints['@something'].max_value).to.have.property('gte');
|
||||
});
|
||||
it('max_value is set to original if not a moment object', () => {
|
||||
expect(constraints['@something'].max_value.gte).to.equal('1234567890');
|
||||
});
|
||||
it('max_value is set to moment.valueOf if given a moment object', () => {
|
||||
const start = moment();
|
||||
run({ start });
|
||||
expect(constraints['@something'].max_value.gte).to.equal(start.valueOf());
|
||||
});
|
||||
});
|
||||
|
||||
context('when given stop', () => {
|
||||
|
@ -78,6 +87,14 @@ describe('ui/index_patterns/_calculate_indices', () => {
|
|||
it('min_value is lte', () => {
|
||||
expect(constraints['@something'].min_value).to.have.property('lte');
|
||||
});
|
||||
it('min_value is set to original if not a moment object', () => {
|
||||
expect(constraints['@something'].min_value.lte).to.equal('1234567890');
|
||||
});
|
||||
it('max_value is set to moment.valueOf if given a moment object', () => {
|
||||
const stop = moment();
|
||||
run({ stop });
|
||||
expect(constraints['@something'].min_value.lte).to.equal(stop.valueOf());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -2,6 +2,11 @@ define(function (require) {
|
|||
const _ = require('lodash');
|
||||
const moment = require('moment');
|
||||
|
||||
// gets parsed value if given arg is a moment object
|
||||
function timeValue(val) {
|
||||
return moment.isMoment(val) ? val.valueOf() : val;
|
||||
}
|
||||
|
||||
return function CalculateIndicesFactory(Promise, es) {
|
||||
|
||||
// Uses the field stats api to determine the names of indices that need to
|
||||
|
@ -17,10 +22,10 @@ define(function (require) {
|
|||
function getFieldStats(pattern, timeFieldName, start, stop) {
|
||||
const constraints = {};
|
||||
if (start) {
|
||||
constraints.max_value = { gte: moment(start).valueOf() };
|
||||
constraints.max_value = { gte: timeValue(start) };
|
||||
}
|
||||
if (stop) {
|
||||
constraints.min_value = { lte: moment(stop).valueOf() };
|
||||
constraints.min_value = { lte: timeValue(stop) };
|
||||
}
|
||||
|
||||
return es.fieldStats({
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
/* global mocha */
|
||||
|
||||
// chrome expects to be loaded first, let it get its way
|
||||
var chrome = require('ui/chrome');
|
||||
|
||||
var Nonsense = require('Nonsense');
|
||||
var sinon = require('sinon');
|
||||
var $ = require('jquery');
|
||||
|
@ -6,8 +10,6 @@ var _ = require('lodash');
|
|||
var parse = require('url').parse;
|
||||
|
||||
var StackTraceMapper = require('ui/StackTraceMapper');
|
||||
var chrome = require('ui/chrome');
|
||||
|
||||
|
||||
/*** the vislib tests have certain style requirements, so lets make sure they are met ***/
|
||||
$('body').attr('id', 'test-harness-body'); // so we can make high priority selectors
|
||||
|
|
81
src/ui/public/utils/__tests__/aggressive_parse.js
Normal file
81
src/ui/public/utils/__tests__/aggressive_parse.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
describe('aggressiveParse', () => {
|
||||
const _ = require('lodash');
|
||||
const expect = require('expect.js');
|
||||
const sinon = require('sinon');
|
||||
const aggressiveParse = require('ui/utils/aggressive_parse');
|
||||
|
||||
let object;
|
||||
let jsonFn;
|
||||
let result;
|
||||
|
||||
beforeEach(() => {
|
||||
object = Object.freeze({
|
||||
foo: 'bar',
|
||||
nums: { two: 2, $three: 3 },
|
||||
another: { level: { $deep: 'inception' } },
|
||||
$no: 'wai'
|
||||
});
|
||||
jsonFn = sinon.stub().returns('{"foo":"bar","$foo":"bar"}');
|
||||
});
|
||||
|
||||
describe('#toJson()', () => {
|
||||
it('returns serialized version of object', () => {
|
||||
result = aggressiveParse.toJson(object);
|
||||
result = JSON.parse(result);
|
||||
|
||||
expect(_.get(result, 'foo')).to.equal(object.foo);
|
||||
expect(_.get(result, 'nums.two')).to.equal(object.nums.two);
|
||||
expect(_.has(result, 'another.level')).to.be(true);
|
||||
});
|
||||
|
||||
it('does not include any properties that begin with $', () => {
|
||||
result = aggressiveParse.toJson(object);
|
||||
result = JSON.parse(result);
|
||||
|
||||
expect(_.has(result, '$no')).to.be(false);
|
||||
expect(_.has(result, 'nums.$three')).to.be(false);
|
||||
expect(_.has(result, 'another.level.$deep')).to.be(false);
|
||||
});
|
||||
|
||||
context('with arity of 2', () => {
|
||||
beforeEach(() => {
|
||||
result = aggressiveParse.toJson(object, jsonFn);
|
||||
result = JSON.parse(result);
|
||||
});
|
||||
|
||||
it('sends first argument to custom json function', () => {
|
||||
expect(jsonFn.calledWith(object)).to.be(true);
|
||||
});
|
||||
|
||||
it('serializes the json returned by jsonFn', () => {
|
||||
expect(_.get(result, 'foo')).to.equal('bar');
|
||||
});
|
||||
|
||||
it('still does not include any properties that begin with $', () => {
|
||||
expect(result).not.to.have.property('$foo');
|
||||
});
|
||||
});
|
||||
|
||||
context('with arity of 3', () => {
|
||||
beforeEach(() => {
|
||||
result = aggressiveParse.toJson({foo: 'bar'}, undefined, 2);
|
||||
});
|
||||
|
||||
it('formats the json string with the number of spaces given', () => {
|
||||
const formattedJson = JSON.stringify({foo: 'bar'}, null, 2);
|
||||
expect(result).to.be(formattedJson);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#replacer()', () => {
|
||||
it('returns undefined if key begins with $', () => {
|
||||
result = aggressiveParse.replacer('$foo', 'bar');
|
||||
expect(result).to.be(undefined);
|
||||
});
|
||||
it('returns value if key does not being with $', () => {
|
||||
result = aggressiveParse.replacer('foo', 'bar');
|
||||
expect(result).to.equal('bar');
|
||||
});
|
||||
});
|
||||
});
|
27
src/ui/public/utils/aggressive_parse.js
Normal file
27
src/ui/public/utils/aggressive_parse.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { isString, startsWith } from 'lodash';
|
||||
|
||||
/**
|
||||
* Serializes the given object into a JSON string
|
||||
*
|
||||
* All properties that begin with $ throughout the entire object are omitted.
|
||||
* If a custom JSON serializer function is passed, then the given object is
|
||||
* passed through it before being re-stringified with the native stringify.
|
||||
*
|
||||
* The space argument is passed unaltered to the native stringify.
|
||||
*/
|
||||
export function toJson(object, jsonFn, space) {
|
||||
if (jsonFn) {
|
||||
// We reparse the stringified json so that we can lean on JSON.stringify's
|
||||
// avoiding-infinite-recursion capabilities when stripping out any
|
||||
// remaining properties that begin with a dollar sign ($)
|
||||
object = JSON.parse(jsonFn(object));
|
||||
}
|
||||
return JSON.stringify(object, replacer, space);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the given value if the key does not begin with a dollar sign ($)
|
||||
*/
|
||||
export function replacer(key, value) {
|
||||
return isString(key) && startsWith(key, '$') ? undefined : value;
|
||||
}
|
|
@ -668,16 +668,21 @@ define(function (require) {
|
|||
* @return {undefined}
|
||||
*/
|
||||
Data.prototype._normalizeOrdered = function () {
|
||||
if (!this.data.ordered || !this.data.ordered.date) return;
|
||||
var data = this.getVisData();
|
||||
var self = this;
|
||||
|
||||
var missingMin = this.data.ordered.min == null;
|
||||
var missingMax = this.data.ordered.max == null;
|
||||
data.forEach(function (d) {
|
||||
if (!d.ordered || !d.ordered.date) return;
|
||||
|
||||
if (missingMax || missingMin) {
|
||||
var extent = d3.extent(this.xValues());
|
||||
if (missingMin) this.data.ordered.min = extent[0];
|
||||
if (missingMax) this.data.ordered.max = extent[1];
|
||||
}
|
||||
var missingMin = d.ordered.min == null;
|
||||
var missingMax = d.ordered.max == null;
|
||||
|
||||
if (missingMax || missingMin) {
|
||||
var extent = d3.extent(self.xValues());
|
||||
if (missingMin) d.ordered.min = extent[0];
|
||||
if (missingMax) d.ordered.max = extent[1];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,6 +8,7 @@ module.exports = function (grunt) {
|
|||
grunt.registerTask('_build:babelOptions', function () {
|
||||
unlink(srcFile);
|
||||
rename(buildFile, srcFile);
|
||||
grunt.file.mkdir('build/kibana/optimize');
|
||||
});
|
||||
|
||||
};
|
||||
|
|
|
@ -18,6 +18,9 @@ module.exports = function (grunt) {
|
|||
sha: grunt.config.get('buildSha')
|
||||
},
|
||||
repository: pkg.repository,
|
||||
engines: {
|
||||
node: pkg.engines.node
|
||||
},
|
||||
dependencies: defaults({}, pkg.dependencies, deepModules)
|
||||
}, null, ' ')
|
||||
);
|
||||
|
|
|
@ -44,7 +44,7 @@ module.exports = function (grunt) {
|
|||
purge: true,
|
||||
config: {
|
||||
http: {
|
||||
port: uiConfig.elasticsearch.port
|
||||
port: uiConfig.servers.elasticsearch.port
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,8 +33,9 @@ module.exports = function (grunt) {
|
|||
},
|
||||
cmd: /^win/.test(platform) ? '.\\bin\\kibana.bat' : './bin/kibana',
|
||||
args: [
|
||||
'--server.port=' + uiConfig.kibana.port,
|
||||
'--elasticsearch.url=' + format(uiConfig.elasticsearch),
|
||||
'--server.port=' + uiConfig.servers.kibana.port,
|
||||
'--env.name=development',
|
||||
'--elasticsearch.url=' + format(uiConfig.servers.elasticsearch),
|
||||
'--logging.json=false'
|
||||
]
|
||||
},
|
||||
|
@ -89,7 +90,7 @@ module.exports = function (grunt) {
|
|||
'-jar',
|
||||
'selenium/selenium-server-standalone-2.47.1.jar',
|
||||
'-port',
|
||||
uiConfig.webdriver.port
|
||||
uiConfig.servers.webdriver.port
|
||||
]
|
||||
},
|
||||
|
||||
|
@ -105,7 +106,7 @@ module.exports = function (grunt) {
|
|||
'-jar',
|
||||
'selenium/selenium-server-standalone-2.47.1.jar',
|
||||
'-port',
|
||||
uiConfig.webdriver.port
|
||||
uiConfig.servers.webdriver.port
|
||||
]
|
||||
},
|
||||
|
||||
|
|
92
test/fixtures/__tests__/scenarioManager.js
vendored
92
test/fixtures/__tests__/scenarioManager.js
vendored
|
@ -21,11 +21,11 @@ describe('scenario manager', function () {
|
|||
|
||||
it('should be able to load scenarios', function () {
|
||||
return manager.load('makelogs')
|
||||
.then(function () {
|
||||
expect(create.getCall(0).args[0].index).to.be('logstash-2015.09.17');
|
||||
expect(create.getCall(1).args[0].index).to.be('logstash-2015.09.18');
|
||||
expect(bulk.called).to.be(true);
|
||||
});
|
||||
.then(function () {
|
||||
expect(create.getCall(0).args[0].index).to.be('logstash-2015.09.17');
|
||||
expect(create.getCall(1).args[0].index).to.be('logstash-2015.09.18');
|
||||
expect(bulk.called).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to delete all indices', function () {
|
||||
|
@ -55,6 +55,52 @@ describe('scenario manager', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('should load if the index does not exist', function () {
|
||||
var load = sinon.stub(manager, 'load', Promise.resolve);
|
||||
var throwError = sinon.stub(manager.client, 'count', Promise.reject);
|
||||
var id = 'makelogs';
|
||||
return manager.loadIfEmpty(id).then(function () {
|
||||
expect(load.calledWith(id)).to.be(true);
|
||||
|
||||
load.restore();
|
||||
throwError.restore();
|
||||
});
|
||||
});
|
||||
|
||||
it('should load if the index is empty', function () {
|
||||
var load = sinon.stub(manager, 'load', Promise.resolve);
|
||||
var returnZero = sinon.stub(manager.client, 'count', function () {
|
||||
return Promise.resolve({
|
||||
'count': 0
|
||||
});
|
||||
});
|
||||
var id = 'makelogs';
|
||||
return manager.loadIfEmpty(id).then(function () {
|
||||
expect(load.calledWith(id)).to.be(true);
|
||||
|
||||
load.restore();
|
||||
returnZero.restore();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should not load if the index is not empty', function () {
|
||||
var load = sinon.stub(manager, 'load', Promise.resolve);
|
||||
var returnOne = sinon.stub(manager.client, 'count', function () {
|
||||
return Promise.resolve({
|
||||
'count': 1
|
||||
});
|
||||
});
|
||||
var id = 'makelogs';
|
||||
return manager.loadIfEmpty(id).then(function () {
|
||||
expect(load.called).to.be(false);
|
||||
|
||||
load.restore();
|
||||
returnOne.restore();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
afterEach(function () {
|
||||
bulk.restore();
|
||||
create.restore();
|
||||
|
@ -62,12 +108,40 @@ describe('scenario manager', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('should throw an error if the scenario is not defined', function () {
|
||||
expect(manager.load).withArgs('makelogs').to.throwError();
|
||||
describe('load', function () {
|
||||
it('should reject if the scenario is not specified', function () {
|
||||
return manager.load()
|
||||
.then(function () {
|
||||
throw new Error('Promise should reject');
|
||||
})
|
||||
.catch(function () { return; });
|
||||
});
|
||||
|
||||
it('should reject if the scenario is not defined', function () {
|
||||
return manager.load('idonotexist')
|
||||
.then(function () {
|
||||
throw new Error('Promise should reject');
|
||||
})
|
||||
.catch(function () { return; });
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error if an index is not defined when clearing', function () {
|
||||
expect(manager.unload).to.throwError();
|
||||
describe('unload', function () {
|
||||
it('should reject if the scenario is not specified', function () {
|
||||
return manager.unload()
|
||||
.then(function () {
|
||||
throw new Error('Promise should reject');
|
||||
})
|
||||
.catch(function () { return; });
|
||||
});
|
||||
|
||||
it('should reject if the scenario is not defined', function () {
|
||||
return manager.unload('idonotexist')
|
||||
.then(function () {
|
||||
throw new Error('Promise should reject');
|
||||
})
|
||||
.catch(function () { return; });
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error if an es server is not specified', function () {
|
||||
|
|
40
test/fixtures/config.js
vendored
40
test/fixtures/config.js
vendored
|
@ -2,23 +2,27 @@ var path = require('path');
|
|||
var rootDir = path.join(__dirname, 'scenarios');
|
||||
|
||||
module.exports = {
|
||||
makelogs: {
|
||||
baseDir: path.join(rootDir, 'makelogs'),
|
||||
bulk: [{
|
||||
indexDefinition: 'makelogsIndexDefinition.js',
|
||||
indexName: 'logstash-2015.09.17',
|
||||
source: 'logstash-2015.09.17.js'
|
||||
}, {
|
||||
indexDefinition: 'makelogsIndexDefinition.js',
|
||||
indexName: 'logstash-2015.09.18',
|
||||
source: 'logstash-2015.09.18.js'
|
||||
}]
|
||||
},
|
||||
emptyKibana: {
|
||||
baseDir: path.join(rootDir, 'emptyKibana'),
|
||||
bulk: [{
|
||||
indexName: '.kibana',
|
||||
source: 'kibana.js'
|
||||
}]
|
||||
scenarios: {
|
||||
makelogs: {
|
||||
baseDir: path.join(rootDir, 'makelogs'),
|
||||
bulk: [{
|
||||
indexName: 'logstash-2015.09.17',
|
||||
indexDefinition: 'makelogsIndexDefinition.js',
|
||||
source: 'logstash-2015.09.17.js'
|
||||
}, {
|
||||
indexName: 'logstash-2015.09.18',
|
||||
indexDefinition: 'makelogsIndexDefinition.js',
|
||||
source: 'logstash-2015.09.18.js'
|
||||
}]
|
||||
},
|
||||
emptyKibana: {
|
||||
baseDir: path.join(rootDir, 'emptyKibana'),
|
||||
bulk: [{
|
||||
indexName: '.kibana',
|
||||
indexDefinition: 'kibanaDefinition.js',
|
||||
source: 'kibana.js',
|
||||
haltOnFailure: false
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
61
test/fixtures/scenarioManager.js
vendored
61
test/fixtures/scenarioManager.js
vendored
|
@ -1,6 +1,7 @@
|
|||
var path = require('path');
|
||||
var config = require('./config');
|
||||
var elasticsearch = require('elasticsearch');
|
||||
var Promise = require('bluebird');
|
||||
var config = require('./config').scenarios;
|
||||
|
||||
function ScenarioManager(server) {
|
||||
if (!server) throw new Error('No server defined');
|
||||
|
@ -16,28 +17,33 @@ function ScenarioManager(server) {
|
|||
* @return {Promise} A promise that is resolved when elasticsearch has a response
|
||||
*/
|
||||
ScenarioManager.prototype.load = function (id) {
|
||||
var scenario = config[id];
|
||||
if (!scenario) throw new Error('No scenario found for ' + id);
|
||||
|
||||
var self = this;
|
||||
var scenario = config[id];
|
||||
if (!scenario) return Promise.reject('No scenario found for ' + id);
|
||||
|
||||
return Promise.all(scenario.bulk.map(function mapBulk(bulk) {
|
||||
var loadIndexDefinition;
|
||||
|
||||
if (bulk.indexDefinition) {
|
||||
var body = require(path.join(scenario.baseDir, bulk.indexDefinition));
|
||||
loadIndexDefinition = self.client.indices.create({
|
||||
index: bulk.indexName,
|
||||
body: require(path.join(scenario.baseDir, bulk.indexDefinition))
|
||||
body: body
|
||||
});
|
||||
} else {
|
||||
loadIndexDefinition = Promise.resolve();
|
||||
}
|
||||
|
||||
return loadIndexDefinition.then(function bulkRequest() {
|
||||
self.client.bulk({
|
||||
body: require(path.join(scenario.baseDir, bulk.source)),
|
||||
return loadIndexDefinition
|
||||
.then(function bulkRequest() {
|
||||
var body = require(path.join(scenario.baseDir, bulk.source));
|
||||
return self.client.bulk({
|
||||
body: body
|
||||
});
|
||||
})
|
||||
.catch(function (err) {
|
||||
if (bulk.haltOnFailure === false) return;
|
||||
throw err;
|
||||
});
|
||||
|
||||
}));
|
||||
};
|
||||
|
||||
|
@ -48,7 +54,7 @@ ScenarioManager.prototype.load = function (id) {
|
|||
*/
|
||||
ScenarioManager.prototype.unload = function (id) {
|
||||
var scenario = config[id];
|
||||
if (!scenario) throw new Error('Expected index');
|
||||
if (!scenario) return Promise.reject('No scenario found for ' + id);
|
||||
|
||||
var indices = scenario.bulk.map(function mapBulk(bulk) {
|
||||
return bulk.indexName;
|
||||
|
@ -67,7 +73,8 @@ ScenarioManager.prototype.unload = function (id) {
|
|||
ScenarioManager.prototype.reload = function (id) {
|
||||
var self = this;
|
||||
|
||||
return this.unload(id).then(function load() {
|
||||
return self.unload(id)
|
||||
.then(function load() {
|
||||
return self.load(id);
|
||||
});
|
||||
};
|
||||
|
@ -82,4 +89,32 @@ ScenarioManager.prototype.deleteAll = function () {
|
|||
});
|
||||
};
|
||||
|
||||
module.exports = ScenarioManager;
|
||||
/**
|
||||
* Load a testing scenario if not already loaded
|
||||
* @param {string} id The scenario id to load
|
||||
* @return {Promise} A promise that is resolved when elasticsearch has a response
|
||||
*/
|
||||
ScenarioManager.prototype.loadIfEmpty = function (id) {
|
||||
var self = this;
|
||||
var scenario = config[id];
|
||||
if (!scenario) throw new Error('No scenario found for ' + id);
|
||||
|
||||
var self = this;
|
||||
return Promise.all(scenario.bulk.map(function mapBulk(bulk) {
|
||||
var loadIndexDefinition;
|
||||
|
||||
return self.client.count({
|
||||
index: bulk.indexName
|
||||
})
|
||||
.then(function handleCountResponse(response) {
|
||||
if (response.count === 0) {
|
||||
return self.load(id);
|
||||
}
|
||||
});
|
||||
}))
|
||||
.catch(function (reason) {
|
||||
return self.load(id);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = ScenarioManager;
|
16
test/fixtures/scenarios/emptyKibana/kibanaDefinition.js
vendored
Normal file
16
test/fixtures/scenarios/emptyKibana/kibanaDefinition.js
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
module.exports = {
|
||||
settings: {
|
||||
number_of_shards: 1,
|
||||
number_of_replicas: 1
|
||||
},
|
||||
mappings: {
|
||||
config: {
|
||||
properties: {
|
||||
buildNum: {
|
||||
type: 'string',
|
||||
index: 'not_analyzed'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1,4 +1,3 @@
|
|||
/*eslint-disable*/
|
||||
module.exports = [{
|
||||
'index': {
|
||||
'_index': 'logstash-2015.09.17',
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
/*eslint-disable*/
|
||||
module.exports = [{
|
||||
'index': {
|
||||
'_index': 'logstash-2015.09.18',
|
||||
|
|
60
test/functional/apps/settings/_creation_form_changes.js
Normal file
60
test/functional/apps/settings/_creation_form_changes.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
define(function (require) {
|
||||
var Common = require('../../../support/pages/Common');
|
||||
var SettingsPage = require('../../../support/pages/SettingsPage');
|
||||
var expect = require('intern/dojo/node!expect.js');
|
||||
|
||||
return function (bdd, scenarioManager) {
|
||||
bdd.describe('user input reactions', function () {
|
||||
var common;
|
||||
var settingsPage;
|
||||
|
||||
bdd.before(function () {
|
||||
common = new Common(this.remote);
|
||||
settingsPage = new SettingsPage(this.remote);
|
||||
});
|
||||
|
||||
bdd.beforeEach(function () {
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
.then(function () {
|
||||
return settingsPage.navigateTo();
|
||||
});
|
||||
});
|
||||
|
||||
bdd.it('should hide time-based index pattern when time-based option is unchecked', function () {
|
||||
var self = this;
|
||||
return settingsPage.getTimeBasedEventsCheckbox()
|
||||
.then(function (selected) {
|
||||
// uncheck the 'time-based events' checkbox
|
||||
return selected.click();
|
||||
})
|
||||
// try to find the checkbox (this shouldn fail)
|
||||
.then(function () {
|
||||
var waitTime = 10000;
|
||||
return settingsPage.getTimeBasedIndexPatternCheckbox(waitTime);
|
||||
})
|
||||
.then(function () {
|
||||
// we expect the promise above to fail
|
||||
var handler = common.handleError(self);
|
||||
var msg = 'Found time based index pattern checkbox';
|
||||
handler(msg);
|
||||
})
|
||||
.catch(function () {
|
||||
// we expect this failure since checkbox should be hidden
|
||||
return;
|
||||
});
|
||||
});
|
||||
|
||||
bdd.it('should enable creation after selecting time field', function () {
|
||||
// select a time field and check that Create button is enabled
|
||||
return settingsPage.selectTimeFieldOption('@timestamp')
|
||||
.then(function () {
|
||||
return settingsPage.getCreateButton().isEnabled()
|
||||
.then(function (enabled) {
|
||||
expect(enabled).to.be.ok();
|
||||
});
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
104
test/functional/apps/settings/_index_pattern_create_delete.js
Normal file
104
test/functional/apps/settings/_index_pattern_create_delete.js
Normal file
|
@ -0,0 +1,104 @@
|
|||
define(function (require) {
|
||||
var Common = require('../../../support/pages/Common');
|
||||
var SettingsPage = require('../../../support/pages/SettingsPage');
|
||||
var expect = require('intern/dojo/node!expect.js');
|
||||
var Promise = require('bluebird');
|
||||
|
||||
return function (bdd, scenarioManager) {
|
||||
bdd.describe('creating and deleting default index', function describeIndexTests() {
|
||||
var common;
|
||||
var settingsPage;
|
||||
var remote;
|
||||
|
||||
bdd.before(function () {
|
||||
common = new Common(this.remote);
|
||||
settingsPage = new SettingsPage(this.remote);
|
||||
remote = this.remote;
|
||||
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
.then(function () {
|
||||
return settingsPage.navigateTo();
|
||||
});
|
||||
});
|
||||
|
||||
bdd.describe('index pattern creation', function indexPatternCreation() {
|
||||
bdd.before(function () {
|
||||
return settingsPage.createIndexPattern();
|
||||
});
|
||||
|
||||
bdd.it('should have index pattern in page header', function pageHeader() {
|
||||
return settingsPage.getIndexPageHeading().getVisibleText()
|
||||
.then(function (patternName) {
|
||||
expect(patternName).to.be('logstash-*');
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
bdd.it('should have index pattern in url', function url() {
|
||||
return common.tryForTime(5000, function () {
|
||||
return remote.getCurrentUrl()
|
||||
.then(function (currentUrl) {
|
||||
expect(currentUrl).to.contain('logstash-*');
|
||||
});
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
bdd.it('should have expected table headers', function checkingHeader() {
|
||||
return settingsPage.getTableHeader()
|
||||
.then(function (headers) {
|
||||
var expectedHeaders = [
|
||||
'name',
|
||||
'type',
|
||||
'format',
|
||||
'analyzed',
|
||||
'indexed',
|
||||
'controls'
|
||||
];
|
||||
|
||||
// 6 name type format analyzed indexed controls
|
||||
expect(headers.length).to.be(expectedHeaders.length);
|
||||
|
||||
var comparedHeaders = headers.map(function compareHead(header, i) {
|
||||
return header.getVisibleText()
|
||||
.then(function (text) {
|
||||
expect(text).to.be(expectedHeaders[i]);
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(comparedHeaders);
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
});
|
||||
|
||||
bdd.describe('index pattern deletion', function indexDelete() {
|
||||
bdd.before(function () {
|
||||
var expectedAlertText = 'Are you sure you want to remove this index pattern?';
|
||||
return settingsPage.removeIndexPattern()
|
||||
.then(function (alertText) {
|
||||
expect(alertText).to.be(expectedAlertText);
|
||||
});
|
||||
});
|
||||
|
||||
bdd.it('should return to index pattern creation page', function returnToPage() {
|
||||
return common.tryForTime(5000, function () {
|
||||
return settingsPage.getCreateButton();
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
bdd.it('should remove index pattern from url', function indexNotInUrl() {
|
||||
// give the url time to settle
|
||||
return common.tryForTime(5000, function () {
|
||||
return remote.getCurrentUrl()
|
||||
.then(function (currentUrl) {
|
||||
expect(currentUrl).to.not.contain('logstash-*');
|
||||
});
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
111
test/functional/apps/settings/_index_pattern_popularity.js
Normal file
111
test/functional/apps/settings/_index_pattern_popularity.js
Normal file
|
@ -0,0 +1,111 @@
|
|||
define(function (require) {
|
||||
var Common = require('../../../support/pages/Common');
|
||||
var SettingsPage = require('../../../support/pages/SettingsPage');
|
||||
var expect = require('intern/dojo/node!expect.js');
|
||||
//var Promise = require('bluebird');
|
||||
|
||||
return function (bdd, scenarioManager) {
|
||||
bdd.describe('index result popularity', function describeIndexTests() {
|
||||
var common;
|
||||
var settingsPage;
|
||||
var remote;
|
||||
|
||||
bdd.before(function () {
|
||||
common = new Common(this.remote);
|
||||
settingsPage = new SettingsPage(this.remote);
|
||||
remote = this.remote;
|
||||
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
.then(function () {
|
||||
return settingsPage.navigateTo();
|
||||
});
|
||||
});
|
||||
|
||||
bdd.beforeEach(function be() {
|
||||
return settingsPage.createIndexPattern();
|
||||
});
|
||||
|
||||
bdd.afterEach(function ae() {
|
||||
return settingsPage.removeIndexPattern();
|
||||
});
|
||||
|
||||
bdd.describe('change popularity', function indexPatternCreation() {
|
||||
var fieldName = 'geo.coordinates';
|
||||
|
||||
// set the page size to All again, https://github.com/elastic/kibana/issues/5030
|
||||
// TODO: remove this after issue #5030 is closed
|
||||
function fix5030() {
|
||||
return settingsPage.setPageSize('All')
|
||||
.then(function () {
|
||||
return common.sleep(1000);
|
||||
});
|
||||
}
|
||||
|
||||
bdd.beforeEach(function () {
|
||||
// increase Popularity of geo.coordinates
|
||||
return settingsPage.setPageSize('All')
|
||||
.then(function () {
|
||||
return common.sleep(1000);
|
||||
})
|
||||
.then(function openControlsByName() {
|
||||
return settingsPage.openControlsByName(fieldName);
|
||||
})
|
||||
.then(function increasePopularity() {
|
||||
return settingsPage.increasePopularity();
|
||||
});
|
||||
});
|
||||
|
||||
bdd.afterEach(function () {
|
||||
// Cancel saving the popularity change (we didn't make a change in this case, just checking the value)
|
||||
return settingsPage.controlChangeCancel();
|
||||
});
|
||||
|
||||
bdd.it('should update the popularity input', function () {
|
||||
return settingsPage.getPopularity()
|
||||
.then(function (popularity) {
|
||||
expect(popularity).to.be('1');
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
bdd.it('should be reset on cancel', function pageHeader() {
|
||||
// Cancel saving the popularity change
|
||||
return settingsPage.controlChangeCancel()
|
||||
.then(function () {
|
||||
return fix5030();
|
||||
})
|
||||
.then(function openControlsByName() {
|
||||
return settingsPage.openControlsByName(fieldName);
|
||||
})
|
||||
// check that its 0 (previous increase was cancelled)
|
||||
.then(function getPopularity() {
|
||||
return settingsPage.getPopularity();
|
||||
})
|
||||
.then(function (popularity) {
|
||||
expect(popularity).to.be('0');
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
bdd.it('can be saved', function pageHeader() {
|
||||
// Saving the popularity change
|
||||
return settingsPage.controlChangeSave()
|
||||
.then(function () {
|
||||
return fix5030();
|
||||
})
|
||||
.then(function openControlsByName() {
|
||||
return settingsPage.openControlsByName(fieldName);
|
||||
})
|
||||
// check that its 0 (previous increase was cancelled)
|
||||
.then(function getPopularity() {
|
||||
return settingsPage.getPopularity();
|
||||
})
|
||||
.then(function (popularity) {
|
||||
expect(popularity).to.be('1');
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
}); // end 'change popularity'
|
||||
}); // end index result popularity
|
||||
};
|
||||
});
|
134
test/functional/apps/settings/_index_pattern_results_sort.js
Normal file
134
test/functional/apps/settings/_index_pattern_results_sort.js
Normal file
|
@ -0,0 +1,134 @@
|
|||
define(function (require) {
|
||||
var Common = require('../../../support/pages/Common');
|
||||
var SettingsPage = require('../../../support/pages/SettingsPage');
|
||||
var expect = require('intern/dojo/node!expect.js');
|
||||
var Promise = require('bluebird');
|
||||
|
||||
return function (bdd, scenarioManager) {
|
||||
bdd.describe('index result field sort', function describeIndexTests() {
|
||||
var common;
|
||||
var settingsPage;
|
||||
var remote;
|
||||
|
||||
bdd.before(function () {
|
||||
common = new Common(this.remote);
|
||||
settingsPage = new SettingsPage(this.remote);
|
||||
remote = this.remote;
|
||||
|
||||
return scenarioManager.reload('emptyKibana');
|
||||
});
|
||||
|
||||
var columns = [{
|
||||
heading: 'name',
|
||||
first: '@message',
|
||||
last: 'xss.raw',
|
||||
selector: function () {
|
||||
return settingsPage.getTableRow(0, 0).getVisibleText();
|
||||
}
|
||||
}, {
|
||||
heading: 'type',
|
||||
first: '_source',
|
||||
last: 'string',
|
||||
selector: function () {
|
||||
return settingsPage.getTableRow(0, 1).getVisibleText();
|
||||
}
|
||||
}];
|
||||
|
||||
columns.forEach(function (col) {
|
||||
bdd.describe('sort by heading - ' + col.heading, function indexPatternCreation() {
|
||||
bdd.before(function () {
|
||||
return settingsPage.navigateTo();
|
||||
});
|
||||
|
||||
bdd.beforeEach(function () {
|
||||
return settingsPage.createIndexPattern();
|
||||
});
|
||||
|
||||
bdd.afterEach(function () {
|
||||
return settingsPage.removeIndexPattern();
|
||||
});
|
||||
|
||||
bdd.it('should sort ascending', function pageHeader() {
|
||||
return settingsPage.sortBy(col.heading)
|
||||
.then(function getText() {
|
||||
return col.selector();
|
||||
})
|
||||
.then(function (rowText) {
|
||||
expect(rowText).to.be(col.first);
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
bdd.it('should sort descending', function pageHeader() {
|
||||
return settingsPage.sortBy(col.heading)
|
||||
.then(function sortAgain() {
|
||||
return settingsPage.sortBy(col.heading);
|
||||
})
|
||||
.then(function getText() {
|
||||
return col.selector();
|
||||
})
|
||||
.then(function (rowText) {
|
||||
expect(rowText).to.be(col.last);
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
bdd.describe('field list pagination', function () {
|
||||
var expectedDefaultPageSize = 25;
|
||||
var expectedFieldCount = 85;
|
||||
var expectedLastPageCount = 10;
|
||||
var pages = [1, 2, 3, 4];
|
||||
|
||||
bdd.before(function () {
|
||||
return settingsPage.navigateTo()
|
||||
.then(function () {
|
||||
return settingsPage.createIndexPattern();
|
||||
});
|
||||
});
|
||||
|
||||
bdd.after(function () {
|
||||
return settingsPage.removeIndexPattern();
|
||||
});
|
||||
|
||||
bdd.it('makelogs data should have expected number of fields', function () {
|
||||
return settingsPage.getFieldsTabCount()
|
||||
.then(function (tabCount) {
|
||||
expect(tabCount).to.be('' + expectedFieldCount);
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
bdd.it('should have correct default page size selected', function () {
|
||||
return settingsPage.getPageSize()
|
||||
.then(function (pageSize) {
|
||||
expect(pageSize).to.be('' + expectedDefaultPageSize);
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
bdd.it('should have the correct number of rows per page', function () {
|
||||
var pageCount = Math.ceil(expectedFieldCount / expectedDefaultPageSize);
|
||||
var chain = pages.reduce(function (chain, val) {
|
||||
return chain.then(function () {
|
||||
return settingsPage.goToPage(val)
|
||||
.then(function () {
|
||||
return common.sleep(1000);
|
||||
})
|
||||
.then(function () {
|
||||
return settingsPage.getPageFieldCount();
|
||||
})
|
||||
.then(function (pageCount) {
|
||||
var expectedSize = (val < 4) ? expectedDefaultPageSize : expectedLastPageCount;
|
||||
expect(pageCount.length).to.be(expectedSize);
|
||||
});
|
||||
});
|
||||
}, Promise.resolve());
|
||||
|
||||
return chain.catch(common.handleError(this));
|
||||
});
|
||||
}); // end describe pagination
|
||||
}); // end index result field sort
|
||||
};
|
||||
});
|
64
test/functional/apps/settings/_initial_state.js
Normal file
64
test/functional/apps/settings/_initial_state.js
Normal file
|
@ -0,0 +1,64 @@
|
|||
define(function (require) {
|
||||
var expect = require('intern/dojo/node!expect.js');
|
||||
var Common = require('../../../support/pages/Common');
|
||||
var SettingsPage = require('../../../support/pages/SettingsPage');
|
||||
|
||||
return function (bdd, scenarioManager) {
|
||||
bdd.describe('initial state', function () {
|
||||
var common;
|
||||
var settingsPage;
|
||||
|
||||
bdd.before(function () {
|
||||
common = new Common(this.remote);
|
||||
settingsPage = new SettingsPage(this.remote);
|
||||
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
.then(function () {
|
||||
return settingsPage.navigateTo();
|
||||
});
|
||||
});
|
||||
|
||||
bdd.it('should load with time pattern checked', function () {
|
||||
return settingsPage.getTimeBasedEventsCheckbox().isSelected()
|
||||
.then(function (selected) {
|
||||
expect(selected).to.be.ok();
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
bdd.it('should load with name pattern unchecked', function () {
|
||||
return settingsPage.getTimeBasedIndexPatternCheckbox().isSelected()
|
||||
.then(function (selected) {
|
||||
expect(selected).to.not.be.ok();
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
bdd.it('should contain default index pattern', function () {
|
||||
var defaultPattern = 'logstash-*';
|
||||
|
||||
return settingsPage.getIndexPatternField().getProperty('value')
|
||||
.then(function (pattern) {
|
||||
expect(pattern).to.be(defaultPattern);
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
bdd.it('should not select the time field', function () {
|
||||
return settingsPage.getTimeFieldNameField().isSelected()
|
||||
.then(function (timeFieldIsSelected) {
|
||||
expect(timeFieldIsSelected).to.not.be.ok();
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
bdd.it('should not be enable creation', function () {
|
||||
return settingsPage.getCreateButton().isEnabled()
|
||||
.then(function (enabled) {
|
||||
expect(enabled).to.not.be.ok();
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
38
test/functional/apps/settings/index.js
Normal file
38
test/functional/apps/settings/index.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
define(function (require) {
|
||||
var bdd = require('intern!bdd');
|
||||
var config = require('intern').config;
|
||||
var url = require('intern/dojo/node!url');
|
||||
var ScenarioManager = require('intern/dojo/node!../../../fixtures/scenarioManager');
|
||||
|
||||
var initialStateTest = require('./_initial_state');
|
||||
var creationChangesTest = require('./_creation_form_changes');
|
||||
var indexPatternCreateDeleteTest = require('./_index_pattern_create_delete');
|
||||
var indexPatternResultsSortTest = require('./_index_pattern_results_sort');
|
||||
var indexPatternPopularityTest = require('./_index_pattern_popularity');
|
||||
|
||||
bdd.describe('settings app', function () {
|
||||
var scenarioManager = new ScenarioManager(url.format(config.servers.elasticsearch));
|
||||
|
||||
// on setup, we create an settingsPage instance
|
||||
// that we will use for all the tests
|
||||
bdd.before(function () {
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
.then(function () {
|
||||
return scenarioManager.loadIfEmpty('makelogs');
|
||||
});
|
||||
});
|
||||
|
||||
bdd.after(function () {
|
||||
return scenarioManager.unload('makelogs')
|
||||
.then(function () {
|
||||
scenarioManager.unload('emptyKibana');
|
||||
});
|
||||
});
|
||||
|
||||
initialStateTest(bdd, scenarioManager);
|
||||
creationChangesTest(bdd, scenarioManager);
|
||||
indexPatternCreateDeleteTest(bdd, scenarioManager);
|
||||
indexPatternResultsSortTest(bdd, scenarioManager);
|
||||
indexPatternPopularityTest(bdd, scenarioManager);
|
||||
});
|
||||
});
|
|
@ -1,21 +0,0 @@
|
|||
define(function (require) {
|
||||
var registerSuite = require('intern!object');
|
||||
var expect = require('intern/dojo/node!expect.js');
|
||||
var config = require('intern').config;
|
||||
var getUrl = require('intern/dojo/node!../utils/getUrl');
|
||||
|
||||
registerSuite(function () {
|
||||
return {
|
||||
'status': function () {
|
||||
return this.remote
|
||||
.get(getUrl(config.kibana, 'status'))
|
||||
.setFindTimeout(60000)
|
||||
.findByCssSelector('.plugin_status_breakdown')
|
||||
.getVisibleText()
|
||||
.then(function (text) {
|
||||
expect(text.indexOf('plugin:kibana Ready')).to.be.above(-1);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
30
test/functional/status_page/index.js
Normal file
30
test/functional/status_page/index.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
define(function (require) {
|
||||
var bdd = require('intern!bdd');
|
||||
var expect = require('intern/dojo/node!expect.js');
|
||||
var config = require('intern').config;
|
||||
var Common = require('../../support/pages/Common');
|
||||
|
||||
bdd.describe('status page', function () {
|
||||
var common;
|
||||
|
||||
bdd.before(function () {
|
||||
common = new Common(this.remote);
|
||||
// load the status page
|
||||
return common.navigateToApp('statusPage', false);
|
||||
});
|
||||
|
||||
bdd.it('should show the kibana plugin as ready', function () {
|
||||
var self = this;
|
||||
|
||||
return common.tryForTime(6000, function () {
|
||||
return self.remote
|
||||
.findByCssSelector('.plugin_status_breakdown')
|
||||
.getVisibleText()
|
||||
.then(function (text) {
|
||||
expect(text.indexOf('plugin:kibana Ready')).to.be.above(-1);
|
||||
});
|
||||
})
|
||||
.catch(common.handleError(self));
|
||||
});
|
||||
});
|
||||
});
|
|
@ -3,6 +3,7 @@ define(function (require) {
|
|||
var _ = require('intern/dojo/node!lodash');
|
||||
|
||||
return _.assign({
|
||||
debug: false,
|
||||
capabilities: {
|
||||
'selenium-version': '2.47.1',
|
||||
'idle-timeout': 30
|
||||
|
@ -10,8 +11,17 @@ define(function (require) {
|
|||
environments: [{
|
||||
browserName: 'firefox'
|
||||
}],
|
||||
tunnelOptions: serverConfig.webdriver,
|
||||
functionalSuites: ['test/functional/status.js'],
|
||||
excludeInstrumentation: /(fixtures|node_modules)\//
|
||||
tunnelOptions: serverConfig.servers.webdriver,
|
||||
functionalSuites: [
|
||||
'test/functional/status_page/index',
|
||||
'test/functional/apps/settings/index'
|
||||
],
|
||||
excludeInstrumentation: /(fixtures|node_modules)\//,
|
||||
loaderOptions: {
|
||||
paths: {
|
||||
'bluebird': './node_modules/bluebird/js/browser/bluebird.js',
|
||||
'moment': './node_modules/moment/moment.js'
|
||||
}
|
||||
}
|
||||
}, serverConfig);
|
||||
});
|
||||
|
|
0
test/output/.empty
Normal file
0
test/output/.empty
Normal file
|
@ -1,17 +1,42 @@
|
|||
var kibanaURL = '/app/kibana';
|
||||
|
||||
module.exports = {
|
||||
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
|
||||
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
|
||||
},
|
||||
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
|
||||
},
|
||||
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
|
||||
}
|
||||
},
|
||||
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
|
||||
},
|
||||
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
|
||||
apps: {
|
||||
statusPage: {
|
||||
pathname: 'status'
|
||||
},
|
||||
discover: {
|
||||
pathname: kibanaURL,
|
||||
hash: '/discover',
|
||||
},
|
||||
visualize: {
|
||||
pathname: kibanaURL,
|
||||
hash: '/visualize',
|
||||
},
|
||||
dashboard: {
|
||||
pathname: kibanaURL,
|
||||
hash: '/dashboard',
|
||||
},
|
||||
settings: {
|
||||
pathname: kibanaURL,
|
||||
hash: '/settings'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
198
test/support/pages/Common.js
Normal file
198
test/support/pages/Common.js
Normal file
|
@ -0,0 +1,198 @@
|
|||
// in test/support/pages/Common.js
|
||||
define(function (require) {
|
||||
var config = require('intern').config;
|
||||
var Promise = require('bluebird');
|
||||
var moment = require('moment');
|
||||
var getUrl = require('intern/dojo/node!../../utils/getUrl');
|
||||
var fs = require('intern/dojo/node!fs');
|
||||
var path = require('intern/dojo/node!path');
|
||||
|
||||
function Common(remote) {
|
||||
this.remote = remote;
|
||||
}
|
||||
|
||||
var defaultTimeout = 60000;
|
||||
|
||||
Common.prototype = {
|
||||
constructor: Common,
|
||||
|
||||
navigateToApp: function (appName, testStatusPage) {
|
||||
var self = this;
|
||||
var appUrl = getUrl(config.servers.kibana, config.apps[appName]);
|
||||
|
||||
var doNavigation = function (url) {
|
||||
return self.tryForTime(defaultTimeout, function () {
|
||||
// since we're using hash URLs, always reload first to force re-render
|
||||
return self.remote.get(url)
|
||||
.then(function () {
|
||||
return self.remote.refresh();
|
||||
})
|
||||
.then(function () {
|
||||
if (testStatusPage !== false) {
|
||||
return self.checkForKibanaApp()
|
||||
.then(function (kibanaLoaded) {
|
||||
if (!kibanaLoaded) throw new Error('Kibana is not loaded, retrying');
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
return self.remote.getCurrentUrl();
|
||||
})
|
||||
.then(function (currentUrl) {
|
||||
var navSuccessful = new RegExp(appUrl).test(currentUrl);
|
||||
if (!navSuccessful) throw new Error('App failed to load: ' + appName);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return doNavigation(appUrl)
|
||||
.then(function () {
|
||||
return self.remote.getCurrentUrl();
|
||||
})
|
||||
.then(function (currentUrl) {
|
||||
var lastUrl = currentUrl;
|
||||
return self.tryForTime(defaultTimeout, function () {
|
||||
// give the app time to update the URL
|
||||
return self.sleep(500)
|
||||
.then(function () {
|
||||
return self.remote.getCurrentUrl();
|
||||
})
|
||||
.then(function (currentUrl) {
|
||||
if (lastUrl !== currentUrl) {
|
||||
lastUrl = currentUrl;
|
||||
throw new Error('URL changed, waiting for it to settle');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
runScript: function (fn, timeout) {
|
||||
var self = this;
|
||||
// by default, give the app 10 seconds to load
|
||||
timeout = timeout || 10000;
|
||||
|
||||
// wait for deps on window before running script
|
||||
return self.remote
|
||||
.setExecuteAsyncTimeout(timeout)
|
||||
.executeAsync(function (done) {
|
||||
var interval = setInterval(function () {
|
||||
var ready = (document.readyState === 'complete');
|
||||
var hasJQuery = !!window.$;
|
||||
|
||||
if (ready && hasJQuery) {
|
||||
console.log('doc ready, jquery loaded');
|
||||
clearInterval(interval);
|
||||
done();
|
||||
}
|
||||
}, 10);
|
||||
}).then(function () {
|
||||
return self.remote.execute(fn);
|
||||
});
|
||||
},
|
||||
|
||||
getApp: function () {
|
||||
var self = this;
|
||||
|
||||
return self.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('.content > .application')
|
||||
.then(function () {
|
||||
return self.runScript(function () {
|
||||
var $ = window.$;
|
||||
var $scope = $('.content > .application').scope();
|
||||
return $scope ? $scope.chrome.getApp() : {};
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
checkForKibanaApp: function () {
|
||||
var self = this;
|
||||
|
||||
return self.getApp()
|
||||
.then(function (app) {
|
||||
var appId = app.id;
|
||||
self.debug('current application: ' + appId);
|
||||
return appId === 'kibana';
|
||||
})
|
||||
.catch(function (err) {
|
||||
self.debug('kibana check failed');
|
||||
self.debug(err);
|
||||
// not on the kibana app...
|
||||
return false;
|
||||
});
|
||||
},
|
||||
|
||||
tryForTime: function (timeout, block) {
|
||||
var self = this;
|
||||
var start = Date.now();
|
||||
var retryDelay = 500;
|
||||
var lastTry = 0;
|
||||
|
||||
function attempt() {
|
||||
lastTry = Date.now();
|
||||
|
||||
if (lastTry - start > timeout) {
|
||||
throw new Error('timeout');
|
||||
}
|
||||
|
||||
return Promise
|
||||
.try(block)
|
||||
.then(function tryForTimeSuccess() {
|
||||
self.debug('tryForTime success in about ' + (lastTry - start) + ' ms');
|
||||
return (lastTry - start);
|
||||
})
|
||||
.catch(function tryForTimeCatch(err) {
|
||||
self.debug('tryForTime failure, retry in ' + retryDelay + 'ms - ' + err.message);
|
||||
return Promise.delay(retryDelay).then(attempt);
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.try(attempt);
|
||||
},
|
||||
|
||||
log: function (logString) {
|
||||
console.log(moment().format('HH:mm:ss.SSS') + ': ' + logString);
|
||||
},
|
||||
|
||||
debug: function (logString) {
|
||||
if (config.debug) this.log(logString);
|
||||
},
|
||||
|
||||
sleep: function (sleepMilliseconds) {
|
||||
this.debug('sleeping for ' + sleepMilliseconds + 'ms');
|
||||
return Promise.resolve().delay(sleepMilliseconds);
|
||||
},
|
||||
|
||||
handleError: function (testObj) {
|
||||
var self = this;
|
||||
var testName = (testObj.parent) ? [testObj.parent.name, testObj.name].join('_') : testObj.name;
|
||||
|
||||
return function (reason) {
|
||||
var now = Date.now();
|
||||
var filename = ['failure', now, testName].join('_') + '.png';
|
||||
|
||||
return self.saveScreenshot(filename)
|
||||
.finally(function () {
|
||||
throw new Error(reason);
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
saveScreenshot: function (filename) {
|
||||
var self = this;
|
||||
var outDir = path.resolve('test', 'output');
|
||||
|
||||
return self.remote.takeScreenshot()
|
||||
.then(function writeScreenshot(data) {
|
||||
var filepath = path.resolve(outDir, filename);
|
||||
self.debug('Test Failed, taking screenshot "' + filepath + '"');
|
||||
fs.writeFileSync(filepath, data);
|
||||
})
|
||||
.catch(function (err) {
|
||||
self.log('SCREENSHOT FAILED: ' + err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return Common;
|
||||
});
|
54
test/support/pages/HeaderPage.js
Normal file
54
test/support/pages/HeaderPage.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
// in test/support/pages/HeaderPage.js
|
||||
define(function (require) {
|
||||
|
||||
var Common = require('./Common');
|
||||
|
||||
var common;
|
||||
|
||||
// the page object is created as a constructor
|
||||
// so we can provide the remote Command object
|
||||
// at runtime
|
||||
function HeaderPage(remote) {
|
||||
this.remote = remote;
|
||||
common = new Common(this.remote);
|
||||
}
|
||||
|
||||
var defaultTimeout = 5000;
|
||||
|
||||
HeaderPage.prototype = {
|
||||
constructor: HeaderPage,
|
||||
|
||||
clickSelector: function (selector) {
|
||||
var self = this.remote;
|
||||
return common.tryForTime(5000, function () {
|
||||
return self.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector(selector)
|
||||
.then(function (tab) {
|
||||
return tab.click();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
clickDiscover: function () {
|
||||
common.debug('click Discover tab');
|
||||
this.clickSelector('a[href*=\'discover\']');
|
||||
},
|
||||
|
||||
clickVisualize: function () {
|
||||
common.debug('click Visualize tab');
|
||||
this.clickSelector('a[href*=\'visualize\']');
|
||||
},
|
||||
|
||||
clickDashboard: function () {
|
||||
common.debug('click Dashboard tab');
|
||||
this.clickSelector('a[href*=\'dashboard\']');
|
||||
},
|
||||
|
||||
clickSettings: function () {
|
||||
common.debug('click Settings tab');
|
||||
this.clickSelector('a[href*=\'settings\']');
|
||||
}
|
||||
};
|
||||
|
||||
return HeaderPage;
|
||||
});
|
290
test/support/pages/SettingsPage.js
Normal file
290
test/support/pages/SettingsPage.js
Normal file
|
@ -0,0 +1,290 @@
|
|||
// in test/support/pages/SettingsPage.js
|
||||
define(function (require) {
|
||||
// the page object is created as a constructor
|
||||
// so we can provide the remote Command object
|
||||
// at runtime
|
||||
|
||||
var Promise = require('bluebird');
|
||||
var Common = require('./Common');
|
||||
|
||||
var defaultTimeout = 60000;
|
||||
var common;
|
||||
|
||||
function SettingsPage(remote) {
|
||||
this.remote = remote;
|
||||
common = new Common(this.remote);
|
||||
}
|
||||
|
||||
SettingsPage.prototype = {
|
||||
constructor: SettingsPage,
|
||||
|
||||
navigateTo: function () {
|
||||
return common.navigateToApp('settings');
|
||||
},
|
||||
|
||||
getTimeBasedEventsCheckbox: function () {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('input[ng-model="index.isTimeBased"]');
|
||||
},
|
||||
|
||||
getTimeBasedIndexPatternCheckbox: function (timeout) {
|
||||
timeout = timeout || defaultTimeout;
|
||||
// fail faster since we're sometimes checking that it doesn't exist
|
||||
return this.remote.setFindTimeout(timeout)
|
||||
.findByCssSelector('input[ng-model="index.nameIsPattern"]');
|
||||
},
|
||||
|
||||
getIndexPatternField: function () {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('[ng-model="index.name"]');
|
||||
},
|
||||
|
||||
getTimeFieldNameField: function () {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('select[ng-model="index.timeField"]');
|
||||
},
|
||||
|
||||
selectTimeFieldOption: function (selection) {
|
||||
var self = this;
|
||||
|
||||
return self.getTimeFieldNameField().click()
|
||||
.then(function () {
|
||||
return self.getTimeFieldNameField().click();
|
||||
})
|
||||
.then(function () {
|
||||
return self.getTimeFieldOption(selection);
|
||||
});
|
||||
},
|
||||
|
||||
getTimeFieldOption: function (selection) {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('option[label="' + selection + '"]').click();
|
||||
},
|
||||
|
||||
getCreateButton: function () {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('.btn');
|
||||
},
|
||||
|
||||
clickCreateButton: function () {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('.btn').click();
|
||||
},
|
||||
|
||||
clickDefaultIndexButton: function () {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('button.btn.btn-warning.ng-scope').click();
|
||||
},
|
||||
|
||||
clickDeletePattern: function () {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('button.btn.btn-danger.ng-scope').click();
|
||||
},
|
||||
|
||||
getIndexPageHeading: function () {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('h1.title.ng-binding.ng-isolate-scope');
|
||||
},
|
||||
|
||||
getConfigureHeader: function () {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('h1');
|
||||
},
|
||||
getTableHeader: function () {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findAllByCssSelector('table.table.table-condensed thead tr th');
|
||||
},
|
||||
|
||||
sortBy: function (columnName) {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findAllByCssSelector('table.table.table-condensed thead tr th span')
|
||||
.then(function (chartTypes) {
|
||||
function getChartType(chart) {
|
||||
return chart.getVisibleText()
|
||||
.then(function (chartString) {
|
||||
if (chartString === columnName) {
|
||||
return chart.click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var getChartTypesPromises = chartTypes.map(getChartType);
|
||||
return Promise.all(getChartTypesPromises);
|
||||
});
|
||||
},
|
||||
|
||||
getTableRow: function (rowNumber, colNumber) {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
// passing in zero-based index, but adding 1 for css 1-based indexes
|
||||
.findByCssSelector('div.agg-table-paginated table.table.table-condensed tbody tr:nth-child(' +
|
||||
(rowNumber + 1) + ') td.ng-scope:nth-child(' +
|
||||
(colNumber + 1) + ') span.ng-binding'
|
||||
);
|
||||
},
|
||||
|
||||
getFieldsTabCount: function () {
|
||||
var self = this;
|
||||
var selector = 'li.kbn-settings-tab.active a small';
|
||||
|
||||
return self.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector(selector).getVisibleText()
|
||||
.then(function (theText) {
|
||||
// the value has () around it, remove them
|
||||
return theText.replace(/\((.*)\)/, '$1');
|
||||
});
|
||||
},
|
||||
|
||||
getPageSize: function () {
|
||||
var selectedItemLabel = '';
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findAllByCssSelector('select.ng-pristine.ng-valid.ng-untouched option')
|
||||
.then(function (chartTypes) {
|
||||
function getChartType(chart) {
|
||||
var thisChart = chart;
|
||||
return chart.isSelected()
|
||||
.then(function (isSelected) {
|
||||
if (isSelected === true) {
|
||||
return thisChart.getProperty('label')
|
||||
.then(function (theLabel) {
|
||||
selectedItemLabel = theLabel;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var getChartTypesPromises = chartTypes.map(getChartType);
|
||||
return Promise.all(getChartTypesPromises);
|
||||
})
|
||||
.then(function () {
|
||||
return selectedItemLabel;
|
||||
});
|
||||
},
|
||||
|
||||
getPageFieldCount: function () {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findAllByCssSelector('div.agg-table-paginated table.table.table-condensed tbody tr td.ng-scope:nth-child(1) span.ng-binding');
|
||||
},
|
||||
|
||||
goToPage: function (pageNum) {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('ul.pagination-other-pages-list.pagination-sm.ng-scope li.ng-scope:nth-child(' +
|
||||
(pageNum + 1) + ') a.ng-binding'
|
||||
)
|
||||
.then(function (page) {
|
||||
return page.click();
|
||||
});
|
||||
},
|
||||
|
||||
openControlsRow: function (row) {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('table.table.table-condensed tbody tr:nth-child(' +
|
||||
(row + 1) + ') td.ng-scope div.actions a.btn.btn-xs.btn-default i.fa.fa-pencil'
|
||||
)
|
||||
.then(function (page) {
|
||||
return page.click();
|
||||
});
|
||||
},
|
||||
|
||||
openControlsByName: function (name) {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('div.actions a.btn.btn-xs.btn-default[href$="/' + name + '"]')
|
||||
.then(function (button) {
|
||||
return button.click();
|
||||
});
|
||||
},
|
||||
|
||||
increasePopularity: function () {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('button.btn.btn-default[aria-label="Plus"]')
|
||||
.then(function (button) {
|
||||
return button.click();
|
||||
});
|
||||
},
|
||||
|
||||
getPopularity: function () {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('input[ng-model="editor.field.count"]')
|
||||
.then(function (input) {
|
||||
return input.getProperty('value');
|
||||
});
|
||||
},
|
||||
|
||||
controlChangeCancel: function () {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('button.btn.btn-primary[aria-label="Cancel"]')
|
||||
.then(function (button) {
|
||||
return button.click();
|
||||
});
|
||||
},
|
||||
|
||||
controlChangeSave: function () {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('button.btn.btn-success.ng-binding[aria-label="Update Field"]')
|
||||
.then(function (button) {
|
||||
return button.click();
|
||||
});
|
||||
},
|
||||
|
||||
setPageSize: function (size) {
|
||||
return this.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('form.form-inline.pagination-size.ng-scope.ng-pristine.ng-valid div.form-group option[label="' + size + '"]')
|
||||
.then(function (button) {
|
||||
return button.click();
|
||||
});
|
||||
},
|
||||
|
||||
createIndexPattern: function () {
|
||||
var self = this;
|
||||
|
||||
return common.tryForTime(defaultTimeout, function () {
|
||||
return self.selectTimeFieldOption('@timestamp')
|
||||
.then(function () {
|
||||
return self.getCreateButton().click();
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
return common.tryForTime(defaultTimeout, function () {
|
||||
return self.remote.getCurrentUrl()
|
||||
.then(function (currentUrl) {
|
||||
if (!currentUrl.match(/indices\/.+\?/)) {
|
||||
throw new Error('Index pattern not created');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
removeIndexPattern: function () {
|
||||
var self = this;
|
||||
var alertText;
|
||||
|
||||
return common.tryForTime(defaultTimeout, function () {
|
||||
return self.clickDeletePattern()
|
||||
.then(function () {
|
||||
return self.remote.getAlertText();
|
||||
})
|
||||
.then(function (text) {
|
||||
alertText = text;
|
||||
})
|
||||
.then(function () {
|
||||
return self.remote.acceptAlert();
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
return common.tryForTime(defaultTimeout, function () {
|
||||
return self.remote.getCurrentUrl()
|
||||
.then(function (currentUrl) {
|
||||
if (currentUrl.match(/indices\/.+\?/)) {
|
||||
throw new Error('Index pattern not removed');
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
return alertText;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return SettingsPage;
|
||||
});
|
|
@ -2,17 +2,36 @@ var expect = require('expect.js');
|
|||
var getUrl = require('../getUrl');
|
||||
|
||||
describe('getUrl', function () {
|
||||
it('should be able to convert a config and a path to a url', function () {
|
||||
expect(getUrl({
|
||||
it('should convert to a url', function () {
|
||||
var url = getUrl({
|
||||
protocol: 'http',
|
||||
hostname: 'localhost',
|
||||
}, {
|
||||
pathname: 'foo'
|
||||
});
|
||||
|
||||
expect(url).to.be('http://localhost/foo');
|
||||
});
|
||||
|
||||
it('should convert to a secure url with port', function () {
|
||||
var url = getUrl({
|
||||
protocol: 'http',
|
||||
hostname: 'localhost',
|
||||
port: 9220
|
||||
}, 'foo')).to.be('http://localhost:9220/foo');
|
||||
}, {
|
||||
pathname: 'foo'
|
||||
});
|
||||
|
||||
expect(url).to.be('http://localhost:9220/foo');
|
||||
});
|
||||
|
||||
it('should convert to a secure hashed url', function () {
|
||||
expect(getUrl({
|
||||
protocol: 'https',
|
||||
hostname: 'localhost',
|
||||
}, 'foo')).to.be('https://localhost/foo');
|
||||
|
||||
}, {
|
||||
pathname: 'foo',
|
||||
hash: 'bar'
|
||||
})).to.be('https://localhost/foo#bar');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
var _ = require('lodash');
|
||||
var url = require('url');
|
||||
|
||||
|
||||
/**
|
||||
* Converts a config and a pathname to a url
|
||||
* @param {object} config A url config
|
||||
|
@ -11,11 +10,14 @@ var url = require('url');
|
|||
* hostname: 'localhost',
|
||||
* port: 9220
|
||||
* }
|
||||
* @param {string} pathname The requested path
|
||||
* @param {object} app The params to append
|
||||
* example:
|
||||
* {
|
||||
* pathname: 'app/kibana',
|
||||
* hash: '/discover'
|
||||
* }
|
||||
* @return {string}
|
||||
*/
|
||||
module.exports = function getPage(config, pathname) {
|
||||
return url.format(_.assign(config, {
|
||||
pathname: pathname
|
||||
}));
|
||||
module.exports = function getPage(config, app) {
|
||||
return url.format(_.assign(config, app));
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue