+
+
+
+
+
+ `;
+
+
+describe('navbar directive', function () {
+ let $rootScope;
+ let $compile;
+ let stubRegistry;
+
+ beforeEach(function () {
+ ngMock.module('kibana', function (PrivateProvider) {
+ stubRegistry = new Registry({
+ index: ['name'],
+ group: ['appName'],
+ order: ['order']
+ });
+
+ PrivateProvider.swap(navbarExtensionsRegistry, stubRegistry);
+ });
+
+ ngMock.module('kibana/navbar');
+
+ // Create the scope
+ ngMock.inject(function ($injector) {
+ $rootScope = $injector.get('$rootScope');
+ $compile = $injector.get('$compile');
+ });
+ });
+
+ function init(markup = defaultMarkup) {
+ // Give us a scope
+ const $el = angular.element(markup);
+ $compile($el)($rootScope);
+ $el.scope().$digest();
+ return $el;
+ }
+
+ describe('incorrect use', function () {
+ it('should throw if missing a name property', function () {
+ const markup = `
`;
+ expect(() => init(markup)).to.throwException(/requires a name attribute/);
+ });
+
+ it('should throw if missing a button group', function () {
+ const markup = `
`;
+ expect(() => init(markup)).to.throwException(/must have exactly 1 button group/);
+ });
+
+ it('should throw if multiple button groups', function () {
+ const markup = `
+
+
+
+
+
+
+
+ `;
+ expect(() => init(markup)).to.throwException(/must have exactly 1 button group/);
+ });
+
+ it('should throw if button group not direct child', function () {
+ const markup = `
`;
+ expect(() => init(markup)).to.throwException(/must have exactly 1 button group/);
+ });
+ });
+
+ describe('injecting extensions', function () {
+ function registerExtension(def = {}) {
+ stubRegistry.register(function () {
+ return _.defaults(def, {
+ name: 'exampleButton',
+ appName: 'testing',
+ order: 0,
+ template: `
+
`
+ });
+ });
+ }
+
+ it('should use the default markup', function () {
+ var $el = init();
+ expect($el.find('.button-group button').length).to.equal(3);
+ });
+
+ it('should append to end then order == 0', function () {
+ registerExtension({ order: 0 });
+ var $el = init();
+
+ expect($el.find('.button-group button').length).to.equal(4);
+ expect($el.find('.button-group button').last().hasClass('test-button')).to.be.ok();
+ });
+
+ it('should append to end then order > 0', function () {
+ registerExtension({ order: 1 });
+ var $el = init();
+
+ expect($el.find('.button-group button').length).to.equal(4);
+ expect($el.find('.button-group button').last().hasClass('test-button')).to.be.ok();
+ });
+
+ it('should append to end then order < 0', function () {
+ registerExtension({ order: -1 });
+ var $el = init();
+
+ expect($el.find('.button-group button').length).to.equal(4);
+ expect($el.find('.button-group button').first().hasClass('test-button')).to.be.ok();
+ });
+ });
+});
diff --git a/src/ui/public/navbar/navbar.js b/src/ui/public/navbar/navbar.js
new file mode 100644
index 000000000000..2e49a760708b
--- /dev/null
+++ b/src/ui/public/navbar/navbar.js
@@ -0,0 +1,56 @@
+const _ = require('lodash');
+const $ = require('jquery');
+const navbar = require('ui/modules').get('kibana/navbar');
+
+require('ui/render_directive');
+
+navbar.directive('navbar', function (Private, $compile) {
+ const navbarExtensions = Private(require('ui/registry/navbar_extensions'));
+ const getExtensions = _.memoize(function (name) {
+ if (!name) throw new Error('navbar directive requires a name attribute');
+ return _.sortBy(navbarExtensions.byAppName[name], 'order');
+ });
+
+ return {
+ restrict: 'E',
+ template: function ($el, $attrs) {
+ const $buttonGroup = $el.children('.button-group');
+ if ($buttonGroup.length !== 1) throw new Error('navbar must have exactly 1 button group');
+
+ const extensions = getExtensions($attrs.name);
+ const buttons = $buttonGroup.children().detach().toArray();
+ const controls = [
+ ...buttons.map(function (button) {
+ return {
+ order: 0,
+ $el: $(button),
+ };
+ }),
+ ...extensions.map(function (extension, i) {
+ return {
+ order: extension.order,
+ index: i,
+ extension: extension,
+ };
+ }),
+ ];
+
+ _.sortBy(controls, 'order').forEach(function (control) {
+ if (control.$el) {
+ return $buttonGroup.append(control.$el);
+ }
+
+ const { extension, index } = control;
+ const $ext = $(`
`);
+ $ext.html(extension.template);
+ $buttonGroup.append($ext);
+ });
+
+ return $el.html();
+ },
+ controllerAs: 'navbar',
+ controller: function ($attrs) {
+ this.extensions = getExtensions($attrs.name);
+ }
+ };
+});
diff --git a/src/ui/public/styles/base.less b/src/ui/public/styles/base.less
index 016c09af33c3..6dcb58bf4b15 100644
--- a/src/ui/public/styles/base.less
+++ b/src/ui/public/styles/base.less
@@ -457,3 +457,14 @@ fieldset {
overflow-y: hidden;
}
}
+
+.list-group {
+ .list-group-item {
+ &.active,
+ &.active:hover,
+ &.active:focus {
+ background-color: @list-group-menu-item-active-bg;
+ cursor: default;
+ }
+ }
+}
diff --git a/tasks/config/run.js b/tasks/config/run.js
index e5c6fbb7fcc0..aacd6a045378 100644
--- a/tasks/config/run.js
+++ b/tasks/config/run.js
@@ -86,6 +86,8 @@ module.exports = function (grunt) {
args: [
'--dev',
'--no-watch',
+ '--no-ssl',
+ '--no-base-path',
'--server.port=5610',
'--optimize.lazyPort=5611',
'--optimize.lazyPrebuild=true',
diff --git a/test/functional/apps/discover/_discover.js b/test/functional/apps/discover/_discover.js
index 100336e8fb80..ffd39f1286c1 100644
--- a/test/functional/apps/discover/_discover.js
+++ b/test/functional/apps/discover/_discover.js
@@ -48,10 +48,11 @@ define(function (require) {
bdd.describe('query', function () {
var queryName1 = 'Query # 1';
+ var fromTimeString = 'September 19th 2015, 06:31:44.000';
+ var toTimeString = 'September 23rd 2015, 18:31:44.000';
- bdd.it('should show correct time range string', function () {
- var expectedTimeRangeString =
- 'September 19th 2015, 06:31:44.000 to September 23rd 2015, 18:31:44.000';
+ bdd.it('should show correct time range string by timepicker', function () {
+ var expectedTimeRangeString = fromTimeString + ' to ' + toTimeString;
return discoverPage.getTimespanText()
.then(function (actualTimeString) {
expect(actualTimeString).to.be(expectedTimeRangeString);
@@ -59,6 +60,7 @@ define(function (require) {
.catch(common.handleError(this));
});
+
bdd.it('save query should show toast message and display query name', function () {
var expectedSavedQueryMessage = 'Discover: Saved Data Source "' + queryName1 + '"';
return discoverPage.saveSearch(queryName1)
@@ -91,6 +93,19 @@ define(function (require) {
.catch(common.handleError(this));
});
+ bdd.it('should show the correct hit count', function () {
+ var expectedHitCount = '14,004';
+ return common.tryForTime(20 * 1000, function tryingForTime() {
+ return discoverPage.getHitCount()
+ .then(function compareData(hitCount) {
+ expect(hitCount).to.be(expectedHitCount);
+ });
+ })
+ .catch(common.handleError(this));
+ });
+
+
+
bdd.it('should show the correct bar chart', function () {
var expectedBarChartData = [ '0', '0', '0', '0', '0', '0',
'2.7056249999999977', '14.771249999999995', '54.112500000000004',
@@ -104,25 +119,176 @@ define(function (require) {
];
return common.sleep(4000)
.then(function () {
- return common.tryForTime(20 * 1000, function tryingForTime() {
- return discoverPage.getBarChartData()
- .then(function compareData(paths) {
- // the largest bars are over 100 pixels high so this is less than 1% tolerance
- var barHeightTolerance = 1;
- for (var y = 0; y < expectedBarChartData.length; y++) {
- common.debug(y + ': expected = ' + expectedBarChartData[y] + ', actual = ' + paths[y] +
- ', Pass = ' + (Math.abs(expectedBarChartData[y] - paths[y]) < barHeightTolerance));
- };
- for (var x = 0; x < expectedBarChartData.length; x++) {
- expect(Math.abs(expectedBarChartData[x] - paths[x]) < barHeightTolerance).to.be.ok();
- }
- });
- });
+ return verifyChartData(expectedBarChartData);
+ })
+ .catch(common.handleError(this));
+ });
+
+ bdd.it('should show correct time range string in chart', function () {
+ var expectedTimeRangeString = fromTimeString + ' - ' + toTimeString;
+ return discoverPage.getChartTimespan()
+ .then(function (actualTimeString) {
+ expect(actualTimeString).to.be(expectedTimeRangeString);
+ })
+ .catch(common.handleError(this));
+ });
+
+ bdd.it('should show correct initial chart interval of 3 hours', function () {
+ var expectedChartInterval = 'by 3 hours';
+ return discoverPage.getChartInterval()
+ .then(function (actualInterval) {
+ expect(actualInterval).to.be(expectedChartInterval);
+ })
+ .catch(common.handleError(this));
+ });
+
+ bdd.it('should show correct data for chart interval Hourly', function () {
+ var chartInterval = 'Hourly';
+ var expectedBarChartData = [ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '1.2763636363636266', '1.914545454545447',
+ '4.680000000000007', '6.594545454545454', '11.061818181818168', '25.314545454545453',
+ '38.50363636363636', '46.374545454545455', '72.53999999999999', '93.60000000000001',
+ '102.10909090909091', '109.97999999999999', '111.04363636363637', '94.87636363636364',
+ '85.72909090909091', '68.28545454545454', '54.88363636363636', '36.58909090909091',
+ '20.209090909090904', '11.700000000000003', '8.083636363636359', '5.105454545454549',
+ '0.6381818181818204', '0.8509090909090986', '2.3400000000000034', '2.978181818181824',
+ '3.61636363636363', '8.083636363636359', '10.423636363636362', '24.46363636363637',
+ '32.33454545454545', '45.94909090909091', '67.00909090909092', '85.51636363636365',
+ '94.87636363636364', '109.1290909090909', '110.61818181818181', '100.83272727272727',
+ '89.55818181818182', '65.30727272727273', '48.92727272727274', '36.16363636363636',
+ '21.059999999999988', '10.210909090909098', '6.38181818181819', '3.190909090909088',
+ '2.127272727272725', '0.4254545454545422', '1.701818181818183', '1.4890909090909048',
+ '3.61636363636363', '7.232727272727274', '7.870909090909095', '22.123636363636365',
+ '32.54727272727273', '51.267272727272726', '66.58363636363637', '85.94181818181818',
+ '104.66181818181818', '108.91636363636364', '107.00181818181818', '100.62',
+ '80.62363636363636', '62.32909090909091', '58.92545454545455', '33.18545454545455',
+ '21.059999999999988', '11.274545454545446', '10.423636363636362', '3.403636363636366',
+ '1.914545454545447', '0.8509090909090986', '0', '0', '0', '0', '0', '0',
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'
+ ];
+ return discoverPage.setChartInterval(chartInterval)
+ .then(function () {
+ return common.sleep(8000);
+ })
+ .then(function () {
+ return verifyChartData(expectedBarChartData);
+ })
+ .catch(common.handleError(this));
+ });
+
+ bdd.it('should show correct data for chart interval Daily', function () {
+ var chartInterval = 'Daily';
+ var expectedBarChartData = [ '0', '111.3138', '107.96759999999999', '108.4122', '0'];
+ return discoverPage.setChartInterval(chartInterval)
+ .then(function () {
+ return common.sleep(8000);
+ })
+ .then(function () {
+ return verifyChartData(expectedBarChartData);
+ })
+ .catch(common.handleError(this));
+ });
+
+ bdd.it('should show correct data for chart interval Weekly', function () {
+ var chartInterval = 'Weekly';
+ var expectedBarChartData = [ '55.6569', '108.1899'];
+ return discoverPage.setChartInterval(chartInterval)
+ .then(function () {
+ return common.sleep(2000);
+ })
+ .then(function () {
+ return verifyChartData(expectedBarChartData);
+ })
+ .catch(common.handleError(this));
+ });
+
+ bdd.it('should show correct data for chart interval Monthly', function () {
+ var chartInterval = 'Monthly';
+ var expectedBarChartData = [ '102.404'];
+ return discoverPage.setChartInterval(chartInterval)
+ .then(function () {
+ return common.sleep(2000);
+ })
+ .then(function () {
+ return verifyChartData(expectedBarChartData);
+ })
+ .catch(common.handleError(this));
+ });
+
+ bdd.it('should show correct data for chart interval Yearly', function () {
+ var chartInterval = 'Yearly';
+ var expectedBarChartData = [ '102.404'];
+ return discoverPage.setChartInterval(chartInterval)
+ .then(function () {
+ return common.sleep(2000);
+ })
+ .then(function () {
+ return verifyChartData(expectedBarChartData);
})
.catch(common.handleError(this));
});
+
+ bdd.it('should show correct data for chart interval Auto', function () {
+ var chartInterval = 'Auto';
+ var expectedBarChartData = [ '0', '0', '0', '0', '0', '0',
+ '2.7056249999999977', '14.771249999999995', '54.112500000000004',
+ '105.080625', '100.25437500000001', '54.916875', '13.747499999999988',
+ '2.266874999999999', '3.0712500000000063', '14.771249999999995',
+ '49.944374999999994', '99.523125', '103.471875', '51.699375',
+ '12.943124999999995', '1.9743749999999949', '2.3400000000000034',
+ '12.796875', '51.699375', '102.96000000000001', '99.08437500000001',
+ '53.08875', '14.698125000000005', '2.1206249999999898', '0', '0',
+ '0', '0', '0', '0', '0'
+ ];
+ return discoverPage.setChartInterval(chartInterval)
+ .then(function () {
+ return common.sleep(4000);
+ })
+ .then(function () {
+ return verifyChartData(expectedBarChartData);
+ })
+ .catch(common.handleError(this));
+ });
+
+ bdd.it('should show Auto chart interval of 3 hours', function () {
+ var expectedChartInterval = 'by 3 hours';
+ return discoverPage.getChartInterval()
+ .then(function (actualInterval) {
+ expect(actualInterval).to.be(expectedChartInterval);
+ })
+ .catch(common.handleError(this));
+ });
+
+
+ function verifyChartData(expectedBarChartData) {
+ return common.tryForTime(20 * 1000, function tryingForTime() {
+ return discoverPage.getBarChartData()
+ .then(function compareData(paths) {
+ // the largest bars are over 100 pixels high so this is less than 1% tolerance
+ var barHeightTolerance = 1;
+ var stringResults = '';
+ var hasFailure = false;
+ for (var y = 0; y < expectedBarChartData.length; y++) {
+ stringResults += y + ': expected = ' + expectedBarChartData[y] + ', actual = ' + paths[y] +
+ ', Pass = ' + (Math.abs(expectedBarChartData[y] - paths[y]) < barHeightTolerance);
+ if ((Math.abs(expectedBarChartData[y] - paths[y]) > barHeightTolerance)) {
+ hasFailure = true;
+ };
+ };
+ if (hasFailure) {
+ common.log(stringResults);
+ common.log(paths);
+ }
+ for (var x = 0; x < expectedBarChartData.length; x++) {
+ expect(Math.abs(expectedBarChartData[x] - paths[x]) < barHeightTolerance).to.be.ok();
+ }
+ });
+ });
+
+ }
+
});
});
};
diff --git a/test/functional/apps/discover/_field_data.js b/test/functional/apps/discover/_field_data.js
new file mode 100644
index 000000000000..e50f65678ca2
--- /dev/null
+++ b/test/functional/apps/discover/_field_data.js
@@ -0,0 +1,268 @@
+define(function (require) {
+ var Common = require('../../../support/pages/Common');
+ var HeaderPage = require('../../../support/pages/HeaderPage');
+ var SettingsPage = require('../../../support/pages/settings_page');
+ var DiscoverPage = require('../../../support/pages/DiscoverPage');
+ var expect = require('intern/dojo/node!expect.js');
+
+ return function (bdd, scenarioManager) {
+ bdd.describe('discover app', function describeIndexTests() {
+ var common;
+ var headerPage;
+ var settingsPage;
+ var discoverPage;
+
+ bdd.before(function () {
+ common = new Common(this.remote);
+ headerPage = new HeaderPage(this.remote);
+ settingsPage = new SettingsPage(this.remote);
+ discoverPage = new DiscoverPage(this.remote);
+ var fromTime = '2015-09-19 06:31:44.000';
+ var toTime = '2015-09-23 18:31:44.000';
+
+ // start each test with an empty kibana index
+ return scenarioManager.reload('emptyKibana')
+ // and load a set of makelogs data
+ .then(function loadIfEmptyMakelogs() {
+ return scenarioManager.loadIfEmpty('logstashFunctional');
+ })
+ .then(function (navigateTo) {
+ common.debug('navigateTo');
+ return settingsPage.navigateTo();
+ })
+ .then(function () {
+ common.debug('createIndexPattern');
+ return settingsPage.createIndexPattern();
+ })
+ .then(function () {
+ common.debug('discover');
+ return common.navigateToApp('discover');
+ })
+ .then(function () {
+ common.debug('setAbsoluteRange');
+ return headerPage.setAbsoluteRange(fromTime, toTime);
+ })
+ .catch(common.handleError(this));
+ });
+
+
+ bdd.describe('field data', function () {
+ var queryName1 = 'Query # 1';
+ var fromTimeString = 'September 19th 2015, 06:31:44.000';
+ var toTimeString = 'September 23rd 2015, 18:31:44.000';
+
+
+ bdd.it('search php should show the correct hit count', function () {
+ var expectedHitCount = '445';
+ return discoverPage.query('php')
+ .then(function () {
+ return common.tryForTime(20 * 1000, function tryingForTime() {
+ return discoverPage.getHitCount()
+ .then(function compareData(hitCount) {
+ expect(hitCount).to.be(expectedHitCount);
+ });
+ });
+ })
+ .catch(common.handleError(this));
+ });
+
+ bdd.it('the search term should be highlighted in the field data', function () {
+ // marks is the style that highlights the text in yellow
+ return discoverPage.getMarks()
+ .then(function (marks) {
+ expect(marks.length).to.be(50);
+ expect(marks.indexOf('php')).to.be(0);
+ })
+ .catch(common.handleError(this));
+ });
+
+
+ bdd.it('search _type:apache should show the correct hit count', function () {
+ var expectedHitCount = '11,156';
+ return discoverPage.query('_type:apache')
+ .then(function () {
+ return common.tryForTime(20 * 1000, function tryingForTime() {
+ return discoverPage.getHitCount()
+ .then(function compareData(hitCount) {
+ expect(hitCount).to.be(expectedHitCount);
+ });
+ });
+ })
+ .catch(common.handleError(this));
+ });
+
+ bdd.it('doc view should show Time and _source columns', function () {
+ var expectedHeader = 'Time _source';
+ return discoverPage.getDocHeader()
+ .then(function (header) {
+ expect(header).to.be(expectedHeader);
+ })
+ .catch(common.handleError(this));
+ });
+
+ bdd.it('doc view should show oldest time first', function () {
+ // Note: Could just check the timestamp, but might as well check that the whole doc is as expected.
+ var ExpectedDoc =
+ 'September 22nd 2015, 23:50:13.253 index:logstash-2015.09.22 @timestamp:September 22nd 2015, 23:50:13.253'
+ + ' ip:238.171.34.42 extension:jpg response:200 geo.coordinates:{ "lat": 38.66494528, "lon": -88.45299556'
+ + ' } geo.src:FR geo.dest:KH geo.srcdest:FR:KH @tags:success, info utc_time:September 22nd 2015,'
+ + ' 23:50:13.253 referer:http://twitter.com/success/nancy-currie agent:Mozilla/4.0 (compatible; MSIE 6.0;'
+ + ' Windows NT 5.1; SV1; .NET CLR 1.1.4322) clientip:238.171.34.42 bytes:7,124'
+ + ' host:media-for-the-masses.theacademyofperformingartsandscience.org request:/uploads/karl-henize.jpg'
+ + ' url:https://media-for-the-masses.theacademyofperformingartsandscience.org/uploads/karl-henize.jpg'
+ + ' @message:238.171.34.42 - - [2015-09-22T23:50:13.253Z] "GET /uploads/karl-henize.jpg HTTP/1.1" 200 7124'
+ + ' "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)" spaces:this is a'
+ + ' thing with lots of spaces wwwwoooooo xss:'
+ + ' headings:
alexander-viktorenko, http://nytimes.com/warning/michael-massimino'
+ + ' links:@www.slate.com, http://www.slate.com/security/frederick-w-leslie, www.www.slate.com'
+ + ' relatedContent:{ "url": "http://www.laweekly.com/music/bjork-at-the-nokia-theatre-12-12-2408191",'
+ + ' "og:type": "article", "og:title": "Bjork at the Nokia Theatre, 12/12", "og:description": "Bjork at the'
+ + ' Nokia Theater, December 12 By Randall Roberts Last night’s Bjork show at the Dystopia –'
+ + ' er, I mean Nokia -- Theatre downtown di...", "og:url": "'
+ + 'http://www.laweekly.com/music/bjork-at-the-nokia-theatre-12-12-2408191", "article:published_time":'
+ + ' "2007-12-13T12:19:35-08:00", "article:modified_time": "2014-11-27T08:28:42-08:00", "article:section":'
+ + ' "Music", "og:image": "'
+ + 'http://IMAGES1.laweekly.com/imager/bjork-at-the-nokia-theatre-12-12/u/original/2470701/bjorktn003.jpg",'
+ + ' "og:image:height": "334", "og:image:width": "480", "og:site_name": "LA Weekly", "twitter:title":'
+ + ' "Bjork at the Nokia Theatre, 12/12", "twitter:description": "Bjork at the Nokia Theater, December 12'
+ + ' By Randall Roberts Last night’s Bjork show at the Dystopia – er, I mean Nokia -- Theatre'
+ + ' downtown di...", "twitter:card": "summary", "twitter:image": "'
+ + 'http://IMAGES1.laweekly.com/imager/bjork-at-the-nokia-theatre-12-12/u/original/2470701/bjorktn003.jpg",'
+ + ' "twitter:site": "@laweekly" }, { "url": "'
+ + 'http://www.laweekly.com/music/the-rapture-at-the-mayan-7-25-2401011", "og:type": "article", "og:title":'
+ + ' "The Rapture at the Mayan, 7/25", "og:description": "If you haven’t yet experienced the'
+ + ' phenomenon of people walk-dancing, apparently the best place to witness this is at a Rapture show.'
+ + ' Here’s...", "og:url": "http://www.laweekly.com/music/the-rapture-at-the-mayan-7-25-2401011",'
+ + ' "article:published_time": "2007-07-26T12:42:30-07:00", "article:modified_time":'
+ + ' "2014-11-27T08:00:51-08:00", "article:section": "Music", "og:image": "'
+ + 'http://IMAGES1.laweekly.com/imager/the-rapture-at-the-mayan-7-25/u/original/2463272/rapturetn05.jpg",'
+ + ' "og:image:height": "321", "og:image:width": "480", "og:site_name": "LA Weekly", "twitter:title": "The'
+ + ' Rapture at the Mayan, 7/25", "twitter:description": "If you haven’t yet experienced the'
+ + ' phenomenon of people walk-dancing, apparently the best place to witness this is at a Rapture show.'
+ + ' Here’s...", "twitter:card": "summary", "twitter:image": "'
+ + 'http://IMAGES1.laweekly.com/imager/the-rapture-at-the-mayan-7-25/u/original/2463272/rapturetn05.jpg",'
+ + ' "twitter:site": "@laweekly" } machine.os:win 7 machine.ram:7,516,192,768 _id:AU_x3_g4GFA8no6QjkYX'
+ + ' _type:apache _index:logstash-2015.09.22 _score: relatedContent.article:modified_time:November 27th'
+ + ' 2014, 16:00:51.000, November 27th 2014, 16:28:42.000 relatedContent.article:published_time:July 26th'
+ + ' 2007, 19:42:30.000, December 13th 2007, 20:19:35.000';
+ return discoverPage.getDocTableIndex(1)
+ .then(function (rowData) {
+ expect(rowData).to.be(ExpectedDoc);
+ })
+ .catch(common.handleError(this));
+ });
+
+ bdd.it('doc view should sort ascending', function () {
+ // Note: Could just check the timestamp, but might as well check that the whole doc is as expected.
+ var ExpectedDoc =
+ 'September 20th 2015, 00:00:00.000 index:logstash-2015.09.20 @timestamp:September 20th 2015, 00:00:00.000'
+ + ' ip:143.84.142.7 extension:jpg response:200 geo.coordinates:{ "lat": 38.68407028, "lon": -120.9871642 }'
+ + ' geo.src:ES geo.dest:US geo.srcdest:ES:US @tags:error, info utc_time:September 20th 2015, 00:00:00.000'
+ + ' referer:http://www.slate.com/success/vladimir-kovalyonok agent:Mozilla/4.0 (compatible; MSIE 6.0;'
+ + ' Windows NT 5.1; SV1; .NET CLR 1.1.4322) clientip:143.84.142.7 bytes:1,623'
+ + ' host:media-for-the-masses.theacademyofperformingartsandscience.org request:/uploads/steven-hawley.jpg'
+ + ' url:https://media-for-the-masses.theacademyofperformingartsandscience.org/uploads/steven-hawley.jpg'
+ + ' @message:143.84.142.7 - - [2015-09-20T00:00:00.000Z] "GET /uploads/steven-hawley.jpg HTTP/1.1" 200'
+ + ' 1623 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)" spaces:this is a'
+ + ' thing with lots of spaces wwwwoooooo xss:'
+ + ' headings:kimiya-yui, http://www.slate.com/success/koichi-wakata'
+ + ' links:thomas-marshburn@twitter.com, http://www.slate.com/info/michael-p-anderson, www.twitter.com'
+ + ' relatedContent:{ "url":'
+ + ' "http://www.laweekly.com/music/jay-electronica-much-better-than-his-name-would-suggest-2412364",'
+ + ' "og:type": "article", "og:title": "Jay Electronica: Much Better Than His Name Would Suggest",'
+ + ' "og:description": "You may not know who Jay Electronica is yet, but I'm willing to bet that you'
+ + ' would had he chosen a better name. Jay Electronica does not sound like the ...", "og:url":'
+ + ' "http://www.laweekly.com/music/jay-electronica-much-better-than-his-name-would-suggest-2412364",'
+ + ' "article:published_time": "2008-04-04T16:00:00-07:00", "article:modified_time":'
+ + ' "2014-11-27T08:01:03-08:00", "article:section": "Music", "og:site_name": "LA Weekly", "twitter:title":'
+ + ' "Jay Electronica: Much Better Than His Name Would Suggest", "twitter:description": "You may not know'
+ + ' who Jay Electronica is yet, but I'm willing to bet that you would had he chosen a better name.'
+ + ' Jay Electronica does not sound like the ...", "twitter:card": "summary", "twitter:site": "@laweekly"'
+ + ' }, { "url": "http://www.laweekly.com/news/mandoe-on-gower-near-fountain-2368123", "og:type":'
+ + ' "article", "og:title": "MANDOE On Gower Near Fountain", "og:description": "MANDOE has a stunner on a'
+ + ' wall north of an east-west street crossing Gower around Fountain (but not on Fountain). MADNOE, PROSE'
+ + ' and FUKM are listed on t...", "og:url": "'
+ + 'http://www.laweekly.com/news/mandoe-on-gower-near-fountain-2368123", "article:published_time":'
+ + ' "2008-04-25T07:26:41-07:00", "article:modified_time": "2014-10-28T15:00:08-07:00", "article:section":'
+ + ' "News", "og:image": "'
+ + 'http://images1.laweekly.com/imager/mandoe-on-gower-near-fountain/u/original/2430891/img_6648.jpg",'
+ + ' "og:image:height": "640", "og:image:width": "480", "og:site_name": "LA Weekly", "twitter:title": '
+ + '"MANDOE On Gower Near Fountain", "twitter:description": "MANDOE has a stunner on a wall north of an'
+ + ' east-west street crossing Gower around Fountain (but not on Fountain). MADNOE, PROSE and FUKM are'
+ + ' listed on t...", "twitter:card": "summary", "twitter:image": "'
+ + 'http://images1.laweekly.com/imager/mandoe-on-gower-near-fountain/u/original/2430891/img_6648.jpg", '
+ + '"twitter:site": "@laweekly" }, { "url": "http://www.laweekly.com/arts/meghan-finds-the-love-2373346",'
+ + ' "og:type": "article", "og:title": "Meghan Finds The Love", "og:description": "LA Weekly is the'
+ + ' definitive source of information for news, music, movies, restaurants, reviews, and events in Los'
+ + ' Angeles.", "og:url": "http://www.laweekly.com/arts/meghan-finds-the-love-2373346",'
+ + ' "article:published_time": "2005-10-20T18:10:25-07:00", "article:modified_time":'
+ + ' "2014-11-25T19:52:35-08:00", "article:section": "Arts", "og:site_name": "LA Weekly", "twitter:title":'
+ + ' "Meghan Finds The Love", "twitter:description": "LA Weekly is the definitive source of information for'
+ + ' news, music, movies, restaurants, reviews, and events in Los Angeles.", "twitter:card": "summary",'
+ + ' "twitter:site": "@laweekly" }, { "url": "http://www.laweekly.com/arts/these-clowns-are-with-me-2371051'
+ + '", "og:type": "article", "og:title": "These Clowns Are With Me", "og:description": " I'
+ + ' didn't mean to blow off all my responsibilities yesterday, but when a schmoozy Hollywood luncheon'
+ + ' turns into a full-on party by 3pm, and...", "og:url": "'
+ + 'http://www.laweekly.com/arts/these-clowns-are-with-me-2371051", "article:published_time": '
+ + '"2006-03-04T17:03:42-08:00", "article:modified_time": "2014-11-25T17:05:47-08:00", "article:section":'
+ + ' "Arts", "og:image": "'
+ + 'http://images1.laweekly.com/imager/these-clowns-are-with-me/u/original/2434556/e4b8scd.jpg",'
+ + ' "og:image:height": "375", "og:image:width": "500", "og:site_name": "LA Weekly", "twitter:title":'
+ + ' "These Clowns Are With Me", "twitter:description": " I didn't mean to blow off all'
+ + ' my responsibilities yesterday, but when a schmoozy Hollywood luncheon turns into a full-on party by'
+ + ' 3pm, and...", "twitter:card": "summary", "twitter:image": "'
+ + 'http://images1.laweekly.com/imager/these-clowns-are-with-me/u/original/2434556/e4b8scd.jpg",'
+ + ' "twitter:site": "@laweekly" }, { "url": "http://www.laweekly.com/arts/shopping-daze-2373807",'
+ + ' "og:type": "article", "og:title": "Shopping Daze", "og:description": "LA Weekly is the definitive '
+ + 'source of information for news, music, movies, restaurants, reviews, and events in Los Angeles.",'
+ + ' "og:url": "http://www.laweekly.com/arts/shopping-daze-2373807", "article:published_time":'
+ + ' "2006-12-13T12:12:04-08:00", "article:modified_time": "2014-11-25T20:15:21-08:00", "article:section":'
+ + ' "Arts", "og:site_name": "LA Weekly", "twitter:title": "Shopping Daze", "twitter:description": "LA'
+ + ' Weekly is the definitive source of information for news, music, movies, restaurants, reviews, and'
+ + ' events in Los Angeles.", "twitter:card": "summary", "twitter:site": "@laweekly" } machine.os:osx'
+ + ' machine.ram:15,032,385,536 _id:AU_x3_g3GFA8no6QjkFm _type:apache _index:logstash-2015.09.20 _score:'
+ + ' relatedContent.article:modified_time:October 28th 2014, 22:00:08.000, November 26th 2014,'
+ + ' 01:05:47.000, November 26th 2014, 03:52:35.000, November 26th 2014, 04:15:21.000, November 27th 2014,'
+ + ' 16:01:03.000 relatedContent.article:published_time:October 21st 2005, 01:10:25.000, March 5th 2006,'
+ + ' 01:03:42.000, December 13th 2006, 20:12:04.000, April 4th 2008, 23:00:00.000, April 25th 2008,'
+ + ' 14:26:41.000';
+ return discoverPage.clickDocSortDown()
+ .then(function () {
+ // we don't technically need this sleep here because the tryForTime will retry and the
+ // results will match on the 2nd or 3rd attempt, but that debug output is huge in this
+ // case and it can be avoided with just a few seconds sleep.
+ return common.sleep(2000);
+ })
+ .then(function () {
+ return common.tryForTime(20 * 1000, function tryingForTime() {
+ return discoverPage.getDocTableIndex(1)
+ .then(function (rowData) {
+ expect(rowData).to.be(ExpectedDoc);
+ });
+ });
+ })
+ .catch(common.handleError(this));
+ });
+
+
+ bdd.it('a bad syntax query should show an error message', function () {
+ var expectedHitCount = '1011,156';
+ var expectedError = 'Discover: Failed to parse query [xxx(yyy]';
+ return discoverPage.query('xxx(yyy')
+ .then(function () {
+ return headerPage.getToastMessage();
+ })
+ .then(function (toastMessage) {
+ expect(toastMessage).to.be(expectedError);
+ })
+ .then(function () {
+ return headerPage.clickToastOK();
+ })
+ .catch(common.handleError(this));
+ });
+
+
+ });
+ });
+ };
+});
diff --git a/test/functional/apps/discover/index.js b/test/functional/apps/discover/index.js
index c2677dd248a3..eef3af445e9a 100644
--- a/test/functional/apps/discover/index.js
+++ b/test/functional/apps/discover/index.js
@@ -4,6 +4,7 @@ define(function (require) {
var url = require('intern/dojo/node!url');
var ScenarioManager = require('intern/dojo/node!../../../fixtures/scenarioManager');
var discoverTest = require('./_discover');
+ var fieldData = require('./_field_data');
bdd.describe('discover app', function () {
var scenarioManager;
@@ -22,5 +23,7 @@ define(function (require) {
discoverTest(bdd, scenarioManager);
+ fieldData(bdd, scenarioManager);
+
});
});
diff --git a/test/serverConfig.js b/test/serverConfig.js
index e88daa58e978..ea3438e728bb 100644
--- a/test/serverConfig.js
+++ b/test/serverConfig.js
@@ -1,3 +1,5 @@
+var shield = require('./shield');
+
var kibanaURL = '/app/kibana';
module.exports = {
@@ -11,13 +13,13 @@ module.exports = {
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: 'user:notsecure'
+ auth: shield.kibanaUser.username + ':' + 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: 'admin:notsecure'
+ auth: shield.admin.username + ':' + shield.admin.password
}
},
apps: {
diff --git a/test/shield.js b/test/shield.js
new file mode 100644
index 000000000000..826945be5a22
--- /dev/null
+++ b/test/shield.js
@@ -0,0 +1,16 @@
+const env = process.env;
+
+exports.kibanaUser = {
+ username: env.SHIELD_KIBANA_USER || 'user',
+ password: env.SHIELD_KIBANA_USER_PASS || 'notsecure'
+};
+
+exports.kibanaServer = {
+ username: env.SHIELD_KIBANA_SERVER || 'kibana',
+ password: env.SHIELD_KIBANA_SERVER_PASS || 'notsecure'
+};
+
+exports.admin = {
+ username: env.SHIELD_ADMIN || 'admin',
+ password: env.SHIELD_ADMIN_PASS || 'notsecure'
+};
diff --git a/test/support/pages/DiscoverPage.js b/test/support/pages/DiscoverPage.js
index eb611669fe75..cbaa0073f43a 100644
--- a/test/support/pages/DiscoverPage.js
+++ b/test/support/pages/DiscoverPage.js
@@ -32,6 +32,12 @@ define(function (require) {
.getVisibleText();
},
+ getChartTimespan: function getChartTimespan() {
+ return thisTime
+ .findByCssSelector('center.small > span:nth-child(1)')
+ .getVisibleText();
+ },
+
saveSearch: function saveSearch(searchName) {
var self = this;
return self.clickSaveSearchButton()
@@ -85,7 +91,6 @@ define(function (require) {
},
getBarChartData: function getBarChartData() {
- common.debug('in getBarChartData');
return thisTime
.findAllByCssSelector('rect[data-label="Count"]')
.then(function (chartData) {
@@ -101,6 +106,75 @@ define(function (require) {
.then(function (bars) {
return bars;
});
+ },
+
+ getChartInterval: function getChartInterval() {
+ return thisTime
+ .findByCssSelector('span.results-interval:nth-child(2) > a:nth-child(1)')
+ .getVisibleText();
+ },
+
+ setChartInterval: function setChartInterval(interval) {
+ return thisTime
+ .findByCssSelector('span.results-interval:nth-child(2) > a:nth-child(1)')
+ .click()
+ .catch(function () {
+ // in some cases we have the link above, but after we've made a
+ // selection we just have a select list.
+ })
+ .then(function () {
+ return thisTime
+ .findByCssSelector('option[label="' + interval + '"]')
+ .click();
+ });
+ },
+
+ getHitCount: function getHitCount() {
+ return thisTime
+ .findByCssSelector('strong.discover-info-hits')
+ .getVisibleText();
+ },
+
+ query: function query(queryString) {
+ return thisTime
+ .findByCssSelector('input[aria-label="Search input"]')
+ .clearValue()
+ .type(queryString)
+ .then(function () {
+ return thisTime
+ .findByCssSelector('button[aria-label="Search"]')
+ .click();
+ });
+ },
+
+ getDocHeader: function getDocHeader() {
+ return thisTime
+ .findByCssSelector('thead.ng-isolate-scope > tr:nth-child(1)')
+ .getVisibleText();
+ },
+
+ getDocTableIndex: function getDocTableIndex(index) {
+ return thisTime
+ .findByCssSelector('tr.discover-table-row:nth-child(' + (index) + ')')
+ .getVisibleText();
+ },
+
+ clickDocSortDown: function clickDocSortDown() {
+ return thisTime
+ .findByCssSelector('.fa-sort-down')
+ .click();
+ },
+
+ clickDocSortUp: function clickDocSortUp() {
+ return thisTime
+ .findByCssSelector('.fa-sort-up')
+ .click();
+ },
+
+ getMarks: function getMarks() {
+ return thisTime
+ .findAllByCssSelector('mark')
+ .getVisibleText();
}
};
diff --git a/test/support/pages/HeaderPage.js b/test/support/pages/HeaderPage.js
index c1e8767baf56..5a78ebb7736e 100644
--- a/test/support/pages/HeaderPage.js
+++ b/test/support/pages/HeaderPage.js
@@ -137,6 +137,13 @@ define(function (require) {
});
},
+ clickToastOK: function clickToastOK() {
+ return this.remote
+ .setFindTimeout(defaultTimeout)
+ .findByCssSelector('button[ng-if="notif.accept"]')
+ .click();
+ },
+
getSpinnerDone: function getSpinnerDone() {
var self = this;
return this.remote
diff --git a/test/utils/kbn_server.js b/test/utils/kbn_server.js
index d97d586e7a79..35a0bc3aa1c5 100644
--- a/test/utils/kbn_server.js
+++ b/test/utils/kbn_server.js
@@ -1,6 +1,7 @@
import { defaultsDeep, set } from 'lodash';
import requirefrom from 'requirefrom';
import { header as basicAuthHeader } from './base_auth';
+import { kibanaUser, kibanaServer } from '../shield';
const src = requirefrom('src');
const KbnServer = src('server/KbnServer');
@@ -26,8 +27,8 @@ const SERVER_DEFAULTS = {
},
elasticsearch: {
url: 'http://localhost:9210',
- username: 'kibana',
- password: 'notsecure'
+ username: kibanaServer.username,
+ password: kibanaServer.password
}
};
@@ -46,7 +47,8 @@ export function createServer(params = {}) {
* Creates request configuration with a basic auth header
*/
export function authOptions() {
- const authHeader = basicAuthHeader('user', 'notsecure');
+ const { username, password } = kibanaUser;
+ const authHeader = basicAuthHeader(username, password);
return set({}, 'headers.Authorization', authHeader);
};