mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
Merge branch 'master' into vis_refactor_docs_and_tests
This commit is contained in:
commit
ea60619149
16 changed files with 359 additions and 251 deletions
|
@ -5,7 +5,6 @@ define(function (require) {
|
|||
var moment = require('moment');
|
||||
|
||||
var _ = require('lodash');
|
||||
var nextTick = require('utils/next_tick');
|
||||
var $ = require('jquery');
|
||||
|
||||
require('directives/truncated');
|
||||
|
@ -60,6 +59,8 @@ define(function (require) {
|
|||
};
|
||||
});
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* kbnTable directive
|
||||
*
|
||||
|
@ -69,18 +70,7 @@ define(function (require) {
|
|||
* <kbn-table columns="columnsToDisplay" rows="rowsToDisplay"></kbn-table>
|
||||
* ```
|
||||
*/
|
||||
module.directive('kbnTable', function ($compile, config) {
|
||||
// base class for all dom nodes
|
||||
var DOMNode = window.Node;
|
||||
|
||||
function scheduleNextRenderTick(cb) {
|
||||
if (typeof window.requestAnimationFrame === 'function') {
|
||||
window.requestAnimationFrame(cb);
|
||||
} else {
|
||||
nextTick(cb);
|
||||
}
|
||||
}
|
||||
|
||||
module.directive('kbnTable', function (config) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: html,
|
||||
|
@ -91,152 +81,64 @@ define(function (require) {
|
|||
sorting: '=',
|
||||
filtering: '=',
|
||||
refresh: '=',
|
||||
maxLength: '=?',
|
||||
maxLength: '=',
|
||||
mapping: '=',
|
||||
timefield: '=?'
|
||||
},
|
||||
link: function ($scope, element) {
|
||||
$scope.limit = 50;
|
||||
$scope.addRows = function () {
|
||||
if ($scope.limit < config.get('discover:sampleSize')) {
|
||||
$scope.limit = $scope.limit + 50;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* kbnTableRow directive
|
||||
*
|
||||
* Display a row in the table
|
||||
* ```
|
||||
* <tr ng-repeat="row in rows" kbn-table-row="row"></tr>
|
||||
* ```
|
||||
*/
|
||||
module.directive('kbnTableRow', function ($compile, config) {
|
||||
// base class for all dom nodes
|
||||
var DOMNode = window.Node;
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
fields: '=',
|
||||
columns: '=',
|
||||
filtering: '=',
|
||||
mapping: '=',
|
||||
maxLength: '=',
|
||||
timefield: '=?',
|
||||
row: '=kbnTableRow'
|
||||
},
|
||||
link: function ($scope, element, attrs) {
|
||||
element.after('<tr>');
|
||||
|
||||
var init = function () {
|
||||
createSummaryRow($scope.row, $scope.row._id);
|
||||
};
|
||||
|
||||
// track a list of id's that are currently open, so that
|
||||
// render can easily render in the same current state
|
||||
var opened = [];
|
||||
|
||||
// whenever we compile, we should create a child scope that we can then detroy
|
||||
var childScopes = {};
|
||||
var childScopeFor = function (id) {
|
||||
if (childScopes[id]) return childScopes[id];
|
||||
|
||||
var $child = $scope.$new();
|
||||
childScopes[id] = $child;
|
||||
return $child;
|
||||
};
|
||||
var clearChildScopeFor = function (id) {
|
||||
if (childScopes[id]) {
|
||||
childScopes[id].$destroy();
|
||||
delete childScopes[id];
|
||||
}
|
||||
};
|
||||
|
||||
// the current position in the list of rows
|
||||
var cursor = 0;
|
||||
|
||||
// the page size to load rows (out of the rows array, load 50 at a time)
|
||||
var pageSize = 50;
|
||||
|
||||
// rendering an entire page while the page is scrolling can cause a good
|
||||
// bit of jank, lets only render a certain amount per "tick"
|
||||
var rowsPerTick;
|
||||
var $child;
|
||||
|
||||
// set the maxLength for summaries
|
||||
if ($scope.maxLength === void 0) {
|
||||
$scope.maxLength = 250;
|
||||
}
|
||||
|
||||
// rerender when either is changed
|
||||
$scope.$watch('rows', render);
|
||||
$scope.$watchCollection('columns', render);
|
||||
$scope.$watch('maxLength', render);
|
||||
|
||||
// the body of the table
|
||||
var $body = element.find('tbody');
|
||||
|
||||
// itterate the columns and rows, rebuild the table's html
|
||||
function render() {
|
||||
// Close all rows
|
||||
opened = [];
|
||||
|
||||
// Clear the body
|
||||
$body.empty();
|
||||
|
||||
// destroy all child scopes
|
||||
Object.keys(childScopes).forEach(clearChildScopeFor);
|
||||
|
||||
if (!$scope.rows || $scope.rows.length === 0) return;
|
||||
if (!$scope.columns || $scope.columns.length === 0) return;
|
||||
cursor = 0;
|
||||
addRows();
|
||||
$scope.addRows = addRows;
|
||||
}
|
||||
|
||||
var renderRows = (function () {
|
||||
// basic buffer that will be pulled from when we are adding rows.
|
||||
var queue = [];
|
||||
var rendering = false;
|
||||
|
||||
return function renderRows(rows) {
|
||||
// overwrite the queue, don't keep old rows
|
||||
queue = rows.slice(0);
|
||||
if (!rendering) {
|
||||
onTick();
|
||||
}
|
||||
};
|
||||
|
||||
function forEachRow(row, i, currentChunk) {
|
||||
var id = rowId(row);
|
||||
var $summary = createSummaryRow(row, id);
|
||||
var $details = $('<tr></tr>');
|
||||
// cursor is the end of current selection, so
|
||||
// subtract the remaining queue size, then the
|
||||
// size of this chunk, and add the current i
|
||||
var currentPosition = cursor - queue.length - currentChunk.length + i;
|
||||
if (currentPosition % 2) {
|
||||
$summary.addClass('even');
|
||||
//$details.addClass('even');
|
||||
}
|
||||
|
||||
$body.append([
|
||||
$summary,
|
||||
$details
|
||||
]);
|
||||
}
|
||||
|
||||
function onTick() {
|
||||
// ensure that the rendering flag is set
|
||||
rendering = true;
|
||||
var performance = window.performance;
|
||||
var timing;
|
||||
|
||||
if (
|
||||
rowsPerTick === void 0
|
||||
&& window.performance
|
||||
&& typeof window.performance.now === 'function'
|
||||
) {
|
||||
timing = performance.now();
|
||||
rowsPerTick = 30;
|
||||
}
|
||||
|
||||
queue
|
||||
// grab the first n from the buffer
|
||||
.splice(0, rowsPerTick || queue.length)
|
||||
// render each row
|
||||
.forEach(forEachRow);
|
||||
|
||||
if (timing) {
|
||||
// we know we have performance.now, because timing was set
|
||||
var time = performance.now() - timing;
|
||||
var rowsRendered = rowsPerTick;
|
||||
var msPerRow = time / rowsPerTick;
|
||||
// aim to fit the rendering into 5 milliseconds
|
||||
rowsPerTick = Math.ceil(15 / msPerRow);
|
||||
console.log('completed render of %d rows in %d milliseconds. rowsPerTick set to %d', rowsRendered, time, rowsPerTick);
|
||||
}
|
||||
|
||||
if (queue.length) {
|
||||
// the queue is not empty, draw again next tick
|
||||
scheduleNextRenderTick(onTick);
|
||||
} else {
|
||||
// unset the rendering flag
|
||||
rendering = false;
|
||||
}
|
||||
}
|
||||
}());
|
||||
|
||||
function addRows() {
|
||||
if (cursor > $scope.rows.length) {
|
||||
$scope.addRows = null;
|
||||
}
|
||||
|
||||
renderRows($scope.rows.slice(cursor, cursor += pageSize));
|
||||
}
|
||||
|
||||
// for now, rows are "tracked" by their index, but this could eventually
|
||||
// be configured so that changing the order of the rows won't prevent
|
||||
|
@ -252,14 +154,10 @@ define(function (require) {
|
|||
}
|
||||
|
||||
// toggle display of the rows details, a full list of the fields from each row
|
||||
$scope.toggleRow = function (id, event) {
|
||||
var row = rowForId(id);
|
||||
$scope.toggleRow = function (row, event) {
|
||||
var id = row._id;
|
||||
|
||||
if (~opened.indexOf(id)) {
|
||||
_.pull(opened, id);
|
||||
} else {
|
||||
opened.push(id);
|
||||
}
|
||||
$scope.open = !$scope.open;
|
||||
|
||||
var $tr = $(event.delegateTarget.parentElement);
|
||||
var $detailsTr = $tr.next();
|
||||
|
@ -268,22 +166,22 @@ define(function (require) {
|
|||
// add/remove $details children
|
||||
///
|
||||
|
||||
var open = !!~opened.indexOf(id);
|
||||
$detailsTr.toggle(open);
|
||||
$detailsTr.toggle($scope.open);
|
||||
|
||||
// Change the caret icon
|
||||
var $toggleIcon = $($(event.delegateTarget).children('i')[0]);
|
||||
$toggleIcon.toggleClass('fa-caret-down');
|
||||
$toggleIcon.toggleClass('fa-caret-right');
|
||||
|
||||
if (!open) {
|
||||
if (!$scope.open) {
|
||||
// close the child scope if it exists
|
||||
clearChildScopeFor(id);
|
||||
$child.$destroy();
|
||||
// no need to go any further
|
||||
return;
|
||||
} else {
|
||||
$child = $scope.$new();
|
||||
}
|
||||
|
||||
|
||||
// The fields to loop over
|
||||
row._fields = row._fields || _.keys(row._source).concat(config.get('metaFields')).sort();
|
||||
row._mode = 'table';
|
||||
|
@ -301,7 +199,7 @@ define(function (require) {
|
|||
return _.contains(validTypes, mapping.type);
|
||||
};
|
||||
|
||||
var $childScope = _.assign(childScopeFor(id), { row: row, showFilters: showFilters });
|
||||
var $childScope = _.assign($child, { row: row, showFilters: showFilters });
|
||||
$compile($detailsTr)($childScope);
|
||||
};
|
||||
|
||||
|
@ -309,30 +207,32 @@ define(function (require) {
|
|||
$scope.filtering(field, row._source[field] || row[field], operation);
|
||||
};
|
||||
|
||||
$scope.$watch('columns', function () {
|
||||
element.empty();
|
||||
createSummaryRow($scope.row, $scope.row._id);
|
||||
});
|
||||
|
||||
// create a tr element that lists the value for each *column*
|
||||
function createSummaryRow(row, id) {
|
||||
var $tr = $('<tr>');
|
||||
|
||||
var expandTd = $('<td>').html('<i class="fa fa-caret-right"></span>')
|
||||
.attr('ng-click', 'toggleRow(' + JSON.stringify(id) + ', $event)');
|
||||
.attr('ng-click', 'toggleRow(row, $event)');
|
||||
$compile(expandTd)($scope);
|
||||
$tr.append(expandTd);
|
||||
element.append(expandTd);
|
||||
|
||||
var td = $(document.createElement('td'));
|
||||
if ($scope.timefield) {
|
||||
td.addClass('discover-table-timefield');
|
||||
td.attr('width', '1%');
|
||||
_displayField(td, row, $scope.timefield);
|
||||
$tr.append(td);
|
||||
element.append(td);
|
||||
}
|
||||
|
||||
_.each($scope.columns, function (column) {
|
||||
td = $(document.createElement('td'));
|
||||
_displayField(td, row, column);
|
||||
$tr.append(td);
|
||||
element.append(td);
|
||||
});
|
||||
|
||||
return $tr;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -373,7 +273,7 @@ define(function (require) {
|
|||
val = document.createElement('kbn-truncated');
|
||||
val.setAttribute('orig', complete);
|
||||
val.setAttribute('length', $scope.maxLength);
|
||||
val = $compile(val)(childScopeFor(rowId(row)))[0];// return the actual element
|
||||
val = $compile(val)($scope)[0];// return the actual element
|
||||
} else {
|
||||
val = val.substring(0, $scope.maxLength) + '...';
|
||||
}
|
||||
|
@ -381,6 +281,8 @@ define(function (require) {
|
|||
|
||||
return val;
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
{{ opts.savedSearch.title }}
|
||||
<i tooltip="Reload saved query" ng-click="resetQuery();" class="fa fa-undo smallest small"></i>
|
||||
</h3>
|
||||
<div class="discover-timechart" ng-if="vis">
|
||||
<div class="discover-timechart" >
|
||||
<center class="small">
|
||||
<span tooltip="To change the time, click the clock icon in the navigation bar">{{timeRange.from | moment}} - {{timeRange.to | moment}}</span>
|
||||
<!-- TODO: Currently no way to apply this setting to the visualization -->
|
||||
|
@ -90,7 +90,7 @@
|
|||
<div class="spinner large"> </div>
|
||||
</div>
|
||||
|
||||
<visualize vis="vis" es-resp="mergedEsResp" search-source="searchSource"></visualize>
|
||||
<visualize ng-if="vis && rows.length != 0" vis="vis" es-resp="mergedEsResp" search-source="searchSource"></visualize>
|
||||
</div>
|
||||
|
||||
<div class="discover-table"
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
<table class="table">
|
||||
<thead kbn-table-header columns="columns" mapping="mapping" sorting="sorting" timefield="timefield"></thead>
|
||||
<tbody></tbody>
|
||||
<tbody>
|
||||
<tr ng-repeat="row in rows |limitTo:limit track by row._index+row._id"
|
||||
kbn-table-row="row"
|
||||
columns="columns" mapping="mapping" sorting="sorting" timefield="timefield" max-length="maxLength" filtering="filtering"></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<kbn-infinite-scroll more="addRows"></kbn-infinite-scroll>
|
|
@ -112,7 +112,7 @@
|
|||
}
|
||||
|
||||
.sidebar-item:hover:hover button.discover-field-toggle {
|
||||
display: inherit;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.discover-field-details-close {
|
||||
|
@ -199,6 +199,10 @@ disc-field-chooser {
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-item-title {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.results {
|
||||
|
|
|
@ -4,6 +4,7 @@ define(function (require) {
|
|||
var AggType = Private(require('components/agg_types/_agg_type'));
|
||||
|
||||
function getTickLabel(query) {
|
||||
|
||||
if (query.query_string && query.query_string.query) {
|
||||
return query.query_string.query;
|
||||
}
|
||||
|
@ -18,14 +19,12 @@ define(function (require) {
|
|||
{
|
||||
name: 'filters',
|
||||
editor: require('text!components/agg_types/controls/filters.html'),
|
||||
default: [ {} ],
|
||||
default: [ {input: {}} ],
|
||||
write: function (aggConfig, output) {
|
||||
output.aggParams = {
|
||||
output.params = {
|
||||
filters: _.transform(aggConfig.params.filters, function (filters, filter, iterator) {
|
||||
// We need to check here
|
||||
filters[getTickLabel(filter.input)] = {
|
||||
query: filter.input || { query_string: {query: '*'} }
|
||||
};
|
||||
filters[getTickLabel(filter.input.query)] = filter.input || {query: {query_string: {query: '*'}}};
|
||||
}, {})
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<div class="form-group">
|
||||
<div ng-repeat="filter in params.filters">
|
||||
<label>Query string {{$index + 1}}</label>
|
||||
<label>Query {{$index + 1}}</label>
|
||||
<div class="form-group vis-editor-agg-form-row">
|
||||
<input query-input
|
||||
ng-model="filter.query"
|
||||
ng-model="filter.input.query"
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="filter{{$index}}">
|
||||
|
@ -18,7 +18,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
ng-click="params.filters.push({})"
|
||||
click-focus="'filter'+(params.filters.length-1)"
|
||||
ng-click="params.filters.push({input:{}})"
|
||||
class="sidebar-item-button primary">
|
||||
Add filter
|
||||
</div>
|
||||
|
|
|
@ -64,9 +64,9 @@ define(function (require) {
|
|||
'date'
|
||||
],
|
||||
name: 'date',
|
||||
convert: function (val) {
|
||||
convert: _.memoize(function (val) {
|
||||
return moment(val).format(config.get('dateFormat'));
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
types: [
|
||||
|
|
|
@ -6,7 +6,7 @@ define(function (require) {
|
|||
|
||||
var module = require('modules').get('kibana/global_state');
|
||||
|
||||
module.service('globalState', function (Private, $rootScope) {
|
||||
module.service('globalState', function (Private, $rootScope, $location) {
|
||||
var State = Private(require('components/state_management/state'));
|
||||
|
||||
_.inherits(GlobalState, State);
|
||||
|
@ -18,6 +18,14 @@ define(function (require) {
|
|||
return qs.replaceParamInUrl(url, this._urlParam, this.toRISON());
|
||||
};
|
||||
|
||||
GlobalState.prototype._readFromURL = function (method) {
|
||||
var search = $location.search();
|
||||
if (method === 'fetch') {
|
||||
return (search[this._urlParam]) ? rison.decode(search[this._urlParam]) : this.toObject();
|
||||
}
|
||||
return rison.decode(search[this._urlParam] || '()');
|
||||
};
|
||||
|
||||
return new GlobalState();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -20,17 +20,22 @@ define(function (require) {
|
|||
this.fetch();
|
||||
}
|
||||
|
||||
State.prototype._readFromURL = function (method) {
|
||||
var search = $location.search();
|
||||
return rison.decode(search[this._urlParam] || '()');
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches the state from the url
|
||||
* @returns {void}
|
||||
*/
|
||||
State.prototype.fetch = function () {
|
||||
var search = $location.search();
|
||||
var stash = rison.decode(search[this._urlParam] || '()');
|
||||
var stash = this._readFromURL('fetch');
|
||||
|
||||
_.defaults(stash, this._defaults);
|
||||
// apply diff to state from stash, this is side effecting so
|
||||
// it will change state in place.
|
||||
// apply diff to state from stash, will change state in place via side effect
|
||||
var diffResults = applyDiff(this, stash);
|
||||
|
||||
if (diffResults.keys.length) {
|
||||
this.emit('fetch_with_changes', diffResults.keys);
|
||||
}
|
||||
|
@ -41,16 +46,19 @@ define(function (require) {
|
|||
* @returns {void}
|
||||
*/
|
||||
State.prototype.save = function () {
|
||||
var search = $location.search();
|
||||
var stash = rison.decode(search[this._urlParam] || '()');
|
||||
var stash = this._readFromURL('save');
|
||||
var state = this.toObject();
|
||||
|
||||
_.defaults(state, this._defaults);
|
||||
// apply diff to stash from state, this is side effecting so
|
||||
// it will change stash in place.
|
||||
// apply diff to state from stash, will change state in place via side effect
|
||||
var diffResults = applyDiff(stash, state);
|
||||
|
||||
if (diffResults.keys.length) {
|
||||
this.emit('save_with_changes', diffResults.keys);
|
||||
}
|
||||
|
||||
// persist the state in the URL
|
||||
var search = $location.search();
|
||||
search[this._urlParam] = this.toRISON();
|
||||
$location.search(search);
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@ define(function (require) {
|
|||
.directive('confirmClick', function () {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {},
|
||||
link: function ($scope, $elem, attrs) {
|
||||
$elem.bind('click', function () {
|
||||
var message = attrs.confirmation || 'Are you sure?';
|
||||
|
|
|
@ -15,10 +15,6 @@ define(function (require) {
|
|||
if (!$route.current.$$route.originalPath.match(/settings\/indices/)) {
|
||||
return indexPatterns.getIds()
|
||||
.then(function (patterns) {
|
||||
if (!patterns || patterns.length === 0) {
|
||||
throw new errors.NoDefinedIndexPatterns();
|
||||
}
|
||||
|
||||
if (!config.get('defaultIndex')) {
|
||||
throw new NoDefaultIndexPattern();
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@
|
|||
'kibana',
|
||||
'sinon/sinon',
|
||||
'specs/apps/discover/hit_sort_fn',
|
||||
'specs/directives/confirm-click',
|
||||
'specs/directives/timepicker',
|
||||
'specs/directives/truncate',
|
||||
'specs/directives/typeahead',
|
||||
|
@ -80,6 +81,7 @@
|
|||
'specs/courier/search_source/_get_normalized_sort',
|
||||
'specs/factories/base_object',
|
||||
'specs/state_management/state',
|
||||
'specs/state_management/global_state',
|
||||
'specs/utils/diff_object',
|
||||
'specs/utils/diff_time_picker_vals',
|
||||
'specs/factories/events',
|
||||
|
|
126
test/unit/specs/directives/confirm-click.js
Normal file
126
test/unit/specs/directives/confirm-click.js
Normal file
|
@ -0,0 +1,126 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
var $ = require('jquery');
|
||||
var sinon = require('sinon/sinon');
|
||||
|
||||
require('directives/confirm_click');
|
||||
|
||||
// Load the kibana app dependencies.
|
||||
require('angular-route');
|
||||
|
||||
// Load kibana and its applications
|
||||
require('index');
|
||||
|
||||
require('apps/discover/index');
|
||||
|
||||
var $parentScope, $scope, $elem;
|
||||
|
||||
var init = function (text) {
|
||||
// Load the application
|
||||
module('kibana');
|
||||
|
||||
// Create the scope
|
||||
inject(function ($rootScope, $compile) {
|
||||
|
||||
// Give us a scope
|
||||
$parentScope = $rootScope;
|
||||
|
||||
// Create the element
|
||||
$elem = angular.element(
|
||||
'<a confirm-click="runThis()">runThis</a>'
|
||||
);
|
||||
|
||||
// And compile it
|
||||
$compile($elem)($parentScope);
|
||||
|
||||
// Fire a digest cycle
|
||||
$elem.scope().$digest();
|
||||
|
||||
// Grab the isolate scope so we can test it
|
||||
$scope = $elem.isolateScope();
|
||||
|
||||
// Add a function to check the run status of.
|
||||
$scope.runThis = sinon.spy();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
describe('confirmClick directive', function () {
|
||||
|
||||
|
||||
describe('event handlers', function () {
|
||||
var events;
|
||||
|
||||
beforeEach(function () {
|
||||
init();
|
||||
events = $._data($elem[0], 'events');
|
||||
});
|
||||
|
||||
it('should get a click handler', function (done) {
|
||||
expect(events).to.be.a(Object);
|
||||
expect(events.click).to.be.a(Array);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should unbind click handlers when the scope is destroyed', function (done) {
|
||||
$scope.$destroy();
|
||||
expect(events.click).to.be(undefined);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
describe('confirmed', function () {
|
||||
var confirmed;
|
||||
|
||||
beforeEach(function () {
|
||||
init();
|
||||
confirmed = sinon.stub(window, 'confirm');
|
||||
confirmed.returns(true);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
window.confirm.restore();
|
||||
});
|
||||
|
||||
it('should trigger window.confirm when clicked', function (done) {
|
||||
$elem.click();
|
||||
expect(confirmed.called).to.be(true);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should run the click function when positively confirmed', function (done) {
|
||||
$elem.click();
|
||||
expect($scope.runThis.called).to.be(true);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('not confirmed', function () {
|
||||
var confirmed;
|
||||
|
||||
beforeEach(function () {
|
||||
init();
|
||||
confirmed = sinon.stub(window, 'confirm');
|
||||
confirmed.returns(false);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
window.confirm.restore();
|
||||
});
|
||||
|
||||
it('should not run the click function when canceled', function (done) {
|
||||
$elem.click();
|
||||
expect($scope.runThis.called).to.be(false);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -7,42 +7,40 @@ define(function (require) {
|
|||
// Load kibana
|
||||
require('index');
|
||||
|
||||
describe('State Management', function () {
|
||||
describe('BaseObject', function () {
|
||||
var $rootScope;
|
||||
var BaseObject;
|
||||
describe('Base Object', function () {
|
||||
var $rootScope;
|
||||
var BaseObject;
|
||||
|
||||
beforeEach(function () {
|
||||
module('kibana');
|
||||
beforeEach(function () {
|
||||
module('kibana');
|
||||
|
||||
inject(function (_$rootScope_, Private) {
|
||||
$rootScope = _$rootScope_;
|
||||
BaseObject = Private(require('factories/base_object'));
|
||||
});
|
||||
inject(function (_$rootScope_, Private) {
|
||||
$rootScope = _$rootScope_;
|
||||
BaseObject = Private(require('factories/base_object'));
|
||||
});
|
||||
|
||||
it('should take an inital set of values', function () {
|
||||
var baseObject = new BaseObject({ message: 'test' });
|
||||
expect(baseObject).to.have.property('message', 'test');
|
||||
});
|
||||
|
||||
it('should serialize _attributes to RISON', function () {
|
||||
var baseObject = new BaseObject();
|
||||
baseObject.message = 'Testing... 1234';
|
||||
var rison = baseObject.toRISON();
|
||||
expect(rison).to.equal('(message:\'Testing... 1234\')');
|
||||
});
|
||||
|
||||
it('should serialize _attributes for JSON', function () {
|
||||
var baseObject = new BaseObject();
|
||||
baseObject.message = 'Testing... 1234';
|
||||
baseObject._private = 'foo';
|
||||
baseObject.$private = 'stuff';
|
||||
var json = JSON.stringify(baseObject);
|
||||
expect(json).to.equal('{"message":"Testing... 1234"}');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should take an inital set of values', function () {
|
||||
var baseObject = new BaseObject({ message: 'test' });
|
||||
expect(baseObject).to.have.property('message', 'test');
|
||||
});
|
||||
|
||||
it('should serialize _attributes to RISON', function () {
|
||||
var baseObject = new BaseObject();
|
||||
baseObject.message = 'Testing... 1234';
|
||||
var rison = baseObject.toRISON();
|
||||
expect(rison).to.equal('(message:\'Testing... 1234\')');
|
||||
});
|
||||
|
||||
it('should serialize _attributes for JSON', function () {
|
||||
var baseObject = new BaseObject();
|
||||
baseObject.message = 'Testing... 1234';
|
||||
baseObject._private = 'foo';
|
||||
baseObject.$private = 'stuff';
|
||||
var json = JSON.stringify(baseObject);
|
||||
expect(json).to.equal('{"message":"Testing... 1234"}');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
37
test/unit/specs/state_management/global_state.js
Normal file
37
test/unit/specs/state_management/global_state.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
define(function (require) {
|
||||
var sinon = require('sinon/sinon');
|
||||
require('components/state_management/global_state');
|
||||
|
||||
// Load kibana
|
||||
require('index');
|
||||
|
||||
describe('State Management', function () {
|
||||
var $rootScope, $location, state;
|
||||
|
||||
beforeEach(function () {
|
||||
module('kibana');
|
||||
|
||||
inject(function (_$location_, globalState) {
|
||||
$location = _$location_;
|
||||
state = globalState;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Global State', function () {
|
||||
it('should use previous state when not in URL', function () {
|
||||
// set satte via URL
|
||||
$location.search({ _g: '(foo:(bar:baz))' });
|
||||
state.fetch();
|
||||
expect(state.toObject()).to.eql({ foo: { bar: 'baz' } });
|
||||
|
||||
$location.search({ _g: '(fizz:buzz)' });
|
||||
state.fetch();
|
||||
expect(state.toObject()).to.eql({ fizz: 'buzz' });
|
||||
|
||||
$location.search({});
|
||||
state.fetch();
|
||||
expect(state.toObject()).to.eql({ fizz: 'buzz' });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,5 +1,4 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
var _ = require('lodash');
|
||||
var sinon = require('sinon/sinon');
|
||||
require('services/private');
|
||||
|
@ -8,26 +7,41 @@ define(function (require) {
|
|||
require('index');
|
||||
|
||||
describe('State Management', function () {
|
||||
describe('State', function () {
|
||||
var $rootScope, $location, State, Events;
|
||||
|
||||
var $rootScope, $location, State, Events;
|
||||
beforeEach(function () {
|
||||
module('kibana');
|
||||
|
||||
beforeEach(function () {
|
||||
module('kibana');
|
||||
inject(function (_$rootScope_, _$location_, Private) {
|
||||
$location = _$location_;
|
||||
$rootScope = _$rootScope_;
|
||||
State = Private(require('components/state_management/state'));
|
||||
Events = Private(require('factories/events'));
|
||||
});
|
||||
});
|
||||
|
||||
inject(function (_$rootScope_, _$location_, Private) {
|
||||
$location = _$location_;
|
||||
$rootScope = _$rootScope_;
|
||||
State = Private(require('components/state_management/state'));
|
||||
Events = Private(require('factories/events'));
|
||||
});
|
||||
describe('Provider', function () {
|
||||
it('should reset the state to the defaults', function () {
|
||||
var state = new State('_s', { message: ['test'] });
|
||||
state.reset();
|
||||
var search = $location.search();
|
||||
expect(search).to.have.property('_s');
|
||||
expect(search._s).to.equal('(message:!(test))');
|
||||
expect(state.message).to.eql(['test']);
|
||||
});
|
||||
|
||||
it('should apply the defaults upon initialization', function () {
|
||||
var state = new State('_s', { message: 'test' });
|
||||
expect(state).to.have.property('message', 'test');
|
||||
});
|
||||
|
||||
it('should inherit from Events', function () {
|
||||
var state = new State();
|
||||
expect(state).to.be.an(Events);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Search', function () {
|
||||
it('should save to $location.search()', function () {
|
||||
var state = new State('_s', { test: 'foo' });
|
||||
state.save();
|
||||
|
@ -47,8 +61,9 @@ define(function (require) {
|
|||
var search = $location.search();
|
||||
$rootScope.$apply();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('Fetch', function () {
|
||||
it('should emit an event if changes are fetched', function (done) {
|
||||
var state = new State();
|
||||
state.on('fetch_with_changes', function (keys) {
|
||||
|
@ -91,20 +106,6 @@ define(function (require) {
|
|||
expect(state).to.have.property('message', 'test');
|
||||
});
|
||||
|
||||
it('should reset the state to the defaults', function () {
|
||||
var state = new State('_s', { message: ['test'] });
|
||||
state.reset();
|
||||
var search = $location.search();
|
||||
expect(search).to.have.property('_s');
|
||||
expect(search._s).to.equal('(message:!(test))');
|
||||
expect(state.message).to.eql(['test']);
|
||||
});
|
||||
|
||||
it('should apply the defaults upon initialization', function () {
|
||||
var state = new State('_s', { message: 'test' });
|
||||
expect(state).to.have.property('message', 'test');
|
||||
});
|
||||
|
||||
it('should call fetch when $routeUpdate is fired on $rootScope', function () {
|
||||
var state = new State();
|
||||
var spy = sinon.spy(state, 'fetch');
|
||||
|
@ -112,7 +113,28 @@ define(function (require) {
|
|||
sinon.assert.calledOnce(spy);
|
||||
});
|
||||
|
||||
it('should clear state when missing form URL', function () {
|
||||
var stateObj;
|
||||
var state = new State();
|
||||
|
||||
// set satte via URL
|
||||
$location.search({ _s: '(foo:(bar:baz))' });
|
||||
state.fetch();
|
||||
stateObj = state.toObject();
|
||||
expect(stateObj).to.eql({ foo: { bar: 'baz' } });
|
||||
|
||||
// ensure changing URL changes state
|
||||
$location.search({ _s: '(one:two)' });
|
||||
state.fetch();
|
||||
stateObj = state.toObject();
|
||||
expect(stateObj).to.eql({ one: 'two' });
|
||||
|
||||
// remove search, state should be empty
|
||||
$location.search({});
|
||||
state.fetch();
|
||||
stateObj = state.toObject();
|
||||
expect(stateObj).to.eql({});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue