mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
Merge branch 'master' into tilemap-fix-3765
This commit is contained in:
commit
5ace38c68f
65 changed files with 2463 additions and 1025 deletions
|
@ -300,7 +300,7 @@ define(function (require) {
|
|||
* @returns {object}
|
||||
*/
|
||||
var cleanFilter = function (filter) {
|
||||
return _.omit(filter, ['$$hashKey', 'meta']);
|
||||
return _.omit(filter, ['meta']);
|
||||
};
|
||||
|
||||
// switch to filtered query if there are filters
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
define(function (require) {
|
||||
return function CourierFetchRequestStatus() {
|
||||
return {
|
||||
ABORTED: {},
|
||||
DUPLICATE: {},
|
||||
INCOMPLETE: {}
|
||||
ABORTED: { CourierFetchRequestStatus: 'aborted' },
|
||||
DUPLICATE: { CourierFetchRequestStatus: 'duplicate' },
|
||||
INCOMPLETE: { CourierFetchRequestStatus: 'incomplete' }
|
||||
};
|
||||
};
|
||||
});
|
|
@ -241,6 +241,7 @@ define(function (require) {
|
|||
return self.id;
|
||||
});
|
||||
};
|
||||
|
||||
return docSource.doCreate(source)
|
||||
.then(finish)
|
||||
.catch(function (err) {
|
||||
|
|
|
@ -12,21 +12,25 @@
|
|||
<div class="bar" ng-show="filters.length">
|
||||
<div class="filter" ng-class="{ negate: filter.meta.negate, disabled: filter.meta.disabled }" ng-repeat="filter in filters">
|
||||
<div class="filter-description">
|
||||
<!--<span><i class="fa" ng-class="{'fa-plus': !filter.meta.negate, 'fa-minus': filter.meta.negate}"></i></span>-->
|
||||
<span ng-if="filter.$state.store == 'globalState'"><i class="fa fa-fw fa-thumb-tack pinned"></i></span>
|
||||
<span>{{ filter.meta.key }}:</span>
|
||||
<span>"{{ filter.meta.value }}"</span>
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<a class="action filter-toggle" ng-click="toggleFilter(filter)">
|
||||
<i ng-show="filter.meta.disabled" class="fa fa-square-o"></i>
|
||||
<i ng-hide="filter.meta.disabled" class="fa fa-check-square-o"></i>
|
||||
<i ng-show="filter.meta.disabled" class="fa fa-fw fa-square-o disabled"></i>
|
||||
<i ng-hide="filter.meta.disabled" class="fa fa-fw fa-check-square-o enabled"></i>
|
||||
</a>
|
||||
<a class="action filter-pin" ng-click="pinFilter(filter)">
|
||||
<i ng-show="filter.$state.store == 'globalState'" class="fa fa-fw fa-thumb-tack pinned"></i>
|
||||
<i ng-hide="filter.$state.store == 'globalState'" class="fa fa-fw fa-thumb-tack fa-rotate-270 unpinned"></i>
|
||||
</a>
|
||||
<a class="action filter-invert" ng-click="invertFilter(filter)">
|
||||
<i ng-show="filter.meta.negate" class="fa fa-search-plus"></i>
|
||||
<i ng-hide="filter.meta.negate" class="fa fa-search-minus"></i>
|
||||
<i ng-show="filter.meta.negate" class="fa fa-fw fa-search-plus negative"></i>
|
||||
<i ng-hide="filter.meta.negate" class="fa fa-fw fa-search-minus positive"></i>
|
||||
</a>
|
||||
<a class="action filter-remove" ng-click="removeFilter(filter)">
|
||||
<i class="fa fa-trash"></i>
|
||||
<i class="fa fa-fw fa-trash"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -52,6 +56,12 @@
|
|||
<div class="filter-link">
|
||||
<div class="filter-description"><a ng-click="toggleAll(true)">Disable</a></div>
|
||||
</div>
|
||||
<div class="filter-link">
|
||||
<div class="filter-description"><a ng-click="pinAll(true)">Pin</a></div>
|
||||
</div>
|
||||
<div class="filter-link">
|
||||
<div class="filter-description"><a ng-click="pinAll(false)">Unpin</a></div>
|
||||
</div>
|
||||
<div class="filter-link">
|
||||
<div class="filter-description"><a ng-click="invertAll()">Invert</a></div>
|
||||
</div>
|
||||
|
|
|
@ -4,34 +4,43 @@ define(function (require) {
|
|||
var template = require('text!components/filter_bar/filter_bar.html');
|
||||
var moment = require('moment');
|
||||
|
||||
var toggleFilter = require('components/filter_bar/lib/toggleFilter');
|
||||
var toggleAll = require('components/filter_bar/lib/toggleAll');
|
||||
var invertFilter = require('components/filter_bar/lib/invertFilter');
|
||||
var invertAll = require('components/filter_bar/lib/invertAll');
|
||||
var removeFilter = require('components/filter_bar/lib/removeFilter');
|
||||
var removeAll = require('components/filter_bar/lib/removeAll');
|
||||
|
||||
var filterAppliedAndUnwrap = require('components/filter_bar/lib/filterAppliedAndUnwrap');
|
||||
|
||||
module.directive('filterBar', function (Private, Promise) {
|
||||
module.directive('filterBar', function (Private, Promise, getAppState) {
|
||||
var mapAndFlattenFilters = Private(require('components/filter_bar/lib/mapAndFlattenFilters'));
|
||||
var mapFlattenAndWrapFilters = Private(require('components/filter_bar/lib/mapFlattenAndWrapFilters'));
|
||||
var extractTimeFilter = Private(require('components/filter_bar/lib/extractTimeFilter'));
|
||||
var filterOutTimeBasedFilter = Private(require('components/filter_bar/lib/filterOutTimeBasedFilter'));
|
||||
var filterAppliedAndUnwrap = require('components/filter_bar/lib/filterAppliedAndUnwrap');
|
||||
var changeTimeFilter = Private(require('components/filter_bar/lib/changeTimeFilter'));
|
||||
var queryFilter = Private(require('components/filter_bar/query_filter'));
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
scope: {
|
||||
state: '='
|
||||
},
|
||||
scope: {},
|
||||
link: function ($scope, $el, attrs) {
|
||||
// bind query filter actions to the scope
|
||||
[
|
||||
'addFilters',
|
||||
'toggleFilter',
|
||||
'toggleAll',
|
||||
'pinFilter',
|
||||
'pinAll',
|
||||
'invertFilter',
|
||||
'invertAll',
|
||||
'removeFilter',
|
||||
'removeAll'
|
||||
].forEach(function (method) {
|
||||
$scope[method] = queryFilter[method];
|
||||
});
|
||||
|
||||
$scope.state = getAppState();
|
||||
|
||||
$scope.applyFilters = function (filters) {
|
||||
var newFilters = filterAppliedAndUnwrap(filters);
|
||||
$scope.state.filters = _.union($scope.state.filters, newFilters);
|
||||
// add new filters
|
||||
$scope.addFilters(filterAppliedAndUnwrap(filters));
|
||||
$scope.newFilters = [];
|
||||
|
||||
// change time filter
|
||||
if ($scope.changeTimeFilter && $scope.changeTimeFilter.meta && $scope.changeTimeFilter.meta.apply) {
|
||||
changeTimeFilter($scope.changeTimeFilter);
|
||||
}
|
||||
|
@ -42,10 +51,20 @@ define(function (require) {
|
|||
$scope.changeTimeFilter = null;
|
||||
};
|
||||
|
||||
// update the scope filter list on filter changes
|
||||
$scope.$listen(queryFilter, 'update', function () {
|
||||
updateFilters();
|
||||
});
|
||||
|
||||
// when appState changes, update scope's state
|
||||
$scope.$watch(getAppState, function (appState) {
|
||||
$scope.state = appState;
|
||||
});
|
||||
|
||||
$scope.$watch('state.$newFilters', function (filters) {
|
||||
if (!filters) return;
|
||||
|
||||
// If the filters is not undefined and the length is greater then
|
||||
// If filters is not undefined and the length is greater than
|
||||
// one we need to set the newFilters attribute and allow the
|
||||
// users to decide what they want to apply.
|
||||
if (filters.length > 1) {
|
||||
|
@ -72,24 +91,22 @@ define(function (require) {
|
|||
return filters;
|
||||
})
|
||||
.then(filterOutTimeBasedFilter)
|
||||
.then(function (filters) {
|
||||
$scope.state.filters = _.union($scope.state.filters, filters);
|
||||
});
|
||||
.then($scope.addFilters);
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$watch('state.filters', function (filters) {
|
||||
function updateFilters() {
|
||||
var filters = queryFilter.getFilters();
|
||||
mapAndFlattenFilters(filters).then(function (results) {
|
||||
$scope.filters = results;
|
||||
// used to display the current filters in the state
|
||||
$scope.filters = _.sortBy(results, function (filter) {
|
||||
return !filter.meta.pinned;
|
||||
});
|
||||
$scope.$emit('filterbar:updated');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$scope.toggleFilter = toggleFilter($scope);
|
||||
$scope.toggleAll = toggleAll($scope);
|
||||
$scope.invertFilter = invertFilter($scope);
|
||||
$scope.invertAll = invertAll($scope);
|
||||
$scope.removeFilter = removeFilter($scope);
|
||||
$scope.removeAll = removeAll($scope);
|
||||
updateFilters();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
@ -99,8 +99,8 @@ filter-bar .bar {
|
|||
}
|
||||
|
||||
> .filter-actions {
|
||||
font-size: 1.3em;
|
||||
line-height: 1.1em;
|
||||
font-size: 1.1em;
|
||||
line-height: 1.4em;
|
||||
position: absolute;
|
||||
padding: 4px 8px;
|
||||
top: 0;
|
||||
|
@ -109,18 +109,22 @@ filter-bar .bar {
|
|||
display: none;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
> .filter-actions > .action {
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.4);
|
||||
padding-right: 6px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
> * {
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.4);
|
||||
padding-right: 0;
|
||||
margin-right: 5px;
|
||||
|
||||
> .filter-actions > .action:last-child {
|
||||
border-right: 0;
|
||||
padding-right: 0;
|
||||
margin-right: 0;
|
||||
&:last-child {
|
||||
border-right: 0;
|
||||
padding-right: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.unpinned {
|
||||
.opacity(.7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.negate {
|
||||
|
|
|
@ -29,8 +29,7 @@ define(function (require) {
|
|||
|
||||
if (!filters.length) return;
|
||||
|
||||
filters = uniqFilters(filters);
|
||||
filters = dedupFilters($state.filters, filters);
|
||||
filters = dedupFilters($state.filters, uniqFilters(filters));
|
||||
// We need to add a bunch of filter deduping here.
|
||||
$state.$newFilters = filters;
|
||||
}
|
||||
|
|
33
src/kibana/components/filter_bar/lib/compareFilters.js
Normal file
33
src/kibana/components/filter_bar/lib/compareFilters.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
var angular = require('angular');
|
||||
var excludedAttributes;
|
||||
var comparators;
|
||||
|
||||
/**
|
||||
* Compare two filters to see if they match
|
||||
* @param {object} first The first filter to compare
|
||||
* @param {object} second The second filter to compare
|
||||
* @param {object} comparatorOptions Parameters to use for comparison
|
||||
* @returns {bool} Filters are the same
|
||||
*/
|
||||
return function (first, second, comparatorOptions) {
|
||||
excludedAttributes = ['$$hashKey', 'meta'];
|
||||
comparators = _.defaults(comparatorOptions || {}, {
|
||||
state: false,
|
||||
negate: false,
|
||||
disabled: false,
|
||||
});
|
||||
|
||||
if (!comparators.state) excludedAttributes.push('$state');
|
||||
|
||||
return _.isEqual(mapFilter(first), mapFilter(second));
|
||||
};
|
||||
|
||||
function mapFilter(filter) {
|
||||
var cleaned = _.omit(filter, excludedAttributes);
|
||||
if (comparators.negate) cleaned.negate = filter.meta && !!filter.meta.negate;
|
||||
if (comparators.disabled) cleaned.disabled = filter.meta && !!filter.meta.disabled;
|
||||
return cleaned;
|
||||
}
|
||||
});
|
|
@ -1,12 +1,22 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
var excludedAttributes = ['meta', '$$hashKey'];
|
||||
return function (existing, filters) {
|
||||
filters = _.filter(filters, function (item) {
|
||||
return !_.find(existing, function (existingFilter) {
|
||||
return _.isEqual(_.omit(existingFilter, excludedAttributes), _.omit(item, excludedAttributes));
|
||||
var angular = require('angular');
|
||||
var compareFilters = require('components/filter_bar/lib/compareFilters');
|
||||
|
||||
/**
|
||||
* Combine 2 filter collections, removing duplicates
|
||||
* @param {object} existing The filters to compare to
|
||||
* @param {object} filters The filters being added
|
||||
* @param {object} comparatorOptions Parameters to use for comparison
|
||||
* @returns {object} An array of filters that were not in existing
|
||||
*/
|
||||
return function (existingFilters, filters, comparatorOptions) {
|
||||
if (!_.isArray(filters)) filters = [filters];
|
||||
|
||||
return _.filter(filters, function (filter) {
|
||||
return !_.find(existingFilters, function (existingFilter) {
|
||||
return compareFilters(existingFilter, filter, comparatorOptions);
|
||||
});
|
||||
});
|
||||
return filters;
|
||||
};
|
||||
});
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
define(function (require) {
|
||||
return function ($scope) {
|
||||
var invertFilter = require('components/filter_bar/lib/invertFilter')($scope);
|
||||
|
||||
/**
|
||||
* Removes all filters
|
||||
* @returns {void}
|
||||
*/
|
||||
return function () {
|
||||
$scope.filters.forEach(function (filter) {
|
||||
invertFilter(filter);
|
||||
});
|
||||
|
||||
$scope.state.filters = $scope.filters;
|
||||
};
|
||||
};
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
return function ($scope) {
|
||||
/**
|
||||
* Inverts the nagate value on the filter
|
||||
* @param {object} filter The filter to toggle
|
||||
& @param {boolean} force disabled true/false
|
||||
* @returns {void}
|
||||
*/
|
||||
return function (filter) {
|
||||
// Toggle the negate meta state
|
||||
filter.meta.negate = !filter.meta.negate;
|
||||
|
||||
// Save the filters back to the searchSource
|
||||
$scope.state.filters = $scope.filters;
|
||||
return filter;
|
||||
};
|
||||
};
|
||||
});
|
|
@ -5,8 +5,13 @@ define(function (require) {
|
|||
return _.deepGet(filter, 'meta.disabled');
|
||||
};
|
||||
|
||||
return function (newFitlers, oldFilters) {
|
||||
var diff = _.difference(oldFilters, newFitlers);
|
||||
return (diff.length && _.every(diff, pluckDisabled));
|
||||
/**
|
||||
* Checks to see if only disabled filters have been changed
|
||||
* @returns {bool} Only disabled filters
|
||||
*/
|
||||
return function (newFilters, oldFilters) {
|
||||
return _.every(newFilters.concat(oldFilters), function (newFilter) {
|
||||
return pluckDisabled(newFilter);
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
19
src/kibana/components/filter_bar/lib/onlyStateChanged.js
Normal file
19
src/kibana/components/filter_bar/lib/onlyStateChanged.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
|
||||
var makeComparable = function (filter) {
|
||||
return _.omit(filter, ['$state', '$$hashKey']);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks to see if only disabled filters have been changed
|
||||
* @returns {bool} Only disabled filters
|
||||
*/
|
||||
return function (newFilters, oldFilters) {
|
||||
var comparableOldFilters = _.map(oldFilters, makeComparable);
|
||||
return _.every(newFilters, function (newFilter, i) {
|
||||
var match = _.find(comparableOldFilters, makeComparable(newFilter));
|
||||
return !!match;
|
||||
});
|
||||
};
|
||||
});
|
|
@ -1,11 +0,0 @@
|
|||
define(function (require) {
|
||||
return function ($scope) {
|
||||
/**
|
||||
* Removes all filters
|
||||
* @returns {void}
|
||||
*/
|
||||
return function () {
|
||||
$scope.state.filters = [];
|
||||
};
|
||||
};
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
return function ($scope) {
|
||||
/**
|
||||
* Removes the filter from the searchSource
|
||||
* @param {object} filter The filter to remove
|
||||
* @returns {void}
|
||||
*/
|
||||
return function (invalidFilter) {
|
||||
// Remove the filter from the the scope $filters and map it back
|
||||
// to the original format to save in searchSource
|
||||
$scope.state.filters = _($scope.filters)
|
||||
.filter(function (filter) {
|
||||
return filter !== invalidFilter;
|
||||
})
|
||||
.value();
|
||||
};
|
||||
|
||||
};
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
return function ($scope) {
|
||||
var toggleFilter = require('components/filter_bar/lib/toggleFilter')($scope);
|
||||
|
||||
/**
|
||||
* Disables all filters
|
||||
* @params {boolean} force disable/enable all filters
|
||||
* @returns {void}
|
||||
*/
|
||||
return function (force) {
|
||||
$scope.filters.forEach(function (filter) {
|
||||
toggleFilter(filter, force);
|
||||
});
|
||||
|
||||
$scope.state.filters = $scope.filters;
|
||||
};
|
||||
};
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
return function ($scope) {
|
||||
/**
|
||||
* Toggles the filter between enabled/disabled.
|
||||
* @param {object} filter The filter to toggle
|
||||
& @param {boolean} force disabled true/false
|
||||
* @returns {void}
|
||||
*/
|
||||
return function (filter, force) {
|
||||
// Toggle the disabled flag
|
||||
var disabled = _.isUndefined(force) ? !filter.meta.disabled : force;
|
||||
filter.meta.disabled = disabled;
|
||||
|
||||
// Save the filters back to the searchSource
|
||||
$scope.state.filters = $scope.filters;
|
||||
return filter;
|
||||
};
|
||||
};
|
||||
});
|
|
@ -1,10 +1,16 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
var dedupFilters = require('components/filter_bar/lib/dedupFilters');
|
||||
return function (filters) {
|
||||
|
||||
/**
|
||||
* Remove duplicate filters from an array of filters
|
||||
* @param {array} filters The filters to remove duplicates from
|
||||
* @returns {object} The original filters array with duplicates removed
|
||||
*/
|
||||
return function (filters, comparatorOptions) {
|
||||
var results = [];
|
||||
_.each(filters, function (filter) {
|
||||
results = _.union(results, dedupFilters(results, [filter]));
|
||||
results = _.union(results, dedupFilters(results, [filter], comparatorOptions));
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
define(function (require) {
|
||||
return function watchFiltersProvider(Private, Promise, Notifier) {
|
||||
var _ = require('lodash');
|
||||
var onlyDisabled = require('components/filter_bar/lib/onlyDisabled');
|
||||
var EventEmitter = Private(require('factories/events'));
|
||||
var notify = new Notifier({ location: 'Fitler Bar' });
|
||||
|
||||
return function ($scope, handlers) {
|
||||
var emitter = new EventEmitter();
|
||||
|
||||
$scope.$watch('state.filters', function (newFilters, oldFilters) {
|
||||
if (newFilters === oldFilters) return;
|
||||
|
||||
return emitter.emit('update')
|
||||
.then(function () {
|
||||
if (onlyDisabled(newFilters, oldFilters)) return;
|
||||
return emitter.emit('fetch');
|
||||
});
|
||||
});
|
||||
|
||||
return emitter;
|
||||
};
|
||||
};
|
||||
});
|
329
src/kibana/components/filter_bar/query_filter.js
Normal file
329
src/kibana/components/filter_bar/query_filter.js
Normal file
|
@ -0,0 +1,329 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
|
||||
return function (Private, $rootScope, getAppState, globalState) {
|
||||
var EventEmitter = Private(require('factories/events'));
|
||||
var onlyDisabled = require('components/filter_bar/lib/onlyDisabled');
|
||||
var onlyStateChanged = require('components/filter_bar/lib/onlyStateChanged');
|
||||
var uniqFilters = require('components/filter_bar/lib/uniqFilters');
|
||||
var compareFilters = require('components/filter_bar/lib/compareFilters');
|
||||
|
||||
var queryFilter = new EventEmitter();
|
||||
|
||||
queryFilter.getFilters = function () {
|
||||
var compareOptions = { disabled: true, negate: true };
|
||||
var appFilters = queryFilter.getAppFilters();
|
||||
var globalFilters = queryFilter.getGlobalFilters();
|
||||
|
||||
return uniqFilters(globalFilters.concat(appFilters), compareOptions);
|
||||
};
|
||||
|
||||
queryFilter.getAppFilters = function () {
|
||||
var appState = getAppState();
|
||||
if (!appState || !appState.filters) return [];
|
||||
return (appState.filters) ? _.map(appState.filters, appendStoreType('appState')) : [];
|
||||
};
|
||||
|
||||
queryFilter.getGlobalFilters = function () {
|
||||
if (!globalState.filters) return [];
|
||||
return _.map(globalState.filters, appendStoreType('globalState'));
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds new filters to the scope and state
|
||||
* @param {object|array} fitlers Filter(s) to add
|
||||
* @param {bool} global Should be added to global state
|
||||
* @returns {object} Resulting new filter list
|
||||
*/
|
||||
queryFilter.addFilters = function (filters, global) {
|
||||
var appState = getAppState();
|
||||
var state = (global) ? globalState : appState;
|
||||
|
||||
if (!_.isArray(filters)) {
|
||||
filters = [filters];
|
||||
}
|
||||
|
||||
if (global) {
|
||||
// simply concat global filters, they will be deduped
|
||||
globalState.filters = globalState.filters.concat(filters);
|
||||
} else if (appState) {
|
||||
if (!appState.filters) appState.filters = [];
|
||||
var mergeOptions = { disabled: true, negate: false };
|
||||
var appFilters = appState.filters.concat(filters);
|
||||
var merged = mergeAndMutateFilters(globalState.filters, appFilters, mergeOptions);
|
||||
globalState.filters = merged[0];
|
||||
appState.filters = merged[1];
|
||||
}
|
||||
|
||||
return saveState();
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the filter from the proper state
|
||||
* @param {object} matchFilter The filter to remove
|
||||
* @returns {object} Resulting new filter list
|
||||
*/
|
||||
queryFilter.removeFilter = function (matchFilter) {
|
||||
var state = getStateByFilter(matchFilter);
|
||||
if (!state) return;
|
||||
|
||||
_.pull(state.filters, matchFilter);
|
||||
return saveState();
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes all filters
|
||||
* @returns {object} Resulting new filter list
|
||||
*/
|
||||
queryFilter.removeAll = function () {
|
||||
var appState = getAppState();
|
||||
appState.filters = [];
|
||||
globalState.filters = [];
|
||||
return saveState();
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggles the filter between enabled/disabled.
|
||||
* @param {object} filter The filter to toggle
|
||||
& @param {boolean} force Disabled true/false
|
||||
* @returns {object} updated filter
|
||||
*/
|
||||
queryFilter.toggleFilter = function (filter, force) {
|
||||
// Toggle the disabled flag
|
||||
var disabled = _.isUndefined(force) ? !filter.meta.disabled : !!force;
|
||||
filter.meta.disabled = disabled;
|
||||
|
||||
// Save the filters back to the searchSource
|
||||
saveState();
|
||||
return filter;
|
||||
};
|
||||
|
||||
/**
|
||||
* Disables all filters
|
||||
* @params {boolean} force Disable/enable all filters
|
||||
* @returns {object} Resulting updated filter list
|
||||
*/
|
||||
queryFilter.toggleAll = function (force) {
|
||||
function doToggle(filter) {
|
||||
queryFilter.toggleFilter(filter, force);
|
||||
}
|
||||
|
||||
executeOnFilters(doToggle);
|
||||
return queryFilter.getFilters();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Inverts the nagate value on the filter
|
||||
* @param {object} filter The filter to toggle
|
||||
* @returns {object} updated filter
|
||||
*/
|
||||
queryFilter.invertFilter = function (filter) {
|
||||
// Toggle the negate meta state
|
||||
filter.meta.negate = !filter.meta.negate;
|
||||
|
||||
saveState();
|
||||
return filter;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inverts all filters
|
||||
* @returns {object} Resulting updated filter list
|
||||
*/
|
||||
queryFilter.invertAll = function () {
|
||||
executeOnFilters(queryFilter.invertFilter);
|
||||
return queryFilter.getFilters();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Pins the filter to the global state
|
||||
* @param {object} filter The filter to pin
|
||||
* @param {boolean} force pinned state
|
||||
* @returns {object} filter passed in
|
||||
*/
|
||||
queryFilter.pinFilter = function (filter, force) {
|
||||
var appState = getAppState();
|
||||
if (!appState) return filter;
|
||||
|
||||
// ensure that both states have a filters property
|
||||
if (!_.isArray(globalState.filters)) globalState.filters = [];
|
||||
if (!_.isArray(appState.filters)) appState.filters = [];
|
||||
|
||||
var appIndex = _.indexOf(appState.filters, filter);
|
||||
var globalIndex = _.indexOf(globalState.filters, filter);
|
||||
if (appIndex === -1 && globalIndex === -1) return;
|
||||
|
||||
if (appIndex !== -1 && force !== false) {
|
||||
appState.filters.splice(appIndex, 1);
|
||||
globalState.filters.push(filter);
|
||||
} else if (globalIndex !== -1 && force !== true) {
|
||||
globalState.filters.splice(globalIndex, 1);
|
||||
appState.filters.push(filter);
|
||||
}
|
||||
|
||||
saveState();
|
||||
return filter;
|
||||
};
|
||||
|
||||
/**
|
||||
* Pins all filters
|
||||
* @params {boolean} force Pin/Unpin all filters
|
||||
* @returns {object} Resulting updated filter list
|
||||
*/
|
||||
queryFilter.pinAll = function (force) {
|
||||
function pin(filter) {
|
||||
queryFilter.pinFilter(filter, force);
|
||||
}
|
||||
|
||||
executeOnFilters(pin);
|
||||
return queryFilter.getFilters();
|
||||
};
|
||||
|
||||
initWatchers();
|
||||
|
||||
return queryFilter;
|
||||
|
||||
/**
|
||||
* Saves both app and global states, ensuring filters are persisted
|
||||
* @returns {object} Resulting filter list, app and global combined
|
||||
*/
|
||||
function saveState() {
|
||||
var appState = getAppState();
|
||||
if (appState) appState.save();
|
||||
globalState.save();
|
||||
return queryFilter.getFilters();
|
||||
}
|
||||
|
||||
function appendStoreType(type) {
|
||||
return function (filter) {
|
||||
filter.$state = {
|
||||
store: type
|
||||
};
|
||||
return filter;
|
||||
};
|
||||
}
|
||||
|
||||
// get state (app or global) or the filter passed in
|
||||
function getStateByFilter(filter) {
|
||||
var appState = getAppState();
|
||||
if (appState) {
|
||||
var appIndex = _.indexOf(appState.filters, filter);
|
||||
if (appIndex !== -1) return appState;
|
||||
}
|
||||
|
||||
var globalIndex = _.indexOf(globalState.filters, filter);
|
||||
if (globalIndex !== -1) return globalState;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// helper to run a function on all filters in all states
|
||||
function executeOnFilters(fn) {
|
||||
var appState = getAppState();
|
||||
var appFilters;
|
||||
if (appState && appState.filters) {
|
||||
appFilters = appState.filters;
|
||||
} else {
|
||||
appFilters = [];
|
||||
}
|
||||
globalState.filters.concat(appFilters).forEach(fn);
|
||||
}
|
||||
|
||||
function mergeAndMutateFilters(globalFilters, appFilters, compareOptions) {
|
||||
appFilters = appFilters || [];
|
||||
globalFilters = globalFilters || [];
|
||||
compareOptions = _.defaults(compareOptions || {}, { disabled: true, negate: true });
|
||||
|
||||
// existing globalFilters should be mutated by appFilters
|
||||
appFilters = _.filter(appFilters, function (filter) {
|
||||
var match = _.find(globalFilters, function (globalFilter) {
|
||||
return compareFilters(globalFilter, filter, compareOptions);
|
||||
});
|
||||
|
||||
// if the filter remains, it doesn't match any filters in global state
|
||||
if (!match) return true;
|
||||
|
||||
// filter matches a filter in globalFilters, mutate existing global filter
|
||||
_.assign(match.meta, filter.meta);
|
||||
return false;
|
||||
});
|
||||
|
||||
appFilters = uniqFilters(appFilters, { disabled: true });
|
||||
globalFilters = uniqFilters(globalFilters, { disabled: true });
|
||||
|
||||
return [globalFilters, appFilters];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes state watchers that use the event emitter
|
||||
* @returns {void}
|
||||
*/
|
||||
function initWatchers() {
|
||||
var removeAppStateWatchers;
|
||||
|
||||
$rootScope.$watch(getAppState, function () {
|
||||
removeAppStateWatchers && removeAppStateWatchers();
|
||||
removeAppStateWatchers = initAppStateWatchers();
|
||||
});
|
||||
|
||||
function initAppStateWatchers() {
|
||||
// multi watch on the app and global states
|
||||
var stateWatchers = [{
|
||||
fn: $rootScope.$watch,
|
||||
deep: true,
|
||||
get: queryFilter.getGlobalFilters
|
||||
}, {
|
||||
fn: $rootScope.$watch,
|
||||
deep: true,
|
||||
get: queryFilter.getAppFilters
|
||||
}];
|
||||
|
||||
// when states change, use event emitter to trigger updates and fetches
|
||||
return $rootScope.$watchMulti(stateWatchers, function (next, prev) {
|
||||
var doUpdate = false;
|
||||
var doFetch = false;
|
||||
var newFilters = [];
|
||||
var oldFilters = [];
|
||||
|
||||
// iterate over each state type, checking for changes
|
||||
stateWatchers.forEach(function (watcher, i) {
|
||||
var nextVal = next[i];
|
||||
var prevVal = prev[i];
|
||||
newFilters = newFilters.concat(nextVal);
|
||||
oldFilters = oldFilters.concat(prevVal);
|
||||
|
||||
// no update or fetch if there was no change
|
||||
if (nextVal === prevVal) return;
|
||||
if (nextVal) doUpdate = true;
|
||||
|
||||
// don't trigger fetch when only disabled filters
|
||||
if (!onlyDisabled(nextVal, prevVal)) doFetch = true;
|
||||
});
|
||||
|
||||
// make sure change wasn't only a state move
|
||||
if (doFetch && newFilters.length === oldFilters.length) {
|
||||
if (onlyStateChanged(newFilters, oldFilters)) doFetch = false;
|
||||
}
|
||||
|
||||
// reconcile filter in global and app states
|
||||
var filters = mergeAndMutateFilters(next[0], next[1]);
|
||||
globalState.filters = filters[0];
|
||||
var appState = getAppState();
|
||||
if (appState) {
|
||||
appState.filters = filters[1];
|
||||
}
|
||||
saveState();
|
||||
|
||||
if (!doUpdate) return;
|
||||
|
||||
return queryFilter.emit('update')
|
||||
.then(function () {
|
||||
if (!doFetch) return;
|
||||
return queryFilter.emit('fetch');
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,73 +1,79 @@
|
|||
// Adds a filter to a passed state
|
||||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
var self = this;
|
||||
return function (Private) {
|
||||
var _ = require('lodash');
|
||||
var queryFilter = Private(require('components/filter_bar/query_filter'));
|
||||
var filterManager = {};
|
||||
|
||||
this.init = function ($state) {
|
||||
self.$state = $state;
|
||||
};
|
||||
filterManager.add = function (field, values, operation, index) {
|
||||
values = _.isArray(values) ? values : [values];
|
||||
var fieldName = _.isObject(field) ? field.name : field;
|
||||
var filters = _.flatten([queryFilter.getAppFilters()], true);
|
||||
var newFilters = [];
|
||||
|
||||
this.add = function (field, values, operation, index) {
|
||||
var negate = (operation === '-');
|
||||
|
||||
values = _.isArray(values) ? values : [values];
|
||||
|
||||
// Have we been passed a simple name or an actual field object?
|
||||
|
||||
var fieldName = _.isObject(field) ? field.name : field;
|
||||
|
||||
var negate = operation === '-';
|
||||
var filters = _.flatten([self.$state.filters], true);
|
||||
|
||||
// TODO: On array fields, negating does not negate the combination, rather all terms
|
||||
_.each(values, function (value) {
|
||||
var existing = _.find(filters, function (filter) {
|
||||
if (!filter) return;
|
||||
|
||||
if (fieldName === '_exists_' && filter.exists) {
|
||||
return filter.exists.field === value;
|
||||
}
|
||||
|
||||
if (filter.query) {
|
||||
return filter.query.match[fieldName] && filter.query.match[fieldName].query === value;
|
||||
}
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
if (existing.meta.negate !== negate) {
|
||||
existing.meta.negate = negate;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (fieldName) {
|
||||
case '_exists_':
|
||||
filters.push({ meta: { negate: negate, index: index }, exists: { field: value } });
|
||||
break;
|
||||
default:
|
||||
// TODO: On array fields, negating does not negate the combination, rather all terms
|
||||
_.each(values, function (value) {
|
||||
var filter;
|
||||
if (field.scripted) {
|
||||
var existing = _.find(filters, function (filter) {
|
||||
if (!filter) return;
|
||||
|
||||
if (fieldName === '_exists_' && filter.exists) {
|
||||
return filter.exists.field === value;
|
||||
}
|
||||
|
||||
if (filter.query) {
|
||||
return filter.query.match[fieldName] && filter.query.match[fieldName].query === value;
|
||||
}
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
existing.meta.disabled = false;
|
||||
if (existing.meta.negate !== negate) {
|
||||
queryFilter.invertFilter(existing);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (fieldName) {
|
||||
case '_exists_':
|
||||
filter = {
|
||||
meta: { negate: negate, index: index, field: fieldName },
|
||||
script: {
|
||||
script: '(' + field.script + ') == value',
|
||||
lang: field.lang,
|
||||
params: {
|
||||
value: value
|
||||
}
|
||||
meta: {
|
||||
negate: negate,
|
||||
index: index
|
||||
},
|
||||
exists: {
|
||||
field: value
|
||||
}
|
||||
};
|
||||
} else {
|
||||
filter = { meta: { negate: negate, index: index }, query: { match: {} } };
|
||||
filter.query.match[fieldName] = { query: value, type: 'phrase' };
|
||||
break;
|
||||
default:
|
||||
if (field.scripted) {
|
||||
filter = {
|
||||
meta: { negate: negate, index: index, field: fieldName },
|
||||
script: {
|
||||
script: '(' + field.script + ') == value',
|
||||
lang: field.lang,
|
||||
params: {
|
||||
value: value
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
filter = { meta: { negate: negate, index: index }, query: { match: {} } };
|
||||
filter.query.match[fieldName] = { query: value, type: 'phrase' };
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
filters.push(filter);
|
||||
|
||||
break;
|
||||
}
|
||||
});
|
||||
newFilters.push(filter);
|
||||
});
|
||||
|
||||
self.$state.filters = filters;
|
||||
queryFilter.addFilters(newFilters);
|
||||
};
|
||||
|
||||
return filterManager;
|
||||
};
|
||||
|
||||
return this;
|
||||
});
|
||||
|
|
|
@ -58,8 +58,7 @@ define(function (require) {
|
|||
State.prototype.fetch = function () {
|
||||
var stash = this._readFromURL();
|
||||
|
||||
// nothing to read from the url?
|
||||
// we should save if were are ordered to persist
|
||||
// nothing to read from the url? save if ordered to persist
|
||||
if (stash === null) {
|
||||
if (this._persistAcrossApps) {
|
||||
return this.save();
|
||||
|
|
|
@ -8,19 +8,18 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<span class="pull-right text-info hintbox-label" ng-click="editor.showUrlTemplateHelp = !editor.showUrlTemplateHelp">
|
||||
<span class="pull-right text-info hintbox-label" ng-click="editor.showUrlTmplHelp = !editor.showUrlTmplHelp">
|
||||
<i class="fa fa-info"></i> Url Template Help
|
||||
</span>
|
||||
|
||||
<label>Template</label>
|
||||
<text ng-model="editor.formatParams.format">
|
||||
<div class="hintbox" ng-if="editor.showUrlTemplateHelp">
|
||||
<label>Url Template</label>
|
||||
<div class="hintbox" ng-if="editor.showUrlTmplHelp">
|
||||
<h4 class="hintbox-heading">
|
||||
<i class="fa fa-question-circle text-info"></i> Url Template Help
|
||||
</h4>
|
||||
|
||||
<p>
|
||||
If a field only contains part of a url then a "Url Template" can be used to format the value as a complete url. The format is a string which uses double curly brace notation <code ng-bind="'\{\{ \}\}'"></code> to inject values. The following values can be accessed:
|
||||
If a field only contains part of a url then a "Url Template" can be used to format the value as a complete url. The format is a string which uses double curly brace notation <code>{­{ }­}</code> to inject values. The following values can be accessed:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
|
@ -31,10 +30,95 @@
|
|||
<strong>rawValue</strong> — The unescaped value
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<caption>Examples</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Value</th>
|
||||
<th>Template</th>
|
||||
<th>Result</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1234</td>
|
||||
<td>http://company.net/profiles?user_id={­{value}­}</td>
|
||||
<td>http://company.net/profiles?user_id=1234</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>users/admin</td>
|
||||
<td>http://company.net/groups?id={­{value}­}</td>
|
||||
<td>http://company.net/groups?id=users%2Fadmin</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>/images/favicon.ico</td>
|
||||
<td>http://www.site.com{­{rawValue}­}</td>
|
||||
<td>http://www.site.com/images/favicon.ico</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<field-format-editor-pattern
|
||||
ng-model="editor.formatParams.template"
|
||||
inputs="url.sampleInputs">
|
||||
</field-format-editor-pattern>
|
||||
<input ng-model="editor.formatParams.urlTemplate" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<span class="pull-right text-info hintbox-label" ng-click="editor.showLabelTmplHelp = !editor.showLabelTmplHelp">
|
||||
<i class="fa fa-info"></i> Label Template Help
|
||||
</span>
|
||||
|
||||
<label>Label Template</label>
|
||||
<div class="hintbox" ng-if="editor.showLabelTmplHelp">
|
||||
<h4 class="hintbox-heading">
|
||||
<i class="fa fa-question-circle text-info"></i> Label Template Help
|
||||
</h4>
|
||||
|
||||
<p>
|
||||
If the url in this field is large, it might be useful to provide an alternate template for the text version of the url. This will be displayed instead of the url, but will still link to the url. The format is a string which uses double curly brace notation <code>{­{ }­}</code> to inject values. The following values can be accessed:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<strong>value</strong> — The fields value
|
||||
</li>
|
||||
<li>
|
||||
<strong>url</strong> — The formatted url
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<caption>Examples</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Value</th>
|
||||
<th>Url Template</th>
|
||||
<th>Label Template</th>
|
||||
<th>Result</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1234</td>
|
||||
<td>http://company.net/profiles?user_id={­{value}­}</td>
|
||||
<td>User #{­{value}­}</td>
|
||||
<td>
|
||||
<a href="http://company.net/profiles?user_id=1234">User #1234</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>/assets/main.css</td>
|
||||
<td>http://site.com{­{rawValue}­}</td>
|
||||
<td>View Asset</td>
|
||||
<td>
|
||||
<a href="http://site.com/assets/main.css">View Asset</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<input ng-model="editor.formatParams.labelTemplate" class="form-control">
|
||||
</div>
|
||||
|
||||
<field-format-editor-samples inputs="url.sampleInputs"></field-format-editor-samples>
|
||||
|
|
|
@ -35,7 +35,8 @@ define(function (require) {
|
|||
Url.templateMatchRE = /{{([\s\S]+?)}}/g;
|
||||
Url.paramDefaults = {
|
||||
type: 'a',
|
||||
template: null
|
||||
urlTemplate: null,
|
||||
labelTemplate: null
|
||||
};
|
||||
|
||||
Url.urlTypes = [
|
||||
|
@ -43,25 +44,45 @@ define(function (require) {
|
|||
{ id: 'img', name: 'Image' }
|
||||
];
|
||||
|
||||
Url.prototype._formatUrl = function (value) {
|
||||
var template = this.param('urlTemplate');
|
||||
if (!template) return value;
|
||||
|
||||
return this._compileTemplate(template)({
|
||||
value: encodeURIComponent(value),
|
||||
rawValue: value
|
||||
});
|
||||
};
|
||||
|
||||
Url.prototype._formatLabel = function (value, url) {
|
||||
var template = this.param('labelTemplate');
|
||||
if (url == null) url = this._formatUrl(value);
|
||||
if (!template) return url;
|
||||
|
||||
return this._compileTemplate(template)({
|
||||
value: value,
|
||||
url: url
|
||||
});
|
||||
};
|
||||
|
||||
Url.prototype._convert = {
|
||||
text: function (value) {
|
||||
var template = this.param('template');
|
||||
return !template ? value : this._compileTemplate(template)(value);
|
||||
return this._formatLabel(value);
|
||||
},
|
||||
|
||||
html: function (rawValue, field, hit) {
|
||||
var url = _.escape(this.convert(rawValue, 'text'));
|
||||
var value = _.escape(rawValue);
|
||||
var url = _.escape(this._formatUrl(rawValue));
|
||||
var label = _.escape(this._formatLabel(rawValue, url));
|
||||
|
||||
switch (this.param('type')) {
|
||||
case 'img': return '<img src="' + url + '" alt="' + value + '" title="' + value + '">';
|
||||
case 'img':
|
||||
return '<img src="' + url + '" alt="' + label + '" title="' + label + '">';
|
||||
default:
|
||||
var urlDisplay = url;
|
||||
if (hit && hit.highlight && hit.highlight[field.name]) {
|
||||
urlDisplay = highlightFilter(url, hit.highlight[field.name]);
|
||||
label = highlightFilter(label, hit.highlight[field.name]);
|
||||
}
|
||||
|
||||
return '<a href="' + url + '" target="_blank">' + urlDisplay + '</a>';
|
||||
return '<a href="' + url + '" target="_blank">' + label + '</a>';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -72,12 +93,7 @@ define(function (require) {
|
|||
return (i % 2) ? part.trim() : part;
|
||||
});
|
||||
|
||||
return function (val) {
|
||||
var locals = {
|
||||
value: encodeURIComponent(val),
|
||||
rawValue: val
|
||||
};
|
||||
|
||||
return function (locals) {
|
||||
// replace all the odd bits with their local var
|
||||
var output = '';
|
||||
var i = -1;
|
||||
|
|
|
@ -57,6 +57,7 @@ define(function (require) {
|
|||
|
||||
// create a new maps array
|
||||
self.maps = [];
|
||||
self.popups = [];
|
||||
|
||||
var worldBounds = L.latLngBounds([-90, -220], [90, 220]);
|
||||
|
||||
|
@ -159,23 +160,29 @@ define(function (require) {
|
|||
self.addLabel(mapData.properties.label, map);
|
||||
}
|
||||
|
||||
// Add button to fit container to points
|
||||
var FitControl = L.Control.extend({
|
||||
options: {
|
||||
position: 'topleft'
|
||||
},
|
||||
onAdd: function (map) {
|
||||
var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar leaflet-control-zoom leaflet-control-fit');
|
||||
$(container).html('<a class="leaflet-control-zoom fa fa-crop" title="Fit Data Bounds"></a>');
|
||||
$(container).on('click', function () {
|
||||
self.fitBounds(map, featureLayer);
|
||||
});
|
||||
return container;
|
||||
}
|
||||
});
|
||||
var fitContainer = L.DomUtil.create('div', 'leaflet-control leaflet-bar leaflet-control-zoom leaflet-control-fit');
|
||||
|
||||
if (mapData && mapData.features.length > 0) {
|
||||
map.addControl(new FitControl());
|
||||
// Add button to fit container to points
|
||||
var FitControl = L.Control.extend({
|
||||
options: {
|
||||
position: 'topleft'
|
||||
},
|
||||
onAdd: function (map) {
|
||||
$(fitContainer).html('<a class="leaflet-control-zoom fa fa-crop" title="Fit Data Bounds"></a>');
|
||||
$(fitContainer).on('click', function () {
|
||||
self.fitBounds(map, featureLayer);
|
||||
});
|
||||
return fitContainer;
|
||||
},
|
||||
onRemove: function (map) {
|
||||
$(fitContainer).off('click');
|
||||
}
|
||||
});
|
||||
map.fitControl = new FitControl();
|
||||
map.addControl(map.fitControl);
|
||||
} else {
|
||||
map.fitControl = undefined;
|
||||
}
|
||||
|
||||
self.maps.push(map);
|
||||
|
@ -551,6 +558,8 @@ define(function (require) {
|
|||
.on('mouseout', function (e) {
|
||||
layer.closePopup();
|
||||
});
|
||||
|
||||
this.popups.push({elem: popup, layer: layer});
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -651,16 +660,22 @@ define(function (require) {
|
|||
* @return {undefined}
|
||||
*/
|
||||
TileMap.prototype.destroy = function () {
|
||||
if (this.maps && this.maps.length) {
|
||||
this.maps.forEach(function (map) {
|
||||
// Cleanup hanging DOM nodes
|
||||
// TODO: The correct way to handle this is to ensure all listeners are properly removed
|
||||
var children = $(map._container).find('*');
|
||||
map.remove();
|
||||
children.remove();
|
||||
if (this.popups) {
|
||||
this.popups.forEach(function (popup) {
|
||||
popup.elem.off('mouseover').off('mouseout');
|
||||
popup.layer.unbindPopup(popup.elem);
|
||||
});
|
||||
this.popups = [];
|
||||
}
|
||||
|
||||
if (this.maps) {
|
||||
this.maps.forEach(function (map) {
|
||||
if (map.fitControl) {
|
||||
map.fitControl.removeFrom(map);
|
||||
}
|
||||
map.remove();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return TileMap;
|
||||
|
|
|
@ -41,6 +41,8 @@ define(function (require) {
|
|||
var vals = new Array(expressions.length);
|
||||
var prev = new Array(expressions.length);
|
||||
var fire = false;
|
||||
var init = 0;
|
||||
var neededInits = expressions.length;
|
||||
|
||||
// first, register all of the multi-watchers
|
||||
var unwatchers = expressions.map(function (expr, i) {
|
||||
|
@ -48,6 +50,10 @@ define(function (require) {
|
|||
if (!expr) return;
|
||||
|
||||
return expr.fn.call($scope, expr.get, function (newVal, oldVal) {
|
||||
if (newVal === oldVal) {
|
||||
init += 1;
|
||||
}
|
||||
|
||||
vals[i] = newVal;
|
||||
prev[i] = oldVal;
|
||||
fire = true;
|
||||
|
@ -58,12 +64,16 @@ define(function (require) {
|
|||
// the other watchers triggered this cycle
|
||||
var flip = false;
|
||||
unwatchers.push($scope.$watch(function () {
|
||||
if (init < neededInits) return init;
|
||||
|
||||
if (fire) {
|
||||
fire = false;
|
||||
flip = !flip;
|
||||
}
|
||||
return flip;
|
||||
}, function () {
|
||||
if (init < neededInits) return false;
|
||||
|
||||
fn(vals.slice(0), prev.slice(0));
|
||||
vals.forEach(function (v, i) {
|
||||
prev[i] = v;
|
||||
|
@ -104,4 +114,4 @@ define(function (require) {
|
|||
return $delegate;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,12 +1,12 @@
|
|||
define(function (require) {
|
||||
var moment = require('moment');
|
||||
var filterManager = require('components/filter_manager/filter_manager');
|
||||
var $ = require('jquery');
|
||||
require('modules')
|
||||
.get('app/dashboard')
|
||||
.directive('dashboardPanel', function (savedVisualizations, savedSearches, Notifier, Private, $compile) {
|
||||
var _ = require('lodash');
|
||||
var loadPanel = Private(require('plugins/dashboard/components/panel/lib/load_panel'));
|
||||
var filterManager = Private(require('components/filter_manager/filter_manager'));
|
||||
var notify = new Notifier();
|
||||
|
||||
require('components/visualize/visualize');
|
||||
|
@ -32,7 +32,6 @@ define(function (require) {
|
|||
$scope.edit = panelConfig.edit;
|
||||
$scope.$on('$destroy', panelConfig.savedObj.destroy);
|
||||
|
||||
filterManager.init($state);
|
||||
$scope.filter = function (field, value, operator) {
|
||||
var index = $scope.savedObj.searchSource.get('index').id;
|
||||
filterManager.add(field, value, operator, index);
|
||||
|
|
|
@ -50,7 +50,7 @@ define(function (require) {
|
|||
app.directive('dashboardApp', function (Notifier, courier, AppState, timefilter, kbnUrl) {
|
||||
return {
|
||||
controller: function ($scope, $route, $routeParams, $location, configFile, Private, getAppState) {
|
||||
var filterBarWatchFilters = Private(require('components/filter_bar/lib/watchFilters'));
|
||||
var queryFilter = Private(require('components/filter_bar/query_filter'));
|
||||
|
||||
var notify = new Notifier({
|
||||
location: 'Dashboard'
|
||||
|
@ -110,22 +110,24 @@ define(function (require) {
|
|||
}
|
||||
|
||||
function updateQueryOnRootSource() {
|
||||
var filters = $state.filters;
|
||||
var filters = queryFilter.getFilters();
|
||||
if ($state.query) {
|
||||
dash.searchSource.set('filter', _.union($state.filters, [{
|
||||
query: $state.query
|
||||
dash.searchSource.set('filter', _.union(filters, [{
|
||||
query: $state.query
|
||||
}]));
|
||||
} else {
|
||||
dash.searchSource.set('filter', filters);
|
||||
}
|
||||
}
|
||||
|
||||
filterBarWatchFilters($scope)
|
||||
.on('update', function () {
|
||||
// update root source when filters update
|
||||
$scope.$listen(queryFilter, 'update', function () {
|
||||
updateQueryOnRootSource();
|
||||
$state.save();
|
||||
})
|
||||
.on('fetch', $scope.refresh);
|
||||
});
|
||||
|
||||
// update data when filters fire fetch event
|
||||
$scope.$listen(queryFilter, 'fetch', $scope.refresh);
|
||||
|
||||
$scope.newDashboard = function () {
|
||||
kbnUrl.change('/dashboard', {});
|
||||
|
|
|
@ -3,7 +3,6 @@ define(function (require) {
|
|||
var angular = require('angular');
|
||||
var moment = require('moment');
|
||||
var ConfigTemplate = require('utils/config_template');
|
||||
var filterManager = require('components/filter_manager/filter_manager');
|
||||
var getSort = require('components/doc_table/lib/get_sort');
|
||||
var rison = require('utils/rison');
|
||||
|
||||
|
@ -69,7 +68,8 @@ define(function (require) {
|
|||
var docTitle = Private(require('components/doc_title/doc_title'));
|
||||
var brushEvent = Private(require('utils/brush_event'));
|
||||
var HitSortFn = Private(require('plugins/discover/_hit_sort_fn'));
|
||||
var filterBarWatchFilters = Private(require('components/filter_bar/lib/watchFilters'));
|
||||
var queryFilter = Private(require('components/filter_bar/query_filter'));
|
||||
var filterManager = Private(require('components/filter_manager/filter_manager'));
|
||||
|
||||
var notify = new Notifier({
|
||||
location: 'Discover'
|
||||
|
@ -115,7 +115,7 @@ define(function (require) {
|
|||
columns: savedSearch.columns || ['_source'],
|
||||
index: $scope.indexPattern.id,
|
||||
interval: 'auto',
|
||||
filters: _.cloneDeep($scope.searchSource.get('filter'))
|
||||
filters: _.cloneDeep($scope.searchSource.getOwn('filter'))
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,6 @@ define(function (require) {
|
|||
});
|
||||
|
||||
var metaFields = config.get('metaFields');
|
||||
filterManager.init($state);
|
||||
|
||||
$scope.opts = {
|
||||
// number of records to fetch, then paginate through
|
||||
|
@ -165,13 +164,15 @@ define(function (require) {
|
|||
if (!angular.equals(sort, currentSort)) $scope.fetch();
|
||||
});
|
||||
|
||||
filterBarWatchFilters($scope)
|
||||
.on('update', function () {
|
||||
// update data source when filters update
|
||||
$scope.$listen(queryFilter, 'update', function () {
|
||||
return $scope.updateDataSource().then(function () {
|
||||
$state.save();
|
||||
});
|
||||
})
|
||||
.on('fetch', $scope.fetch);
|
||||
});
|
||||
|
||||
// fetch data when filters fire fetch event
|
||||
$scope.$listen(queryFilter, 'fetch', $scope.fetch);
|
||||
|
||||
$scope.$watch('opts.timefield', function (timefield) {
|
||||
timefilter.enabled = !!timefield;
|
||||
|
@ -424,7 +425,7 @@ define(function (require) {
|
|||
fields: {'*': {}},
|
||||
fragment_size: 2147483647 // Limit of an integer.
|
||||
})
|
||||
.set('filter', $state.filters || []);
|
||||
.set('filter', queryFilter.getFilters());
|
||||
});
|
||||
|
||||
// TODO: On array fields, negating does not negate the combination, rather all terms
|
||||
|
|
|
@ -79,6 +79,20 @@ define(function (require) {
|
|||
// build collection of agg params html
|
||||
type.params.forEach(function (param, i) {
|
||||
var aggParam;
|
||||
// if field param exists, compute allowed fields
|
||||
if (param.name === 'field') {
|
||||
$aggParamEditorsScope.indexedFields = getIndexedFields(param);
|
||||
}
|
||||
|
||||
if ($aggParamEditorsScope.indexedFields) {
|
||||
var hasIndexedFields = $aggParamEditorsScope.indexedFields.length > 0;
|
||||
var isExtraParam = i > 0;
|
||||
if (!hasIndexedFields && isExtraParam) { // don't draw the rest of the options if their are no indexed fields.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var type = 'basic';
|
||||
if (param.advanced) type = 'advanced';
|
||||
|
||||
|
@ -86,10 +100,6 @@ define(function (require) {
|
|||
aggParamHTML[type].push(aggParam);
|
||||
}
|
||||
|
||||
// if field param exists, compute allowed fields
|
||||
if (param.name === 'field') {
|
||||
$aggParamEditorsScope.indexedFields = getIndexedFields(param);
|
||||
}
|
||||
});
|
||||
|
||||
// compile the paramEditors html elements
|
||||
|
@ -112,8 +122,7 @@ define(function (require) {
|
|||
}
|
||||
|
||||
var attrs = {
|
||||
'agg-param': 'agg.type.params[' + idx + ']',
|
||||
'ng-hide': '!indexedFields.length && ' + idx + ' > 0' // if there are no fields, and this is one of the extra options
|
||||
'agg-param': 'agg.type.params[' + idx + ']'
|
||||
};
|
||||
|
||||
if (param.advanced) {
|
||||
|
|
|
@ -58,7 +58,7 @@ define(function (require) {
|
|||
var Notifier = require('components/notify/_notifier');
|
||||
var docTitle = Private(require('components/doc_title/doc_title'));
|
||||
var brushEvent = Private(require('utils/brush_event'));
|
||||
var filterBarWatchFilters = Private(require('components/filter_bar/lib/watchFilters'));
|
||||
var queryFilter = Private(require('components/filter_bar/query_filter'));
|
||||
var filterBarClickHandler = Private(require('components/filter_bar/filter_bar_click_handler'));
|
||||
|
||||
var notify = new Notifier({
|
||||
|
@ -152,19 +152,16 @@ define(function (require) {
|
|||
timefilter.enabled = !!timeField;
|
||||
});
|
||||
|
||||
filterBarWatchFilters($scope)
|
||||
.on('update', function () {
|
||||
if ($state.filters && $state.filters.length) {
|
||||
searchSource.set('filter', $state.filters);
|
||||
} else {
|
||||
searchSource.set('filter', []);
|
||||
}
|
||||
// update the searchSource when filters update
|
||||
$scope.$listen(queryFilter, 'update', function () {
|
||||
searchSource.set('filter', queryFilter.getFilters());
|
||||
$state.save();
|
||||
})
|
||||
.on('fetch', function () {
|
||||
$scope.fetch();
|
||||
});
|
||||
|
||||
// fetch data when filters fire fetch event
|
||||
$scope.$listen(queryFilter, 'fetch', $scope.fetch);
|
||||
|
||||
|
||||
$scope.$listen($state, 'fetch_with_changes', function (keys) {
|
||||
if (_.contains(keys, 'linked') && $state.linked === true) {
|
||||
// abort and reload route
|
||||
|
@ -187,7 +184,7 @@ define(function (require) {
|
|||
}
|
||||
|
||||
if (_.isEqual(keys, ['filters'])) {
|
||||
// updates will happen in filterBarWatchFilters() if needed
|
||||
// updates will happen in filter watcher if needed
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -210,7 +207,7 @@ define(function (require) {
|
|||
|
||||
$scope.fetch = function () {
|
||||
$state.save();
|
||||
searchSource.set('filter', $state.filters);
|
||||
searchSource.set('filter', queryFilter.getFilters());
|
||||
if (!$state.linked) searchSource.set('query', $state.query);
|
||||
if ($scope.vis.type.requiresSearch) {
|
||||
courier.fetch();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
@hintbox-background-color: @gray-lighter;
|
||||
@hintbox-spacing-vertical: 10px;
|
||||
@hintbox-spacing-horizontal: 12px;
|
||||
@hintbox-table-border-color: #BFC9CA;
|
||||
|
||||
.hintbox {
|
||||
padding: @hintbox-spacing-vertical @hintbox-spacing-horizontal;
|
||||
|
@ -38,4 +39,25 @@
|
|||
> * + * {
|
||||
margin-top: @hintbox-spacing-vertical;
|
||||
}
|
||||
|
||||
// https://github.com/twbs/bootstrap/blob/2aa102bfd40859d15790febed1939e0111a6fb1a/less/tables.less#L88-L106
|
||||
.table-bordered {
|
||||
border: 1px solid @hintbox-table-border-color;
|
||||
> thead,
|
||||
> tbody,
|
||||
> tfoot {
|
||||
> tr {
|
||||
> th,
|
||||
> td {
|
||||
border: 1px solid @hintbox-table-border-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
> thead > tr {
|
||||
> th,
|
||||
> td {
|
||||
border-bottom-width: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -295,5 +295,19 @@ define(function (require) {
|
|||
|
||||
return list;
|
||||
},
|
||||
|
||||
pushAll: function (source, dest) {
|
||||
var start = dest.length;
|
||||
var adding = source.length;
|
||||
|
||||
// allocate - http://goo.gl/e2i0S0
|
||||
dest.length = start + adding;
|
||||
|
||||
// fill sparse positions
|
||||
var i = -1;
|
||||
while (++i < adding) dest[start + i] = source[i];
|
||||
|
||||
return dest;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
@ -48,7 +48,7 @@ module.exports = function (app) {
|
|||
return path.basename(filename).charAt(0) !== '_';
|
||||
})
|
||||
.map(function (filename) {
|
||||
return path.relative(unit, filename).replace(/\.js$/, '');
|
||||
return path.relative(unit, filename).replace(/\\/g, '/').replace(/\.js$/, '');
|
||||
});
|
||||
|
||||
res.end(JSON.stringify(moduleIds));
|
||||
|
|
|
@ -6,7 +6,8 @@ var plugins = function (dir) {
|
|||
if (!dir) return [];
|
||||
var files = glob.sync(path.join(dir, '*', 'index.js')) || [];
|
||||
return files.map(function (file) {
|
||||
return file.replace(dir, 'plugins').replace(/\.js$/, '');
|
||||
var relative = path.relative(dir, file);
|
||||
return path.join('plugins', relative).replace(/\\/g, '/').replace(/\.js$/, '');
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ define(function (require) {
|
|||
{ name: 'ssl', type: 'boolean', indexed: true, analyzed: true, sortable: true, filterable: true, count: 20 },
|
||||
{ name: '@timestamp', type: 'date', indexed: true, analyzed: true, sortable: true, filterable: true, count: 30 },
|
||||
{ name: 'time', type: 'date', indexed: true, analyzed: true, sortable: true, filterable: true, count: 30 },
|
||||
{ name: '@tags', type: 'string', indexed: true, analyzed: true, sortable: true, filterable: true },
|
||||
{ name: 'utc_time', type: 'date', indexed: true, analyzed: true, sortable: true, filterable: true },
|
||||
{ name: 'phpmemory', type: 'number', indexed: true, analyzed: true, sortable: true, filterable: true },
|
||||
{ name: 'ip', type: 'ip', indexed: true, analyzed: true, sortable: true, filterable: true },
|
||||
|
|
18
test/unit/fixtures/mock_state.js
Normal file
18
test/unit/fixtures/mock_state.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
var sinon = require('test_utils/auto_release_sinon');
|
||||
|
||||
function MockState(defaults) {
|
||||
this.on = _.noop;
|
||||
this.off = _.noop;
|
||||
this.save = sinon.stub();
|
||||
_.assign(this, defaults);
|
||||
}
|
||||
|
||||
MockState.prototype.resetStub = function () {
|
||||
this.save = sinon.stub();
|
||||
return this;
|
||||
};
|
||||
|
||||
return MockState;
|
||||
});
|
|
@ -147,6 +147,7 @@ define(function (require) {
|
|||
var section = getSections($elem);
|
||||
$scope.columns.push('bytes');
|
||||
$scope.$digest();
|
||||
|
||||
expect(section.selected.text()).to.contain('bytes');
|
||||
expect(section.popular.text()).to.not.contain('bytes');
|
||||
|
||||
|
|
130
test/unit/specs/components/filter_bar/_addFilters.js
Normal file
130
test/unit/specs/components/filter_bar/_addFilters.js
Normal file
|
@ -0,0 +1,130 @@
|
|||
define(function (require) {
|
||||
return ['add filters', function () {
|
||||
var _ = require('lodash');
|
||||
var sinon = require('test_utils/auto_release_sinon');
|
||||
var MockState = require('fixtures/mock_state');
|
||||
var storeNames = {
|
||||
app: 'appState',
|
||||
global: 'globalState'
|
||||
};
|
||||
var filters;
|
||||
var queryFilter;
|
||||
var $rootScope, appState, globalState;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
|
||||
beforeEach(function () {
|
||||
appState = new MockState({ filters: [] });
|
||||
globalState = new MockState({ filters: [] });
|
||||
|
||||
filters = [
|
||||
{
|
||||
query: { match: { extension: { query: 'jpg', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
},
|
||||
{
|
||||
query: { match: { '@tags': { query: 'info', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
},
|
||||
{
|
||||
query: { match: { '_type': { query: 'nginx', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
module('kibana/global_state', function ($provide) {
|
||||
$provide.service('getAppState', function () {
|
||||
return function () {
|
||||
return appState;
|
||||
};
|
||||
});
|
||||
|
||||
$provide.service('globalState', function () {
|
||||
return globalState;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (_$rootScope_, Private) {
|
||||
$rootScope = _$rootScope_;
|
||||
queryFilter = Private(require('components/filter_bar/query_filter'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('adding filters', function () {
|
||||
it('should add filters to appState', function () {
|
||||
queryFilter.addFilters(filters);
|
||||
expect(appState.filters.length).to.be(3);
|
||||
expect(globalState.filters.length).to.be(0);
|
||||
});
|
||||
|
||||
it('should add filters to globalState', function () {
|
||||
queryFilter.addFilters(filters, true);
|
||||
expect(appState.filters.length).to.be(0);
|
||||
expect(globalState.filters.length).to.be(3);
|
||||
});
|
||||
|
||||
it('should accept a single filter', function () {
|
||||
queryFilter.addFilters(filters[0]);
|
||||
expect(appState.filters.length).to.be(1);
|
||||
expect(globalState.filters.length).to.be(0);
|
||||
});
|
||||
|
||||
it('should fire the update and fetch events', function () {
|
||||
var emitSpy = sinon.spy(queryFilter, 'emit');
|
||||
|
||||
// set up the watchers
|
||||
$rootScope.$digest();
|
||||
queryFilter.addFilters(filters);
|
||||
// trigger the digest loop to fire the watchers
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(emitSpy.callCount).to.be(2);
|
||||
expect(emitSpy.firstCall.args[0]).to.be('update');
|
||||
expect(emitSpy.secondCall.args[0]).to.be('fetch');
|
||||
});
|
||||
});
|
||||
|
||||
describe('filter reconciliation', function () {
|
||||
it('should de-dupe appState filters being added', function () {
|
||||
var newFilter = _.cloneDeep(filters[1]);
|
||||
appState.filters = filters;
|
||||
expect(appState.filters.length).to.be(3);
|
||||
|
||||
queryFilter.addFilters(newFilter);
|
||||
$rootScope.$digest();
|
||||
expect(appState.filters.length).to.be(3);
|
||||
});
|
||||
|
||||
it('should de-dupe globalState filters being added', function () {
|
||||
var newFilter = _.cloneDeep(filters[1]);
|
||||
globalState.filters = filters;
|
||||
expect(globalState.filters.length).to.be(3);
|
||||
|
||||
queryFilter.addFilters(newFilter, true);
|
||||
$rootScope.$digest();
|
||||
expect(globalState.filters.length).to.be(3);
|
||||
});
|
||||
|
||||
it('should mutate global filters on appState filter changes', function () {
|
||||
var idx = 1;
|
||||
globalState.filters = filters;
|
||||
var appFilter = _.cloneDeep(filters[idx]);
|
||||
appFilter.meta.negate = true;
|
||||
// use addFilters here, so custom adding logic can be applied
|
||||
queryFilter.addFilters(appFilter);
|
||||
|
||||
var res = queryFilter.getFilters();
|
||||
expect(res).to.have.length(3);
|
||||
_.each(res, function (filter, i) {
|
||||
expect(filter.$state.store).to.be('globalState');
|
||||
// make sure global filter actually mutated
|
||||
expect(filter.meta.negate).to.be(i === idx);
|
||||
});
|
||||
});
|
||||
});
|
||||
}];
|
||||
});
|
164
test/unit/specs/components/filter_bar/_getFilters.js
Normal file
164
test/unit/specs/components/filter_bar/_getFilters.js
Normal file
|
@ -0,0 +1,164 @@
|
|||
define(function (require) {
|
||||
return ['get filters', function () {
|
||||
var _ = require('lodash');
|
||||
var MockState = require('fixtures/mock_state');
|
||||
var storeNames = {
|
||||
app: 'appState',
|
||||
global: 'globalState'
|
||||
};
|
||||
var queryFilter;
|
||||
var $rootScope, appState, globalState;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
|
||||
beforeEach(function () {
|
||||
appState = new MockState({ filters: [] });
|
||||
globalState = new MockState({ filters: [] });
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
module('kibana/global_state', function ($provide) {
|
||||
$provide.service('getAppState', function () {
|
||||
return function () {
|
||||
return appState;
|
||||
};
|
||||
});
|
||||
|
||||
$provide.service('globalState', function () {
|
||||
return globalState;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (_$rootScope_, Private) {
|
||||
$rootScope = _$rootScope_;
|
||||
queryFilter = Private(require('components/filter_bar/query_filter'));
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFilters method', function () {
|
||||
var filters;
|
||||
|
||||
beforeEach(function () {
|
||||
filters = [
|
||||
{ query: { match: { extension: { query: 'jpg', type: 'phrase' } } } },
|
||||
{ query: { match: { '@tags': { query: 'info', type: 'phrase' } } } }
|
||||
];
|
||||
});
|
||||
|
||||
it('should return app and global filters', function () {
|
||||
appState.filters = [filters[0]];
|
||||
globalState.filters = [filters[1]];
|
||||
|
||||
// global filters should be listed first
|
||||
var res = queryFilter.getFilters();
|
||||
expect(res[0]).to.eql(filters[1]);
|
||||
expect(res[1]).to.eql(filters[0]);
|
||||
|
||||
// should return updated version of filters
|
||||
var newFilter = { query: { match: { '_type': { query: 'nginx', type: 'phrase' } } } };
|
||||
appState.filters.push(newFilter);
|
||||
|
||||
res = queryFilter.getFilters();
|
||||
expect(res).to.contain(newFilter);
|
||||
});
|
||||
|
||||
it('should append the state store', function () {
|
||||
appState.filters = [filters[0]];
|
||||
globalState.filters = [filters[1]];
|
||||
|
||||
var res = queryFilter.getFilters();
|
||||
expect(res[0].$state.store).to.be(storeNames.global);
|
||||
expect(res[1].$state.store).to.be(storeNames.app);
|
||||
});
|
||||
|
||||
it('should return filters from specific states', function () {
|
||||
var states = [
|
||||
[ globalState, queryFilter.getGlobalFilters ],
|
||||
[ appState, queryFilter.getAppFilters ],
|
||||
];
|
||||
|
||||
_.each(states, function (state) {
|
||||
state[0].filters = filters;
|
||||
var res = state[1]();
|
||||
expect(res.length).to.be(state[0].filters.length);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('filter reconciliation', function () {
|
||||
var filters;
|
||||
|
||||
beforeEach(function () {
|
||||
filters = [
|
||||
{
|
||||
query: { match: { extension: { query: 'jpg', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
},
|
||||
{
|
||||
query: { match: { '@tags': { query: 'info', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
},
|
||||
{
|
||||
query: { match: { '_type': { query: 'nginx', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should skip appState filters that match globalState filters', function () {
|
||||
globalState.filters = filters;
|
||||
var appFilter = _.cloneDeep(filters[1]);
|
||||
appState.filters.push(appFilter);
|
||||
|
||||
// global filters should be listed first
|
||||
var res = queryFilter.getFilters();
|
||||
expect(res).to.have.length(3);
|
||||
_.each(res, function (filter) {
|
||||
expect(filter.$state.store).to.be('globalState');
|
||||
});
|
||||
});
|
||||
|
||||
it('should append conflicting appState filters', function () {
|
||||
globalState.filters = filters;
|
||||
var appFilter = _.cloneDeep(filters[1]);
|
||||
appFilter.meta.negate = true;
|
||||
appState.filters.push(appFilter);
|
||||
|
||||
// global filters should be listed first
|
||||
var res = queryFilter.getFilters();
|
||||
expect(res).to.have.length(4);
|
||||
expect(res.filter(function (filter) {
|
||||
return filter.$state.store === storeNames.global;
|
||||
}).length).to.be(3);
|
||||
expect(res.filter(function (filter) {
|
||||
return filter.$state.store === storeNames.app;
|
||||
}).length).to.be(1);
|
||||
});
|
||||
|
||||
it('should not affect disabled filters', function () {
|
||||
// test adding to globalState
|
||||
globalState.filters = _.map(filters, function (filter) {
|
||||
var f = _.cloneDeep(filter);
|
||||
f.meta.disabled = true;
|
||||
return f;
|
||||
});
|
||||
_.each(filters, function (filter) { globalState.filters.push(filter); });
|
||||
var res = queryFilter.getFilters();
|
||||
expect(res).to.have.length(6);
|
||||
|
||||
// test adding to appState
|
||||
globalState.filters = _.map(filters, function (filter) {
|
||||
var f = _.cloneDeep(filter);
|
||||
f.meta.disabled = true;
|
||||
return f;
|
||||
});
|
||||
_.each(filters, function (filter) { appState.filters.push(filter); });
|
||||
res = queryFilter.getFilters();
|
||||
expect(res).to.have.length(6);
|
||||
});
|
||||
});
|
||||
}];
|
||||
});
|
114
test/unit/specs/components/filter_bar/_invertFilters.js
Normal file
114
test/unit/specs/components/filter_bar/_invertFilters.js
Normal file
|
@ -0,0 +1,114 @@
|
|||
define(function (require) {
|
||||
return ['invert filters', function () {
|
||||
var _ = require('lodash');
|
||||
var sinon = require('test_utils/auto_release_sinon');
|
||||
var MockState = require('fixtures/mock_state');
|
||||
var storeNames = {
|
||||
app: 'appState',
|
||||
global: 'globalState'
|
||||
};
|
||||
var filters;
|
||||
var queryFilter;
|
||||
var $rootScope, appState, globalState;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
|
||||
beforeEach(function () {
|
||||
appState = new MockState({ filters: [] });
|
||||
globalState = new MockState({ filters: [] });
|
||||
|
||||
filters = [
|
||||
{
|
||||
query: { match: { extension: { query: 'jpg', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
},
|
||||
{
|
||||
query: { match: { '@tags': { query: 'info', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
},
|
||||
{
|
||||
query: { match: { '_type': { query: 'nginx', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
module('kibana/global_state', function ($provide) {
|
||||
$provide.service('getAppState', function () {
|
||||
return function () {
|
||||
return appState;
|
||||
};
|
||||
});
|
||||
|
||||
$provide.service('globalState', function () {
|
||||
return globalState;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (_$rootScope_, Private) {
|
||||
$rootScope = _$rootScope_;
|
||||
queryFilter = Private(require('components/filter_bar/query_filter'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('inverting a filter', function () {
|
||||
it('should swap the negate property in appState', function () {
|
||||
_.each(filters, function (filter) {
|
||||
expect(filter.meta.negate).to.be(false);
|
||||
appState.filters.push(filter);
|
||||
});
|
||||
|
||||
queryFilter.invertFilter(filters[1]);
|
||||
expect(appState.filters[1].meta.negate).to.be(true);
|
||||
});
|
||||
|
||||
it('should toggle the negate property in globalState', function () {
|
||||
_.each(filters, function (filter) {
|
||||
expect(filter.meta.negate).to.be(false);
|
||||
globalState.filters.push(filter);
|
||||
});
|
||||
|
||||
queryFilter.invertFilter(filters[1]);
|
||||
expect(globalState.filters[1].meta.negate).to.be(true);
|
||||
});
|
||||
|
||||
it('should fire the update and fetch events', function () {
|
||||
var emitSpy = sinon.spy(queryFilter, 'emit');
|
||||
appState.filters = filters;
|
||||
|
||||
// set up the watchers
|
||||
$rootScope.$digest();
|
||||
queryFilter.invertFilter(filters[1]);
|
||||
// trigger the digest loop to fire the watchers
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(emitSpy.callCount).to.be(2);
|
||||
expect(emitSpy.firstCall.args[0]).to.be('update');
|
||||
expect(emitSpy.secondCall.args[0]).to.be('fetch');
|
||||
});
|
||||
});
|
||||
|
||||
describe('bulk inverting', function () {
|
||||
beforeEach(function () {
|
||||
appState.filters = filters;
|
||||
globalState.filters = _.map(_.cloneDeep(filters), function (filter) {
|
||||
filter.meta.negate = true;
|
||||
return filter;
|
||||
});
|
||||
});
|
||||
|
||||
it('should swap the negate state for all filters', function () {
|
||||
queryFilter.invertAll();
|
||||
_.each(appState.filters, function (filter) {
|
||||
expect(filter.meta.negate).to.be(true);
|
||||
});
|
||||
_.each(globalState.filters, function (filter) {
|
||||
expect(filter.meta.negate).to.be(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
}];
|
||||
});
|
160
test/unit/specs/components/filter_bar/_pinFilters.js
Normal file
160
test/unit/specs/components/filter_bar/_pinFilters.js
Normal file
|
@ -0,0 +1,160 @@
|
|||
define(function (require) {
|
||||
return ['pin filters', function () {
|
||||
var _ = require('lodash');
|
||||
var sinon = require('test_utils/auto_release_sinon');
|
||||
var MockState = require('fixtures/mock_state');
|
||||
var storeNames = {
|
||||
app: 'appState',
|
||||
global: 'globalState'
|
||||
};
|
||||
var filters;
|
||||
var queryFilter;
|
||||
var $rootScope, appState, globalState;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
|
||||
beforeEach(function () {
|
||||
appState = new MockState({ filters: [] });
|
||||
globalState = new MockState({ filters: [] });
|
||||
|
||||
filters = [
|
||||
{
|
||||
query: { match: { extension: { query: 'gif', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
},
|
||||
{
|
||||
query: { match: { extension: { query: 'jpg', type: 'phrase' } } },
|
||||
meta: { negate: true, disabled: false }
|
||||
},
|
||||
{
|
||||
query: { match: { extension: { query: 'png', type: 'phrase' } } },
|
||||
meta: { negate: true, disabled: true }
|
||||
},
|
||||
{
|
||||
query: { match: { '@tags': { query: 'info', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
},
|
||||
{
|
||||
query: { match: { '@tags': { query: 'success', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
},
|
||||
{
|
||||
query: { match: { '@tags': { query: 'security', type: 'phrase' } } },
|
||||
meta: { negate: true, disabled: false }
|
||||
},
|
||||
{
|
||||
query: { match: { '_type': { query: 'nginx', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
},
|
||||
{
|
||||
query: { match: { '_type': { query: 'apache', type: 'phrase' } } },
|
||||
meta: { negate: true, disabled: true }
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
module('kibana/global_state', function ($provide) {
|
||||
$provide.service('getAppState', function () {
|
||||
return function () {
|
||||
return appState;
|
||||
};
|
||||
});
|
||||
|
||||
$provide.service('globalState', function () {
|
||||
return globalState;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (_$rootScope_, Private) {
|
||||
$rootScope = _$rootScope_;
|
||||
queryFilter = Private(require('components/filter_bar/query_filter'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('pin a filter', function () {
|
||||
beforeEach(function () {
|
||||
globalState.filters = _.filter(filters, function (filter) {
|
||||
return !!filter.query.match._type;
|
||||
});
|
||||
appState.filters = _.filter(filters, function (filter) {
|
||||
return !filter.query.match._type;
|
||||
});
|
||||
expect(globalState.filters).to.have.length(2);
|
||||
expect(appState.filters).to.have.length(6);
|
||||
});
|
||||
|
||||
it('should move filter from appState to globalState', function () {
|
||||
var filter = appState.filters[1];
|
||||
|
||||
queryFilter.pinFilter(filter);
|
||||
expect(globalState.filters).to.contain(filter);
|
||||
expect(globalState.filters).to.have.length(3);
|
||||
expect(appState.filters).to.have.length(5);
|
||||
});
|
||||
|
||||
it('should move filter from globalState to appState', function () {
|
||||
var filter = globalState.filters[1];
|
||||
|
||||
queryFilter.pinFilter(filter);
|
||||
expect(appState.filters).to.contain(filter);
|
||||
expect(globalState.filters).to.have.length(1);
|
||||
expect(appState.filters).to.have.length(7);
|
||||
});
|
||||
|
||||
|
||||
it('should only fire the update event', function () {
|
||||
var filter = appState.filters[1];
|
||||
var emitSpy = sinon.spy(queryFilter, 'emit');
|
||||
|
||||
// set up the watchers
|
||||
$rootScope.$digest();
|
||||
queryFilter.pinFilter(filter);
|
||||
// trigger the digest loop to fire the watchers
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(emitSpy.callCount).to.be(1);
|
||||
expect(emitSpy.firstCall.args[0]).to.be('update');
|
||||
});
|
||||
});
|
||||
|
||||
describe('bulk pinning', function () {
|
||||
beforeEach(function () {
|
||||
globalState.filters = _.filter(filters, function (filter) {
|
||||
return !!filter.query.match.extension;
|
||||
});
|
||||
appState.filters = _.filter(filters, function (filter) {
|
||||
return !filter.query.match.extension;
|
||||
});
|
||||
expect(globalState.filters).to.have.length(3);
|
||||
expect(appState.filters).to.have.length(5);
|
||||
});
|
||||
|
||||
it('should swap the filters in both states', function () {
|
||||
var appSample = _.sample(appState.filters);
|
||||
var globalSample = _.sample(globalState.filters);
|
||||
|
||||
queryFilter.pinAll();
|
||||
expect(globalState.filters).to.have.length(5);
|
||||
expect(appState.filters).to.have.length(3);
|
||||
|
||||
expect(globalState.filters).to.contain(appSample);
|
||||
expect(appState.filters).to.contain(globalSample);
|
||||
});
|
||||
|
||||
it('should move all filters to globalState', function () {
|
||||
queryFilter.pinAll(true);
|
||||
expect(globalState.filters).to.have.length(8);
|
||||
expect(appState.filters).to.have.length(0);
|
||||
});
|
||||
|
||||
it('should move all filters to appState', function () {
|
||||
queryFilter.pinAll(false);
|
||||
expect(globalState.filters).to.have.length(0);
|
||||
expect(appState.filters).to.have.length(8);
|
||||
});
|
||||
});
|
||||
}];
|
||||
});
|
116
test/unit/specs/components/filter_bar/_removeFilters.js
Normal file
116
test/unit/specs/components/filter_bar/_removeFilters.js
Normal file
|
@ -0,0 +1,116 @@
|
|||
define(function (require) {
|
||||
return ['remove filters', function () {
|
||||
var _ = require('lodash');
|
||||
var sinon = require('test_utils/auto_release_sinon');
|
||||
var MockState = require('fixtures/mock_state');
|
||||
var storeNames = {
|
||||
app: 'appState',
|
||||
global: 'globalState'
|
||||
};
|
||||
var filters;
|
||||
var queryFilter;
|
||||
var $rootScope, appState, globalState;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
|
||||
beforeEach(function () {
|
||||
appState = new MockState({ filters: [] });
|
||||
globalState = new MockState({ filters: [] });
|
||||
|
||||
filters = [
|
||||
{
|
||||
query: { match: { extension: { query: 'jpg', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
},
|
||||
{
|
||||
query: { match: { '@tags': { query: 'info', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
},
|
||||
{
|
||||
query: { match: { '_type': { query: 'nginx', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
module('kibana/global_state', function ($provide) {
|
||||
$provide.service('getAppState', function () {
|
||||
return function () {
|
||||
return appState;
|
||||
};
|
||||
});
|
||||
|
||||
$provide.service('globalState', function () {
|
||||
return globalState;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (_$rootScope_, Private) {
|
||||
$rootScope = _$rootScope_;
|
||||
queryFilter = Private(require('components/filter_bar/query_filter'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('removing a filter', function () {
|
||||
it('should remove the filter from appState', function () {
|
||||
appState.filters = filters;
|
||||
expect(appState.filters).to.have.length(3);
|
||||
queryFilter.removeFilter(filters[0]);
|
||||
expect(appState.filters).to.have.length(2);
|
||||
});
|
||||
|
||||
it('should remove the filter from globalState', function () {
|
||||
globalState.filters = filters;
|
||||
expect(globalState.filters).to.have.length(3);
|
||||
queryFilter.removeFilter(filters[0]);
|
||||
expect(globalState.filters).to.have.length(2);
|
||||
});
|
||||
|
||||
it('should fire the update and fetch events', function () {
|
||||
var emitSpy = sinon.spy(queryFilter, 'emit');
|
||||
appState.filters = filters;
|
||||
|
||||
// set up the watchers
|
||||
$rootScope.$digest();
|
||||
queryFilter.removeFilter(filters[0]);
|
||||
// trigger the digest loop to fire the watchers
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(emitSpy.callCount).to.be(2);
|
||||
expect(emitSpy.firstCall.args[0]).to.be('update');
|
||||
expect(emitSpy.secondCall.args[0]).to.be('fetch');
|
||||
});
|
||||
|
||||
it('should only remove matching instances', function () {
|
||||
globalState.filters.push(filters[0]);
|
||||
globalState.filters.push(filters[1]);
|
||||
appState.filters.push(filters[2]);
|
||||
|
||||
queryFilter.removeFilter(_.cloneDeep(filters[0]));
|
||||
expect(globalState.filters).to.have.length(2);
|
||||
expect(appState.filters).to.have.length(1);
|
||||
|
||||
queryFilter.removeFilter(_.cloneDeep(filters[2]));
|
||||
expect(globalState.filters).to.have.length(2);
|
||||
expect(appState.filters).to.have.length(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bulk removal', function () {
|
||||
it('should remove all the filters from both states', function () {
|
||||
globalState.filters.push(filters[0]);
|
||||
globalState.filters.push(filters[1]);
|
||||
appState.filters.push(filters[2]);
|
||||
expect(globalState.filters).to.have.length(2);
|
||||
expect(appState.filters).to.have.length(1);
|
||||
|
||||
queryFilter.removeAll();
|
||||
expect(globalState.filters).to.have.length(0);
|
||||
expect(appState.filters).to.have.length(0);
|
||||
});
|
||||
});
|
||||
}];
|
||||
});
|
158
test/unit/specs/components/filter_bar/_toggleFilters.js
Normal file
158
test/unit/specs/components/filter_bar/_toggleFilters.js
Normal file
|
@ -0,0 +1,158 @@
|
|||
define(function (require) {
|
||||
return ['toggle filters', function () {
|
||||
var _ = require('lodash');
|
||||
var sinon = require('test_utils/auto_release_sinon');
|
||||
var MockState = require('fixtures/mock_state');
|
||||
var storeNames = {
|
||||
app: 'appState',
|
||||
global: 'globalState'
|
||||
};
|
||||
var filters;
|
||||
var queryFilter;
|
||||
var $rootScope, appState, globalState;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
|
||||
beforeEach(function () {
|
||||
appState = new MockState({ filters: [] });
|
||||
globalState = new MockState({ filters: [] });
|
||||
|
||||
filters = [
|
||||
{
|
||||
query: { match: { extension: { query: 'jpg', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
},
|
||||
{
|
||||
query: { match: { '@tags': { query: 'info', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
},
|
||||
{
|
||||
query: { match: { '_type': { query: 'nginx', type: 'phrase' } } },
|
||||
meta: { negate: false, disabled: false }
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
module('kibana/global_state', function ($provide) {
|
||||
$provide.service('getAppState', function () {
|
||||
return function () {
|
||||
return appState;
|
||||
};
|
||||
});
|
||||
|
||||
$provide.service('globalState', function () {
|
||||
return globalState;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (_$rootScope_, Private) {
|
||||
$rootScope = _$rootScope_;
|
||||
queryFilter = Private(require('components/filter_bar/query_filter'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggling a filter', function () {
|
||||
it('should toggle the disabled property in appState', function () {
|
||||
_.each(filters, function (filter) {
|
||||
expect(filter.meta.disabled).to.be(false);
|
||||
appState.filters.push(filter);
|
||||
});
|
||||
|
||||
queryFilter.toggleFilter(filters[1]);
|
||||
expect(appState.filters[1].meta.disabled).to.be(true);
|
||||
});
|
||||
|
||||
it('should toggle the disabled property in globalState', function () {
|
||||
_.each(filters, function (filter) {
|
||||
expect(filter.meta.disabled).to.be(false);
|
||||
globalState.filters.push(filter);
|
||||
});
|
||||
|
||||
queryFilter.toggleFilter(filters[1]);
|
||||
expect(globalState.filters[1].meta.disabled).to.be(true);
|
||||
});
|
||||
|
||||
it('should fire the update and fetch events', function () {
|
||||
var emitSpy = sinon.spy(queryFilter, 'emit');
|
||||
appState.filters = filters;
|
||||
|
||||
// set up the watchers
|
||||
$rootScope.$digest();
|
||||
queryFilter.toggleFilter(filters[1]);
|
||||
// trigger the digest loop to fire the watchers
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(emitSpy.callCount).to.be(2);
|
||||
expect(emitSpy.firstCall.args[0]).to.be('update');
|
||||
expect(emitSpy.secondCall.args[0]).to.be('fetch');
|
||||
});
|
||||
|
||||
it('should always enable the filter', function () {
|
||||
appState.filters = filters.map(function (filter) {
|
||||
filter.meta.disabled = true;
|
||||
return filter;
|
||||
});
|
||||
|
||||
expect(appState.filters[1].meta.disabled).to.be(true);
|
||||
queryFilter.toggleFilter(filters[1], false);
|
||||
expect(appState.filters[1].meta.disabled).to.be(false);
|
||||
queryFilter.toggleFilter(filters[1], false);
|
||||
expect(appState.filters[1].meta.disabled).to.be(false);
|
||||
});
|
||||
|
||||
it('should always disable the filter', function () {
|
||||
globalState.filters = filters;
|
||||
|
||||
expect(globalState.filters[1].meta.disabled).to.be(false);
|
||||
queryFilter.toggleFilter(filters[1], true);
|
||||
expect(globalState.filters[1].meta.disabled).to.be(true);
|
||||
queryFilter.toggleFilter(filters[1], true);
|
||||
expect(globalState.filters[1].meta.disabled).to.be(true);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('bulk toggling', function () {
|
||||
beforeEach(function () {
|
||||
appState.filters = filters;
|
||||
globalState.filters = _.map(_.cloneDeep(filters), function (filter) {
|
||||
filter.meta.disabled = true;
|
||||
return filter;
|
||||
});
|
||||
});
|
||||
|
||||
it('should swap the enabled state for all filters', function () {
|
||||
queryFilter.toggleAll();
|
||||
_.each(appState.filters, function (filter) {
|
||||
expect(filter.meta.disabled).to.be(true);
|
||||
});
|
||||
_.each(globalState.filters, function (filter) {
|
||||
expect(filter.meta.disabled).to.be(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should enable all filters', function () {
|
||||
queryFilter.toggleAll(true);
|
||||
_.each(appState.filters, function (filter) {
|
||||
expect(filter.meta.disabled).to.be(true);
|
||||
});
|
||||
_.each(globalState.filters, function (filter) {
|
||||
expect(filter.meta.disabled).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should disable all filters', function () {
|
||||
queryFilter.toggleAll(false);
|
||||
_.each(appState.filters, function (filter) {
|
||||
expect(filter.meta.disabled).to.be(false);
|
||||
});
|
||||
_.each(globalState.filters, function (filter) {
|
||||
expect(filter.meta.disabled).to.be(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
}];
|
||||
});
|
|
@ -3,7 +3,6 @@ define(function (require) {
|
|||
describe('Filter Bar Directive', function () {
|
||||
describe('dedupFilters(existing, filters)', function () {
|
||||
|
||||
|
||||
it('should return only filters which are not in the existing', function () {
|
||||
var existing = [
|
||||
{ range: { bytes: { from: 0, to: 1024 } } },
|
||||
|
@ -25,13 +24,26 @@ define(function (require) {
|
|||
];
|
||||
var filters = [
|
||||
{ range: { bytes: { from: 1024, to: 2048 } } },
|
||||
{ meta: { negate: false }, query: { match: { _term: { query: 'apache', type: 'phrase' } } } }
|
||||
{ query: { match: { _term: { query: 'apache', type: 'phrase' } } } }
|
||||
];
|
||||
var results = dedupFilters(existing, filters);
|
||||
expect(results).to.contain(filters[0]);
|
||||
expect(results).to.not.contain(filters[1]);
|
||||
});
|
||||
|
||||
it('should ignore $state attribute', function () {
|
||||
var existing = [
|
||||
{ range: { bytes: { from: 0, to: 1024 } } },
|
||||
{ $state: { store: 'appState' }, query: { match: { _term: { query: 'apache', type: 'phrase' } } } }
|
||||
];
|
||||
var filters = [
|
||||
{ range: { bytes: { from: 1024, to: 2048 } } },
|
||||
{ $state: { store: 'globalState' }, query: { match: { _term: { query: 'apache', type: 'phrase' } } } }
|
||||
];
|
||||
var results = dedupFilters(existing, filters);
|
||||
expect(results).to.contain(filters[0]);
|
||||
expect(results).to.not.contain(filters[1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,4 +16,4 @@ define(function (require) {
|
|||
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,16 +5,31 @@ define(function (require) {
|
|||
var $ = require('jquery');
|
||||
|
||||
require('components/filter_bar/filter_bar');
|
||||
var MockState = require('fixtures/mock_state');
|
||||
|
||||
describe('Filter Bar Directive', function () {
|
||||
var $rootScope, $compile, $timeout, Promise;
|
||||
var appState, queryFilter, mapFilter, getIndexPatternStub, indexPattern, $el;
|
||||
// require('test_utils/no_digest_promises').activateForSuite();
|
||||
|
||||
var $rootScope, $compile, getIndexPatternStub, indexPattern;
|
||||
beforeEach(function () {
|
||||
appState = new MockState({ filters: [] });
|
||||
|
||||
beforeEach(function (done) {
|
||||
module('kibana/global_state', function ($provide) {
|
||||
$provide.service('getAppState', function () {
|
||||
return function () {
|
||||
return appState;
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
// load the application
|
||||
module('kibana');
|
||||
|
||||
getIndexPatternStub = sinon.stub();
|
||||
|
||||
module('kibana/courier', function ($provide) {
|
||||
$provide.service('courier', function () {
|
||||
var courier = { indexPatterns: { get: getIndexPatternStub } };
|
||||
|
@ -22,38 +37,59 @@ define(function (require) {
|
|||
});
|
||||
});
|
||||
|
||||
inject(function (Promise, Private, _$rootScope_, _$compile_) {
|
||||
inject(function (Private, $injector, _$rootScope_, _$compile_, _$timeout_) {
|
||||
$rootScope = _$rootScope_;
|
||||
$compile = _$compile_;
|
||||
$timeout = _$timeout_;
|
||||
Promise = $injector.get('Promise');
|
||||
mapFilter = Private(require('components/filter_bar/lib/mapFilter'));
|
||||
|
||||
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
|
||||
getIndexPatternStub.returns(Promise.resolve(indexPattern));
|
||||
|
||||
$rootScope.state = {
|
||||
filters: [
|
||||
{ meta: { index: 'logstash-*' }, query: { match: { '_type': { query: 'apache' } } } },
|
||||
{ meta: { index: 'logstash-*' }, query: { match: { '_type': { query: 'nginx' } } } },
|
||||
{ meta: { index: 'logstash-*' }, exists: { field: '@timestamp' } },
|
||||
{ meta: { index: 'logstash-*' }, missing: { field: 'host' }, disabled: true },
|
||||
]
|
||||
var queryFilter = Private(require('components/filter_bar/query_filter'));
|
||||
queryFilter.getFilters = function () {
|
||||
return appState.filters;
|
||||
};
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render all the filters in state', function () {
|
||||
var el = $compile('<filter-bar state=state></filter-bar>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
var filters = el.find('.filter');
|
||||
expect(filters).to.have.length(4);
|
||||
expect($(filters[0]).find('span')[0].innerHTML).to.equal('_type:');
|
||||
expect($(filters[0]).find('span')[1].innerHTML).to.equal('"apache"');
|
||||
expect($(filters[1]).find('span')[0].innerHTML).to.equal('_type:');
|
||||
expect($(filters[1]).find('span')[1].innerHTML).to.equal('"nginx"');
|
||||
expect($(filters[2]).find('span')[0].innerHTML).to.equal('exists:');
|
||||
expect($(filters[2]).find('span')[1].innerHTML).to.equal('"@timestamp"');
|
||||
expect($(filters[3]).find('span')[0].innerHTML).to.equal('missing:');
|
||||
expect($(filters[3]).find('span')[1].innerHTML).to.equal('"host"');
|
||||
});
|
||||
describe('Element rendering', function () {
|
||||
beforeEach(function (done) {
|
||||
var filters = [
|
||||
{ meta: { index: 'logstash-*' }, query: { match: { '_type': { query: 'apache' } } } },
|
||||
{ meta: { index: 'logstash-*' }, query: { match: { '_type': { query: 'nginx' } } } },
|
||||
{ meta: { index: 'logstash-*' }, exists: { field: '@timestamp' } },
|
||||
{ meta: { index: 'logstash-*' }, missing: { field: 'host' }, disabled: true },
|
||||
];
|
||||
|
||||
Promise.map(filters, mapFilter).then(function (filters) {
|
||||
appState.filters = filters;
|
||||
$el = $compile('<filter-bar></filter-bar>')($rootScope);
|
||||
});
|
||||
|
||||
var off = $rootScope.$on('filterbar:updated', function () {
|
||||
off();
|
||||
// force a nextTick so it continues *after* the $digest loop completes
|
||||
setTimeout(done, 0);
|
||||
});
|
||||
|
||||
// kick off the digest loop
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
it('should render all the filters in state', function () {
|
||||
var filters = $el.find('.filter');
|
||||
expect(filters).to.have.length(4);
|
||||
expect($(filters[0]).find('span')[0].innerHTML).to.equal('_type:');
|
||||
expect($(filters[0]).find('span')[1].innerHTML).to.equal('"apache"');
|
||||
expect($(filters[1]).find('span')[0].innerHTML).to.equal('_type:');
|
||||
expect($(filters[1]).find('span')[1].innerHTML).to.equal('"nginx"');
|
||||
expect($(filters[2]).find('span')[0].innerHTML).to.equal('exists:');
|
||||
expect($(filters[2]).find('span')[1].innerHTML).to.equal('"@timestamp"');
|
||||
expect($(filters[3]).find('span')[0].innerHTML).to.equal('missing:');
|
||||
expect($(filters[3]).find('span')[1].innerHTML).to.equal('"host"');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,23 +3,95 @@ define(function (require) {
|
|||
describe('Filter Bar Directive', function () {
|
||||
describe('onlyDisabled()', function () {
|
||||
|
||||
it('should return true if all filters remove are disabled', function () {
|
||||
it('should return true if all filters are disabled', function () {
|
||||
var filters = [
|
||||
{ meta: { disabled: true } },
|
||||
{ meta: { disabled: true } },
|
||||
{ meta: { disabled: true } }
|
||||
];
|
||||
var newFilters = [{ meta: { disabled: true } }];
|
||||
expect(onlyDisabled(newFilters, filters)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return false if all filters are not disabled', function () {
|
||||
var filters = [
|
||||
{ meta: { disabled: false } },
|
||||
{ meta: { disabled: false } },
|
||||
{ meta: { disabled: false } }
|
||||
];
|
||||
var newFilters = [{ meta: { disabled: false } }];
|
||||
expect(onlyDisabled(newFilters, filters)).to.be(false);
|
||||
});
|
||||
|
||||
it('should return false if only old filters are disabled', function () {
|
||||
var filters = [
|
||||
{ meta: { disabled: true } },
|
||||
{ meta: { disabled: true } },
|
||||
{ meta: { disabled: true } }
|
||||
];
|
||||
var newFilters = [{ meta: { disabled: false } }];
|
||||
expect(onlyDisabled(newFilters, filters)).to.be(false);
|
||||
});
|
||||
|
||||
it('should return false if new filters are not disabled', function () {
|
||||
var filters = [
|
||||
{ meta: { disabled: false } },
|
||||
{ meta: { disabled: false } },
|
||||
{ meta: { disabled: false } }
|
||||
];
|
||||
var newFilters = [{ meta: { disabled: true } }];
|
||||
expect(onlyDisabled(newFilters, filters)).to.be(false);
|
||||
});
|
||||
|
||||
it('should return true when all removed filters were disabled', function () {
|
||||
var filters = [
|
||||
{ meta: { disabled: true } },
|
||||
{ meta: { disabled: true } },
|
||||
{ meta: { disabled: true } }
|
||||
];
|
||||
var newFilters = [];
|
||||
expect(onlyDisabled(newFilters, filters)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return false when all removed filters were not disabled', function () {
|
||||
var filters = [
|
||||
{ meta: { disabled: false } },
|
||||
{ meta: { disabled: false } },
|
||||
{ meta: { disabled: false } }
|
||||
];
|
||||
var newFilters = [];
|
||||
expect(onlyDisabled(newFilters, filters)).to.be(false);
|
||||
});
|
||||
|
||||
it('should return true if all changed filters are disabled', function () {
|
||||
var filters = [
|
||||
{ meta: { disabled: true, negate: false } },
|
||||
{ meta: { disabled: true, negate: false } }
|
||||
];
|
||||
var newFilters = [
|
||||
{ meta: { disabled: true, negate: true } },
|
||||
{ meta: { disabled: true, negate: true } }
|
||||
];
|
||||
expect(onlyDisabled(newFilters, filters)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return false if all filters remove were not disabled', function () {
|
||||
var filters = [
|
||||
{ meta: { disabled: false } },
|
||||
{ meta: { disabled: false } },
|
||||
{ meta: { disabled: true } }
|
||||
];
|
||||
var newFilters = [{ meta: { disabled: false } }];
|
||||
expect(onlyDisabled(newFilters, filters)).to.be(false);
|
||||
});
|
||||
|
||||
it('should return false when all removed filters are not disabled', function () {
|
||||
var filters = [
|
||||
{ meta: { disabled: true } },
|
||||
{ meta: { disabled: false } },
|
||||
{ meta: { disabled: true } }
|
||||
];
|
||||
var newFilters = [filters[1]];
|
||||
expect(onlyDisabled(newFilters, filters)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return false if all filters remove are not disabled', function () {
|
||||
var filters = [
|
||||
{ meta: { disabled: true } },
|
||||
{ meta: { disabled: false } },
|
||||
{ meta: { disabled: false } }
|
||||
];
|
||||
var newFilters = [filters[1]];
|
||||
var newFilters = [];
|
||||
expect(onlyDisabled(newFilters, filters)).to.be(false);
|
||||
});
|
||||
|
||||
|
|
59
test/unit/specs/components/filter_bar/query_filter.js
Normal file
59
test/unit/specs/components/filter_bar/query_filter.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
var queryFilter;
|
||||
var EventEmitter;
|
||||
var $rootScope;
|
||||
|
||||
describe('Query Filter', function () {
|
||||
describe('Module', function () {
|
||||
beforeEach(module('kibana'));
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (_$rootScope_, Private) {
|
||||
$rootScope = _$rootScope_;
|
||||
queryFilter = Private(require('components/filter_bar/query_filter'));
|
||||
EventEmitter = Private(require('factories/events'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('module instance', function () {
|
||||
it('should be an event emitter', function () {
|
||||
expect(queryFilter).to.be.an(EventEmitter);
|
||||
});
|
||||
});
|
||||
|
||||
describe('module methods', function () {
|
||||
it('should have methods for getting filters', function () {
|
||||
expect(queryFilter.getFilters).to.be.a('function');
|
||||
expect(queryFilter.getAppFilters).to.be.a('function');
|
||||
expect(queryFilter.getGlobalFilters).to.be.a('function');
|
||||
});
|
||||
|
||||
it('should have methods for modifying filters', function () {
|
||||
expect(queryFilter.addFilters).to.be.a('function');
|
||||
expect(queryFilter.toggleFilter).to.be.a('function');
|
||||
expect(queryFilter.toggleAll).to.be.a('function');
|
||||
expect(queryFilter.removeFilter).to.be.a('function');
|
||||
expect(queryFilter.removeAll).to.be.a('function');
|
||||
expect(queryFilter.invertFilter).to.be.a('function');
|
||||
expect(queryFilter.invertAll).to.be.a('function');
|
||||
expect(queryFilter.pinFilter).to.be.a('function');
|
||||
expect(queryFilter.pinAll).to.be.a('function');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Actions', function () {
|
||||
var childSuites = [
|
||||
require('specs/components/filter_bar/_getFilters'),
|
||||
require('specs/components/filter_bar/_addFilters'),
|
||||
require('specs/components/filter_bar/_removeFilters'),
|
||||
require('specs/components/filter_bar/_toggleFilters'),
|
||||
require('specs/components/filter_bar/_invertFilters'),
|
||||
require('specs/components/filter_bar/_pinFilters'),
|
||||
].forEach(function (s) {
|
||||
describe(s[0], s[1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,40 +0,0 @@
|
|||
define(function (require) {
|
||||
var removeAll = require('components/filter_bar/lib/removeAll');
|
||||
|
||||
describe('Filter Bar Directive', function () {
|
||||
|
||||
var $rootScope, $compile;
|
||||
|
||||
beforeEach(function (done) {
|
||||
// load the application
|
||||
module('kibana');
|
||||
|
||||
inject(function (_$rootScope_, _$compile_) {
|
||||
$rootScope = _$rootScope_;
|
||||
$compile = _$compile_;
|
||||
$rootScope.state = {
|
||||
filters: [
|
||||
{ query: { match: { '@tags': { query: 'test' } } } },
|
||||
{ query: { match: { '@tags': { query: 'bar' } } } },
|
||||
{ exists: { field: '@timestamp' } },
|
||||
{ missing: { field: 'host' }, meta: { disabled: true } },
|
||||
]
|
||||
};
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('removeAll', function () {
|
||||
it('should remove all the filters', function () {
|
||||
var fn = removeAll($rootScope);
|
||||
expect($rootScope.state.filters).to.have.length(4);
|
||||
fn();
|
||||
expect($rootScope.state.filters).to.have.length(0);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
define(function (require) {
|
||||
var removeFilter = require('components/filter_bar/lib/removeFilter');
|
||||
|
||||
describe('Filter Bar Directive', function () {
|
||||
|
||||
var $rootScope, $compile;
|
||||
|
||||
beforeEach(function (done) {
|
||||
// load the application
|
||||
module('kibana');
|
||||
|
||||
inject(function (_$rootScope_, _$compile_) {
|
||||
$rootScope = _$rootScope_;
|
||||
$compile = _$compile_;
|
||||
$rootScope.state = {
|
||||
filters: [
|
||||
{ query: { match: { '@tags': { query: 'test' } } } },
|
||||
{ query: { match: { '@tags': { query: 'bar' } } } },
|
||||
{ exists: { field: '@timestamp' } },
|
||||
{ missing: { field: 'host' }, meta: { disabled: true } },
|
||||
]
|
||||
};
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('removeFilter', function () {
|
||||
it('should remove the filter from the state', function () {
|
||||
var filter = $rootScope.state.filters[2];
|
||||
var fn = removeFilter($rootScope);
|
||||
fn(filter);
|
||||
expect($rootScope.state.filters).to.not.contain(filter);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
define(function (require) {
|
||||
describe('Filter Bar Directive', function () {
|
||||
var toggleAll = require('components/filter_bar/lib/toggleAll');
|
||||
var _ = require('lodash');
|
||||
var sinon = require('test_utils/auto_release_sinon');
|
||||
var mapFilter, $rootScope, $compile, Promise, getIndexPatternStub, indexPattern;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
|
||||
beforeEach(function () {
|
||||
getIndexPatternStub = sinon.stub();
|
||||
module('kibana/courier', function ($provide) {
|
||||
$provide.service('courier', function () {
|
||||
var courier = { indexPatterns: { get: getIndexPatternStub } };
|
||||
return courier;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(inject(function (_Promise_, _$rootScope_, _$compile_, Private) {
|
||||
Promise = _Promise_;
|
||||
mapFilter = Private(require('components/filter_bar/lib/mapFilter'));
|
||||
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
|
||||
getIndexPatternStub.returns(Promise.resolve(indexPattern));
|
||||
$rootScope = _$rootScope_;
|
||||
$compile = _$compile_;
|
||||
$rootScope.state = {
|
||||
filters: [
|
||||
{ meta: { index: 'logstash-*' }, query: { match: { '_type': { query: 'apache' } } } },
|
||||
{ meta: { index: 'logstash-*' }, query: { match: { '_type': { query: 'nginx' } } } },
|
||||
{ meta: { index: 'logstash-*' }, exists: { field: '@timestamp' } },
|
||||
{ meta: { index: 'logstash-*', disabled: true }, missing: { field: 'host' } },
|
||||
]
|
||||
};
|
||||
}));
|
||||
|
||||
describe('toggleAll', function () {
|
||||
var fn;
|
||||
|
||||
beforeEach(function (done) {
|
||||
var _filters = _($rootScope.state.filters)
|
||||
.filter(function (filter) { return filter; })
|
||||
.flatten(true)
|
||||
.value();
|
||||
|
||||
Promise.map(_filters, mapFilter).then(function (filters) {
|
||||
$rootScope.filters = filters;
|
||||
done();
|
||||
});
|
||||
$rootScope.$apply();
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
fn = toggleAll($rootScope);
|
||||
});
|
||||
|
||||
var pickDisabled = function (filter) {
|
||||
return filter.meta.disabled;
|
||||
};
|
||||
|
||||
it('should toggle all the filters', function () {
|
||||
expect(_.filter($rootScope.state.filters, pickDisabled)).to.have.length(1);
|
||||
fn();
|
||||
expect(_.filter($rootScope.state.filters, pickDisabled)).to.have.length(3);
|
||||
});
|
||||
|
||||
it('should disable all the filters', function () {
|
||||
expect(_.filter($rootScope.state.filters, pickDisabled)).to.have.length(1);
|
||||
fn(true);
|
||||
expect(_.filter($rootScope.state.filters, pickDisabled)).to.have.length(4);
|
||||
});
|
||||
|
||||
it('should enable all the filters', function () {
|
||||
expect(_.filter($rootScope.state.filters, pickDisabled)).to.have.length(1);
|
||||
fn(false);
|
||||
expect(_.filter($rootScope.state.filters, pickDisabled)).to.have.length(0);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
define(function (require) {
|
||||
describe('Filter Bar Directive', function () {
|
||||
var sinon = require('test_utils/auto_release_sinon');
|
||||
var toggleFilter = require('components/filter_bar/lib/toggleFilter');
|
||||
var $rootScope, $compile, mapFilter, getIndexPatternStub, indexPattern;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
|
||||
beforeEach(function () {
|
||||
getIndexPatternStub = sinon.stub();
|
||||
module('kibana/courier', function ($provide) {
|
||||
$provide.service('courier', function () {
|
||||
var courier = { indexPatterns: { get: getIndexPatternStub } };
|
||||
return courier;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(inject(function (Promise, Private, _$rootScope_, _$compile_) {
|
||||
mapFilter = Private(require('components/filter_bar/lib/mapFilter'));
|
||||
$rootScope = _$rootScope_;
|
||||
$compile = _$compile_;
|
||||
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
|
||||
getIndexPatternStub.returns(Promise.resolve(indexPattern));
|
||||
$rootScope.state = {
|
||||
filters: [
|
||||
{ meta: { index: 'logstash-*' }, query: { match: { '_type': { query: 'apache' } } } },
|
||||
{ meta: { index: 'logstash-*' }, query: { match: { '_type': { query: 'nginx' } } } },
|
||||
{ meta: { index: 'logstash-*' }, exists: { field: '@timestamp' } },
|
||||
{ missing: { field: 'host' }, meta: { disabled: true, index: 'logstash-*' } },
|
||||
]
|
||||
};
|
||||
}));
|
||||
|
||||
describe('toggleFilter', function () {
|
||||
it('should toggle filters on and off', function (done) {
|
||||
var filter = $rootScope.state.filters[0];
|
||||
var fn = toggleFilter($rootScope);
|
||||
mapFilter(filter).then(fn).then(function (result) {
|
||||
expect(result.meta).to.have.property('disabled', true);
|
||||
done();
|
||||
});
|
||||
$rootScope.$apply();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
@ -12,6 +12,35 @@ define(function (require) {
|
|||
expect(results).to.have.length(1);
|
||||
});
|
||||
|
||||
it('should filter out duplicates, ignoring meta attributes', function () {
|
||||
var before = [
|
||||
{
|
||||
meta: { negate: true },
|
||||
query: { _type: { match: { query: 'apache', type: 'phrase' } } }
|
||||
},
|
||||
{
|
||||
meta: { negate: false },
|
||||
query: { _type: { match: { query: 'apache', type: 'phrase' } } }
|
||||
}
|
||||
];
|
||||
var results = uniqFilters(before);
|
||||
expect(results).to.have.length(1);
|
||||
});
|
||||
|
||||
it('should filter out duplicates, ignoring $state attributes', function () {
|
||||
var before = [
|
||||
{
|
||||
$state: { store: 'appState' },
|
||||
query: { _type: { match: { query: 'apache', type: 'phrase' } } }
|
||||
},
|
||||
{
|
||||
$state: { store: 'globalState' },
|
||||
query: { _type: { match: { query: 'apache', type: 'phrase' } } }
|
||||
}
|
||||
];
|
||||
var results = uniqFilters(before);
|
||||
expect(results).to.have.length(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
define(function (require) {
|
||||
describe('Filter Bar watchFilters()', function () {
|
||||
var sinon = require('test_utils/auto_release_sinon');
|
||||
var _ = require('lodash');
|
||||
|
||||
var watchFilters;
|
||||
var Promise;
|
||||
var EventEmitter;
|
||||
var $scope;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
beforeEach(inject(function (Private, $injector) {
|
||||
Promise = $injector.get('Promise');
|
||||
EventEmitter = Private(require('factories/events'));
|
||||
watchFilters = Private(require('components/filter_bar/lib/watchFilters'));
|
||||
$scope = {
|
||||
$watch: sinon.stub()
|
||||
};
|
||||
}));
|
||||
|
||||
it('returns an event emitter', function () {
|
||||
expect(watchFilters($scope)).to.be.an(EventEmitter);
|
||||
});
|
||||
|
||||
it('listens to the filters on state', function () {
|
||||
watchFilters($scope, { update: _.noop, fetch: _.noop });
|
||||
expect($scope.$watch).to.have.property('callCount', 1);
|
||||
|
||||
var call = $scope.$watch.getCall(0);
|
||||
expect(call.args[0]).to.be('state.filters');
|
||||
});
|
||||
|
||||
describe('change handling', function () {
|
||||
require('test_utils/no_digest_promises').activateForSuite();
|
||||
|
||||
it('calls update and fetch', function () {
|
||||
var onFetch = sinon.stub();
|
||||
var onUpdate = sinon.stub();
|
||||
|
||||
watchFilters($scope).on('fetch', onFetch).on('update', onUpdate);
|
||||
var handler = $scope.$watch.args[0][1];
|
||||
|
||||
return handler([ {} ], [])
|
||||
.then(function () {
|
||||
expect(onUpdate).to.have.property('callCount', 1);
|
||||
expect(onFetch).to.have.property('callCount', 1);
|
||||
});
|
||||
});
|
||||
|
||||
it('only calls update if all filters are disabled', function () {
|
||||
var onFetch = sinon.stub();
|
||||
var onUpdate = sinon.stub();
|
||||
|
||||
watchFilters($scope).on('fetch', onFetch).on('update', onUpdate);
|
||||
var handler = $scope.$watch.args[0][1];
|
||||
|
||||
return handler([ ], [ { meta: { disabled: true } } ])
|
||||
.then(function () {
|
||||
expect(onUpdate).to.have.property('callCount', 1);
|
||||
expect(onFetch).to.have.property('callCount', 0);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls nothing if there were no changes', function () {
|
||||
var onFetch = sinon.stub();
|
||||
var onUpdate = sinon.stub();
|
||||
|
||||
watchFilters($scope).on('fetch', onFetch).on('update', onUpdate);
|
||||
var handler = $scope.$watch.args[0][1];
|
||||
var cur = [];
|
||||
var prev = cur;
|
||||
|
||||
return Promise.try(handler, [cur, prev])
|
||||
.then(function () {
|
||||
expect(onUpdate).to.have.property('callCount', 0);
|
||||
expect(onFetch).to.have.property('callCount', 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
|
@ -1,18 +1,35 @@
|
|||
define(function (require) {
|
||||
var filterManager = require('components/filter_manager/filter_manager');
|
||||
var $state;
|
||||
var _ = require('lodash');
|
||||
var MockState = require('fixtures/mock_state');
|
||||
var filterManager;
|
||||
var appState;
|
||||
|
||||
describe('Filter Manager', function () {
|
||||
beforeEach(module('kibana'));
|
||||
|
||||
beforeEach(function () {
|
||||
$state = {
|
||||
filters: []
|
||||
};
|
||||
filterManager.init($state);
|
||||
|
||||
module('kibana/global_state', function ($provide) {
|
||||
$provide.service('getAppState', function () {
|
||||
return function () {
|
||||
return appState;
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should have an init function that sets the state to be used', function () {
|
||||
expect(filterManager.init).to.be.a(Function);
|
||||
filterManager.init($state);
|
||||
expect(filterManager.$state).to.be($state);
|
||||
beforeEach(function () {
|
||||
inject(function (Private) {
|
||||
filterManager = Private(require('components/filter_manager/filter_manager'));
|
||||
appState = new MockState();
|
||||
appState.filters = [];
|
||||
|
||||
// mock queryFilter's invertFilter since it's used in the manager
|
||||
var queryFilter = Private(require('components/filter_bar/query_filter'));
|
||||
queryFilter.invertFilter = function (filter) {
|
||||
filter.meta.negate = !filter.meta.negate;
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
it('should have an `add` function', function () {
|
||||
|
@ -20,35 +37,49 @@ define(function (require) {
|
|||
});
|
||||
|
||||
it('should add a filter', function () {
|
||||
expect($state.filters.length).to.be(0);
|
||||
expect(appState.filters.length).to.be(0);
|
||||
filterManager.add('myField', 1, '+', 'myIndex');
|
||||
expect($state.filters.length).to.be(1);
|
||||
expect(appState.filters.length).to.be(1);
|
||||
});
|
||||
|
||||
it('should add multiple filters if passed an array of values', function () {
|
||||
filterManager.add('myField', [1, 2, 3], '+', 'myIndex');
|
||||
expect($state.filters.length).to.be(3);
|
||||
expect(appState.filters.length).to.be(3);
|
||||
});
|
||||
|
||||
it('should add an exists filter if _exists_ is used as the field', function () {
|
||||
filterManager.add('_exists_', 'myField', '+', 'myIndex');
|
||||
expect($state.filters[0].exists).to.eql({field: 'myField'});
|
||||
expect(appState.filters[0].exists).to.eql({field: 'myField'});
|
||||
});
|
||||
|
||||
it('Should negate existing filter instead of added a conflicting filter', function () {
|
||||
filterManager.add('myField', 1, '+', 'myIndex');
|
||||
expect($state.filters.length).to.be(1);
|
||||
expect(appState.filters.length).to.be(1);
|
||||
filterManager.add('myField', 1, '-', 'myIndex');
|
||||
expect($state.filters.length).to.be(1);
|
||||
expect($state.filters[0].meta.negate).to.be(true);
|
||||
expect(appState.filters.length).to.be(1);
|
||||
expect(appState.filters[0].meta.negate).to.be(true);
|
||||
|
||||
filterManager.add('_exists_', 'myField', '+', 'myIndex');
|
||||
expect($state.filters.length).to.be(2);
|
||||
expect(appState.filters.length).to.be(2);
|
||||
filterManager.add('_exists_', 'myField', '-', 'myIndex');
|
||||
expect($state.filters.length).to.be(2);
|
||||
expect($state.filters[1].meta.negate).to.be(true);
|
||||
expect(appState.filters.length).to.be(2);
|
||||
expect(appState.filters[1].meta.negate).to.be(true);
|
||||
});
|
||||
|
||||
it('should enable matching filters being changed', function () {
|
||||
_.each([true, false], function (negate) {
|
||||
appState.filters = [{
|
||||
query: { match: { myField: { query: 1 } } },
|
||||
meta: { disabled: true, negate: negate }
|
||||
}];
|
||||
expect(appState.filters.length).to.be(1);
|
||||
expect(appState.filters[0].meta.disabled).to.be(true);
|
||||
|
||||
filterManager.add('myField', 1, '+', 'myIndex');
|
||||
expect(appState.filters.length).to.be(1);
|
||||
expect(appState.filters[0].meta.disabled).to.be(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
115
test/unit/specs/components/stringify/_conformance.js
Normal file
115
test/unit/specs/components/stringify/_conformance.js
Normal file
|
@ -0,0 +1,115 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
|
||||
var fieldFormats;
|
||||
var FieldFormat;
|
||||
var config;
|
||||
|
||||
var formatIds = [
|
||||
'bytes',
|
||||
'date',
|
||||
'ip',
|
||||
'number',
|
||||
'percent',
|
||||
'string',
|
||||
'url',
|
||||
'_source'
|
||||
];
|
||||
|
||||
return ['conformance', function () {
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
beforeEach(inject(function (Private, $injector) {
|
||||
fieldFormats = Private(require('registry/field_formats'));
|
||||
FieldFormat = Private(require('components/index_patterns/_field_format/FieldFormat'));
|
||||
config = $injector.get('config');
|
||||
}));
|
||||
|
||||
formatIds.forEach(function (id) {
|
||||
var instance;
|
||||
var Type;
|
||||
|
||||
beforeEach(function () {
|
||||
Type = fieldFormats.getType(id);
|
||||
instance = fieldFormats.getInstance(id);
|
||||
});
|
||||
|
||||
describe(id + ' Type', function () {
|
||||
it('has an id', function () {
|
||||
expect(Type.id).to.be.a('string');
|
||||
});
|
||||
|
||||
it('has a title', function () {
|
||||
expect(Type.title).to.be.a('string');
|
||||
});
|
||||
|
||||
it('declares compatible field formats as a string or array', function () {
|
||||
expect(Type.fieldType).to.be.ok();
|
||||
expect(_.isString(Type.fieldType) || _.isArray(Type.fieldType)).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe(id + ' Instance', function () {
|
||||
it('extends FieldFormat', function () {
|
||||
expect(instance).to.be.a(FieldFormat);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('registers all of the fieldFormats', function () {
|
||||
expect(_.difference(fieldFormats.raw, formatIds.map(fieldFormats.getType))).to.eql([]);
|
||||
});
|
||||
|
||||
describe('Bytes format', basicPatternTests('bytes', require('numeral')));
|
||||
describe('Percent Format', basicPatternTests('percent', require('numeral')));
|
||||
describe('Date Format', basicPatternTests('date', require('moment')));
|
||||
|
||||
describe('Number Format', function () {
|
||||
basicPatternTests('number', require('numeral'))();
|
||||
|
||||
it('tries to parse strings', function () {
|
||||
var number = new (fieldFormats.getType('number'))({ pattern: '0.0b' });
|
||||
expect(number.convert(123.456)).to.be('123.5B');
|
||||
expect(number.convert('123.456')).to.be('123.5B');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function basicPatternTests(id, lib) {
|
||||
var confKey = id === 'date' ? 'dateFormat' : 'format:' + id + ':defaultPattern';
|
||||
|
||||
return function () {
|
||||
it('converts using the format:' + id + ':defaultPattern config', function () {
|
||||
var inst = fieldFormats.getInstance(id);
|
||||
[
|
||||
'0b',
|
||||
'0 b',
|
||||
'0.[000] b',
|
||||
'0.[000]b',
|
||||
'0.[0]b'
|
||||
].forEach(function (pattern) {
|
||||
var num = _.random(-10000, 10000, true);
|
||||
config.set(confKey, pattern);
|
||||
expect(inst.convert(num)).to.be(lib(num).format(pattern));
|
||||
});
|
||||
});
|
||||
|
||||
it('uses the pattern param if available', function () {
|
||||
var num = _.random(-10000, 10000, true);
|
||||
var defFormat = '0b';
|
||||
var customFormat = '0.00000%';
|
||||
|
||||
config.set(confKey, defFormat);
|
||||
var defInst = fieldFormats.getInstance(id);
|
||||
|
||||
var Type = fieldFormats.getType(id);
|
||||
var customInst = new Type({ pattern: customFormat });
|
||||
|
||||
expect(defInst.convert(num)).to.not.be(customInst.convert(num));
|
||||
expect(defInst.convert(num)).to.be(lib(num).format(defFormat));
|
||||
expect(customInst.convert(num)).to.be(lib(num).format(customFormat));
|
||||
});
|
||||
};
|
||||
}
|
||||
}];
|
||||
});
|
16
test/unit/specs/components/stringify/_ip.js
Normal file
16
test/unit/specs/components/stringify/_ip.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
define(function (require) {
|
||||
return ['IP Address Format', function () {
|
||||
var fieldFormats;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
beforeEach(inject(function (Private) {
|
||||
fieldFormats = Private(require('registry/field_formats'));
|
||||
}));
|
||||
|
||||
it('convers a value from a decimal to a string', function () {
|
||||
var ip = fieldFormats.getInstance('ip');
|
||||
expect(ip.convert(1186489492)).to.be('70.184.100.148');
|
||||
});
|
||||
|
||||
}];
|
||||
});
|
34
test/unit/specs/components/stringify/_source.js
Normal file
34
test/unit/specs/components/stringify/_source.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
define(function (require) {
|
||||
return ['_source formatting', function () {
|
||||
var $ = require('jquery');
|
||||
var _ = require('lodash');
|
||||
|
||||
var fieldFormats;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
beforeEach(inject(function (Private) {
|
||||
fieldFormats = Private(require('registry/field_formats'));
|
||||
}));
|
||||
|
||||
describe('Source format', function () {
|
||||
var indexPattern;
|
||||
var hits;
|
||||
var format;
|
||||
var convertHtml;
|
||||
|
||||
beforeEach(inject(function (Private) {
|
||||
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
|
||||
hits = Private(require('fixtures/hits'));
|
||||
format = fieldFormats.getInstance('_source');
|
||||
convertHtml = format.getConverterFor('html');
|
||||
}));
|
||||
|
||||
it('uses the _source, field, and hit to create a <dl>', function () {
|
||||
var hit = _.first(hits);
|
||||
var $dl = $(convertHtml(hit._source, indexPattern.fields.byName._source, hit));
|
||||
expect($dl.is('dl')).to.be.ok();
|
||||
expect($dl.find('dt')).to.have.length(_.keys(indexPattern.flattenHit(hit)).length);
|
||||
});
|
||||
});
|
||||
}];
|
||||
});
|
119
test/unit/specs/components/stringify/_url.js
Normal file
119
test/unit/specs/components/stringify/_url.js
Normal file
|
@ -0,0 +1,119 @@
|
|||
define(function (require) {
|
||||
return ['Url Format', function () {
|
||||
var $ = require('jquery');
|
||||
var fieldFormats;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
beforeEach(inject(function (Private) {
|
||||
fieldFormats = Private(require('registry/field_formats'));
|
||||
}));
|
||||
|
||||
describe('Url Format', function () {
|
||||
var Url;
|
||||
|
||||
beforeEach(function () {
|
||||
Url = fieldFormats.getType('url');
|
||||
});
|
||||
|
||||
it('ouputs a simple <a> tab by default', function () {
|
||||
var url = new Url();
|
||||
|
||||
var $a = $(url.convert('http://elastic.co', 'html'));
|
||||
expect($a.is('a')).to.be(true);
|
||||
expect($a.size()).to.be(1);
|
||||
expect($a.attr('href')).to.be('http://elastic.co');
|
||||
expect($a.attr('target')).to.be('_blank');
|
||||
expect($a.children().size()).to.be(0);
|
||||
});
|
||||
|
||||
it('outputs an <image> if type === "img"', function () {
|
||||
var url = new Url({ type: 'img' });
|
||||
|
||||
var $img = $(url.convert('http://elastic.co', 'html'));
|
||||
expect($img.is('img')).to.be(true);
|
||||
expect($img.attr('src')).to.be('http://elastic.co');
|
||||
});
|
||||
|
||||
describe('url template', function () {
|
||||
it('accepts a template', function () {
|
||||
var url = new Url({ urlTemplate: 'url: {{ value }}' });
|
||||
var $a = $(url.convert('url', 'html'));
|
||||
expect($a.is('a')).to.be(true);
|
||||
expect($a.size()).to.be(1);
|
||||
expect($a.attr('href')).to.be('url: url');
|
||||
expect($a.attr('target')).to.be('_blank');
|
||||
expect($a.children().size()).to.be(0);
|
||||
});
|
||||
|
||||
it('only outputs the url if the contentType === "text"', function () {
|
||||
var url = new Url();
|
||||
expect(url.convert('url', 'text')).to.be('url');
|
||||
});
|
||||
});
|
||||
|
||||
describe('label template', function () {
|
||||
it('accepts a template', function () {
|
||||
var url = new Url({ labelTemplate: 'extension: {{ value }}' });
|
||||
var $a = $(url.convert('php', 'html'));
|
||||
expect($a.is('a')).to.be(true);
|
||||
expect($a.size()).to.be(1);
|
||||
expect($a.attr('href')).to.be('php');
|
||||
expect($a.html()).to.be('extension: php');
|
||||
});
|
||||
|
||||
it('uses the label template for text formating', function () {
|
||||
var url = new Url({ labelTemplate: 'external {{value }}'});
|
||||
expect(url.convert('url', 'text')).to.be('external url');
|
||||
});
|
||||
|
||||
it('can use the raw value', function () {
|
||||
var url = new Url({
|
||||
labelTemplate: 'external {{value}}'
|
||||
});
|
||||
expect(url.convert('url?', 'text')).to.be('external url?');
|
||||
});
|
||||
|
||||
it('can use the url', function () {
|
||||
var url = new Url({
|
||||
urlTemplate: 'http://google.com/{{value}}',
|
||||
labelTemplate: 'external {{url}}'
|
||||
});
|
||||
expect(url.convert('url?', 'text')).to.be('external http://google.com/url%3F');
|
||||
});
|
||||
});
|
||||
|
||||
describe('templating', function () {
|
||||
it('ignores unknown variables', function () {
|
||||
var url = new Url({ urlTemplate: '{{ not really a var }}' });
|
||||
expect(url.convert('url', 'text')).to.be('');
|
||||
});
|
||||
|
||||
it('does not allow executing code in variable expressions', function () {
|
||||
window.SHOULD_NOT_BE_TRUE = false;
|
||||
var url = new Url({ urlTemplate: '{{ (window.SHOULD_NOT_BE_TRUE = true) && value }}' });
|
||||
expect(url.convert('url', 'text')).to.be('');
|
||||
});
|
||||
|
||||
describe('', function () {
|
||||
before(function () {
|
||||
Object.prototype.cantStopMeNow = {
|
||||
toString: function () {
|
||||
return 'fail';
|
||||
},
|
||||
cantStopMeNow: null
|
||||
};
|
||||
});
|
||||
|
||||
it('does not get values from the prototype chain', function () {
|
||||
var url = new Url({ urlTemplate: '{{ cantStopMeNow }}' });
|
||||
expect(url.convert('url', 'text')).to.be('');
|
||||
});
|
||||
|
||||
after(function () {
|
||||
delete Object.prototype.cantStopMeNow;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}];
|
||||
});
|
|
@ -1,229 +1,12 @@
|
|||
define(function (require) {
|
||||
describe('Stringify Component', function () {
|
||||
var _ = require('lodash');
|
||||
var $ = require('jquery');
|
||||
run(require('specs/components/stringify/_conformance'));
|
||||
run(require('specs/components/stringify/_ip'));
|
||||
run(require('specs/components/stringify/_source'));
|
||||
run(require('specs/components/stringify/_url'));
|
||||
|
||||
var fieldFormats;
|
||||
var FieldFormat;
|
||||
var config;
|
||||
var $rootScope;
|
||||
|
||||
var formatIds = [
|
||||
'bytes',
|
||||
'date',
|
||||
'ip',
|
||||
'number',
|
||||
'percent',
|
||||
'string',
|
||||
'url',
|
||||
'_source'
|
||||
];
|
||||
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
beforeEach(inject(function (Private, $injector) {
|
||||
fieldFormats = Private(require('registry/field_formats'));
|
||||
FieldFormat = Private(require('components/index_patterns/_field_format/FieldFormat'));
|
||||
config = $injector.get('config');
|
||||
$rootScope = $injector.get('$rootScope');
|
||||
}));
|
||||
|
||||
it('registers all of the fieldFormats', function () {
|
||||
expect(_.difference(fieldFormats.raw, formatIds.map(fieldFormats.getType))).to.eql([]);
|
||||
});
|
||||
|
||||
describe('conformance', function () {
|
||||
formatIds.forEach(function (id) {
|
||||
var instance;
|
||||
var Type;
|
||||
|
||||
beforeEach(function () {
|
||||
Type = fieldFormats.getType(id);
|
||||
instance = fieldFormats.getInstance(id);
|
||||
});
|
||||
|
||||
describe(id + ' Type', function () {
|
||||
it('has an id', function () {
|
||||
expect(Type.id).to.be.a('string');
|
||||
});
|
||||
|
||||
it('has a title', function () {
|
||||
expect(Type.title).to.be.a('string');
|
||||
});
|
||||
|
||||
it('declares compatible field formats as a string or array', function () {
|
||||
expect(Type.fieldType).to.be.ok();
|
||||
expect(_.isString(Type.fieldType) || _.isArray(Type.fieldType)).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe(id + ' Instance', function () {
|
||||
it('extends FieldFormat', function () {
|
||||
expect(instance).to.be.a(FieldFormat);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Bytes format', basicPatternTests('bytes', require('numeral')));
|
||||
describe('Percent Format', basicPatternTests('percent', require('numeral')));
|
||||
describe('Date Format', basicPatternTests('date', require('moment')));
|
||||
|
||||
describe('Number Format', function () {
|
||||
basicPatternTests('number', require('numeral'))();
|
||||
|
||||
it('tries to parse strings', function () {
|
||||
var number = new (fieldFormats.getType('number'))({ pattern: '0.0b' });
|
||||
expect(number.convert(123.456)).to.be('123.5B');
|
||||
expect(number.convert('123.456')).to.be('123.5B');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function basicPatternTests(id, lib) {
|
||||
var confKey = id === 'date' ? 'dateFormat' : 'format:' + id + ':defaultPattern';
|
||||
|
||||
return function () {
|
||||
it('converts using the format:' + id + ':defaultPattern config', function () {
|
||||
var inst = fieldFormats.getInstance(id);
|
||||
[
|
||||
'0b',
|
||||
'0 b',
|
||||
'0.[000] b',
|
||||
'0.[000]b',
|
||||
'0.[0]b'
|
||||
].forEach(function (pattern) {
|
||||
var num = _.random(-10000, 10000, true);
|
||||
config.set(confKey, pattern);
|
||||
expect(inst.convert(num)).to.be(lib(num).format(pattern));
|
||||
});
|
||||
});
|
||||
|
||||
it('uses the pattern param if available', function () {
|
||||
var num = _.random(-10000, 10000, true);
|
||||
var defFormat = '0b';
|
||||
var customFormat = '0.00000%';
|
||||
|
||||
config.set(confKey, defFormat);
|
||||
var defInst = fieldFormats.getInstance(id);
|
||||
|
||||
var Type = fieldFormats.getType(id);
|
||||
var customInst = new Type({ pattern: customFormat });
|
||||
|
||||
expect(defInst.convert(num)).to.not.be(customInst.convert(num));
|
||||
expect(defInst.convert(num)).to.be(lib(num).format(defFormat));
|
||||
expect(customInst.convert(num)).to.be(lib(num).format(customFormat));
|
||||
});
|
||||
};
|
||||
function run(suite) {
|
||||
describe(suite[0], suite[1]);
|
||||
}
|
||||
|
||||
describe('Ip format', function () {
|
||||
it('convers a value from a decimal to a string', function () {
|
||||
var ip = fieldFormats.getInstance('ip');
|
||||
expect(ip.convert(1186489492)).to.be('70.184.100.148');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Url Format', function () {
|
||||
var Url;
|
||||
|
||||
beforeEach(function () {
|
||||
Url = fieldFormats.getType('url');
|
||||
});
|
||||
|
||||
it('ouputs a simple <a> tab by default', function () {
|
||||
var url = new Url();
|
||||
|
||||
var $a = $(url.convert('http://elastic.co', 'html'));
|
||||
expect($a.is('a')).to.be(true);
|
||||
expect($a.size()).to.be(1);
|
||||
expect($a.attr('href')).to.be('http://elastic.co');
|
||||
expect($a.attr('target')).to.be('_blank');
|
||||
expect($a.children().size()).to.be(0);
|
||||
});
|
||||
|
||||
it('outputs an <image> if type === "img"', function () {
|
||||
var url = new Url({ type: 'img' });
|
||||
|
||||
var $img = $(url.convert('http://elastic.co', 'html'));
|
||||
expect($img.is('img')).to.be(true);
|
||||
expect($img.attr('src')).to.be('http://elastic.co');
|
||||
});
|
||||
|
||||
it('only outputs the url if the contentType === "text"', function () {
|
||||
var url = new Url();
|
||||
expect(url.convert('url', 'text')).to.be('url');
|
||||
});
|
||||
|
||||
describe('template', function () {
|
||||
|
||||
it('accepts a template', function () {
|
||||
var url = new Url({ template: 'url: {{ value }}' });
|
||||
var $a = $(url.convert('url', 'html'));
|
||||
expect($a.is('a')).to.be(true);
|
||||
expect($a.size()).to.be(1);
|
||||
expect($a.attr('href')).to.be('url: url');
|
||||
expect($a.attr('target')).to.be('_blank');
|
||||
expect($a.children().size()).to.be(0);
|
||||
});
|
||||
|
||||
it('renders for text contentType', function () {
|
||||
var url = new Url({ template: 'url: {{ value }}' });
|
||||
expect(url.convert('url', 'text')).to.be('url: url');
|
||||
});
|
||||
|
||||
it('ignores unknown variables', function () {
|
||||
var url = new Url({ template: '{{ not really a var }}' });
|
||||
expect(url.convert('url', 'text')).to.be('');
|
||||
});
|
||||
|
||||
it('does not allow executing code in variable expressions', function () {
|
||||
window.SHOULD_NOT_BE_TRUE = false;
|
||||
var url = new Url({ template: '{{ (window.SHOULD_NOT_BE_TRUE = true) && value }}' });
|
||||
expect(url.convert('url', 'text')).to.be('');
|
||||
});
|
||||
|
||||
describe('', function () {
|
||||
before(function () {
|
||||
Object.prototype.cantStopMeNow = {
|
||||
toString: function () {
|
||||
return 'fail';
|
||||
},
|
||||
cantStopMeNow: null
|
||||
};
|
||||
});
|
||||
|
||||
it('does not get values from the prototype chain', function () {
|
||||
var url = new Url({ template: '{{ cantStopMeNow }}' });
|
||||
expect(url.convert('url', 'text')).to.be('');
|
||||
});
|
||||
|
||||
after(function () {
|
||||
delete Object.prototype.cantStopMeNow;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Source format', function () {
|
||||
var indexPattern;
|
||||
var hits;
|
||||
var format;
|
||||
var convertHtml;
|
||||
|
||||
beforeEach(inject(function (Private) {
|
||||
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
|
||||
hits = Private(require('fixtures/hits'));
|
||||
format = fieldFormats.getInstance('_source');
|
||||
convertHtml = format.getConverterFor('html');
|
||||
}));
|
||||
|
||||
it('uses the _source, field, and hit to create a <dl>', function () {
|
||||
var hit = _.first(hits);
|
||||
var $dl = $(convertHtml(hit._source, indexPattern.fields.byName._source, hit));
|
||||
expect($dl.is('dl')).to.be.ok();
|
||||
expect($dl.find('dt')).to.have.length(_.keys(indexPattern.flattenHit(hit)).length);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
describe('$scope.$watchMulti', function () {
|
||||
var sinon = require('test_utils/auto_release_sinon');
|
||||
|
||||
|
@ -11,119 +12,189 @@ define(function (require) {
|
|||
$scope = $rootScope.$new();
|
||||
}));
|
||||
|
||||
it('exposes $watchMulti on all scopes', function () {
|
||||
expect($rootScope.$watchMulti).to.be.a('function');
|
||||
expect($scope).to.have.property('$watchMulti', $rootScope.$watchMulti);
|
||||
describe('basic functionality', function () {
|
||||
it('exposes $watchMulti on all scopes', function () {
|
||||
expect($rootScope.$watchMulti).to.be.a('function');
|
||||
expect($scope).to.have.property('$watchMulti', $rootScope.$watchMulti);
|
||||
|
||||
var $isoScope = $scope.$new(true);
|
||||
expect($isoScope).to.have.property('$watchMulti', $rootScope.$watchMulti);
|
||||
});
|
||||
|
||||
it('only triggers a single watch on initialization', function () {
|
||||
var stub = sinon.stub();
|
||||
|
||||
$scope.$watchMulti([
|
||||
'one',
|
||||
'two',
|
||||
'three'
|
||||
], stub);
|
||||
$rootScope.$apply();
|
||||
|
||||
expect(stub.callCount).to.be(1);
|
||||
});
|
||||
|
||||
it('only triggers a single watch when multiple values change', function () {
|
||||
var stub = sinon.spy(function (a, b) {});
|
||||
|
||||
$scope.$watchMulti([
|
||||
'one',
|
||||
'two',
|
||||
'three'
|
||||
], stub);
|
||||
|
||||
$rootScope.$apply();
|
||||
expect(stub.callCount).to.be(1);
|
||||
|
||||
$scope.one = 'a';
|
||||
$scope.two = 'b';
|
||||
$scope.three = 'c';
|
||||
$rootScope.$apply();
|
||||
|
||||
expect(stub.callCount).to.be(2);
|
||||
});
|
||||
|
||||
it('passes an array of the current values as the first arg, and an array of the previous values as the second',
|
||||
function () {
|
||||
var stub = sinon.spy(function (a, b) {});
|
||||
|
||||
$scope.one = 'a';
|
||||
$scope.two = 'b';
|
||||
$scope.three = 'c';
|
||||
$scope.$watchMulti([
|
||||
'one',
|
||||
'two',
|
||||
'three'
|
||||
], stub);
|
||||
|
||||
$rootScope.$apply();
|
||||
expect(stub.firstCall.args).to.eql([
|
||||
['a', 'b', 'c'],
|
||||
['a', 'b', 'c']
|
||||
]);
|
||||
|
||||
$scope.one = 'do';
|
||||
$scope.two = 're';
|
||||
$scope.three = 'mi';
|
||||
$rootScope.$apply();
|
||||
|
||||
expect(stub.secondCall.args).to.eql([
|
||||
['do', 're', 'mi'],
|
||||
['a', 'b', 'c']
|
||||
]);
|
||||
});
|
||||
|
||||
it('the current value is always up to date', function () {
|
||||
var count = 0;
|
||||
|
||||
$scope.vals = [1, 0];
|
||||
$scope.$watchMulti([ 'vals[0]', 'vals[1]' ], function (cur, prev) {
|
||||
expect(cur).to.eql($scope.vals);
|
||||
count++;
|
||||
var $isoScope = $scope.$new(true);
|
||||
expect($isoScope).to.have.property('$watchMulti', $rootScope.$watchMulti);
|
||||
});
|
||||
|
||||
var $child = $scope.$new();
|
||||
$child.$watch('vals[0]', function (cur) {
|
||||
$child.vals[1] = cur;
|
||||
});
|
||||
it('returns a working unwatch function', function () {
|
||||
$scope.a = 0;
|
||||
$scope.b = 0;
|
||||
var triggers = 0;
|
||||
var unwatch = $scope.$watchMulti(['a', 'b'], function () { triggers++; });
|
||||
|
||||
$rootScope.$apply();
|
||||
expect(count).to.be(2);
|
||||
// initial watch
|
||||
$scope.$apply();
|
||||
expect(triggers).to.be(1);
|
||||
|
||||
// prove that it triggers on chagne
|
||||
$scope.a++;
|
||||
$scope.$apply();
|
||||
expect(triggers).to.be(2);
|
||||
|
||||
// remove watchers
|
||||
expect($scope.$$watchers).to.not.eql([]);
|
||||
unwatch();
|
||||
expect($scope.$$watchers).to.eql([]);
|
||||
|
||||
// prove that it doesn't trigger anymore
|
||||
$scope.a++;
|
||||
$scope.$apply();
|
||||
expect(triggers).to.be(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a working unwatch function', function () {
|
||||
$scope.a = 0;
|
||||
$scope.b = 0;
|
||||
var triggers = 0;
|
||||
var unwatch = $scope.$watchMulti(['a', 'b'], function () { triggers++; });
|
||||
describe('simple scope watchers', function () {
|
||||
it('only triggers a single watch on initialization', function () {
|
||||
var stub = sinon.stub();
|
||||
|
||||
// initial watch
|
||||
$scope.$apply();
|
||||
expect(triggers).to.be(1);
|
||||
$scope.$watchMulti([
|
||||
'one',
|
||||
'two',
|
||||
'three'
|
||||
], stub);
|
||||
$rootScope.$apply();
|
||||
|
||||
// prove that it triggers on chagne
|
||||
$scope.a++;
|
||||
$scope.$apply();
|
||||
expect(triggers).to.be(2);
|
||||
expect(stub.callCount).to.be(1);
|
||||
});
|
||||
|
||||
// remove watchers
|
||||
expect($scope.$$watchers).to.not.eql([]);
|
||||
unwatch();
|
||||
expect($scope.$$watchers).to.eql([]);
|
||||
it('only triggers a single watch when multiple values change', function () {
|
||||
var stub = sinon.spy(function (a, b) {});
|
||||
|
||||
// prove that it doesn't trigger anymore
|
||||
$scope.a++;
|
||||
$scope.$apply();
|
||||
expect(triggers).to.be(2);
|
||||
$scope.$watchMulti([
|
||||
'one',
|
||||
'two',
|
||||
'three'
|
||||
], stub);
|
||||
|
||||
$rootScope.$apply();
|
||||
expect(stub.callCount).to.be(1);
|
||||
|
||||
$scope.one = 'a';
|
||||
$scope.two = 'b';
|
||||
$scope.three = 'c';
|
||||
$rootScope.$apply();
|
||||
|
||||
expect(stub.callCount).to.be(2);
|
||||
});
|
||||
|
||||
it('passes an array of the current and previous values, in order',
|
||||
function () {
|
||||
var stub = sinon.spy(function (a, b) {});
|
||||
|
||||
$scope.one = 'a';
|
||||
$scope.two = 'b';
|
||||
$scope.three = 'c';
|
||||
$scope.$watchMulti([
|
||||
'one',
|
||||
'two',
|
||||
'three'
|
||||
], stub);
|
||||
|
||||
$rootScope.$apply();
|
||||
expect(stub.firstCall.args).to.eql([
|
||||
['a', 'b', 'c'],
|
||||
['a', 'b', 'c']
|
||||
]);
|
||||
|
||||
$scope.one = 'do';
|
||||
$scope.two = 're';
|
||||
$scope.three = 'mi';
|
||||
$rootScope.$apply();
|
||||
|
||||
expect(stub.secondCall.args).to.eql([
|
||||
['do', 're', 'mi'],
|
||||
['a', 'b', 'c']
|
||||
]);
|
||||
});
|
||||
|
||||
it('always has an up to date value', function () {
|
||||
var count = 0;
|
||||
|
||||
$scope.vals = [1, 0];
|
||||
$scope.$watchMulti([ 'vals[0]', 'vals[1]' ], function (cur, prev) {
|
||||
expect(cur).to.eql($scope.vals);
|
||||
count++;
|
||||
});
|
||||
|
||||
var $child = $scope.$new();
|
||||
$child.$watch('vals[0]', function (cur) {
|
||||
$child.vals[1] = cur;
|
||||
});
|
||||
|
||||
$rootScope.$apply();
|
||||
expect(count).to.be(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('complex watch expressions', function () {
|
||||
var stateWatchers;
|
||||
var firstValue;
|
||||
var secondValue;
|
||||
|
||||
beforeEach(function () {
|
||||
var firstGetter = function () {
|
||||
return firstValue;
|
||||
};
|
||||
|
||||
var secondGetter = function () {
|
||||
return secondValue;
|
||||
};
|
||||
|
||||
stateWatchers = [{
|
||||
fn: $rootScope.$watch,
|
||||
get: firstGetter
|
||||
}, {
|
||||
fn: $rootScope.$watch,
|
||||
get: secondGetter
|
||||
}];
|
||||
});
|
||||
|
||||
it('should trigger the watcher on initialization', function () {
|
||||
var stub = sinon.stub();
|
||||
firstValue = 'first';
|
||||
secondValue = 'second';
|
||||
|
||||
$scope.$watchMulti(stateWatchers, stub);
|
||||
$rootScope.$apply();
|
||||
|
||||
expect(stub.callCount).to.be(1);
|
||||
|
||||
expect(stub.firstCall.args[0]).to.eql([firstValue, secondValue]);
|
||||
expect(stub.firstCall.args[1]).to.eql([firstValue, secondValue]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('nested watchers', function () {
|
||||
it('should trigger the handler at least once', function () {
|
||||
var $scope = $rootScope.$new();
|
||||
$scope.$$watchers = [{
|
||||
get: _.noop,
|
||||
fn: _.noop,
|
||||
eq: false,
|
||||
last: false
|
||||
}, {
|
||||
get: _.noop,
|
||||
fn: registerWatchers,
|
||||
eq: false,
|
||||
last: false
|
||||
}];
|
||||
|
||||
var first = sinon.stub();
|
||||
var second = sinon.stub();
|
||||
|
||||
function registerWatchers() {
|
||||
$scope.$watchMulti([first, second], function () {
|
||||
expect(first.callCount).to.be.greaterThan(0);
|
||||
expect(second.callCount).to.be.greaterThan(0);
|
||||
});
|
||||
}
|
||||
$scope.$digest();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,6 +2,7 @@ define(function (require) {
|
|||
var sinon = require('test_utils/auto_release_sinon');
|
||||
var faker = require('faker');
|
||||
var _ = require('lodash');
|
||||
var MockState = require('fixtures/mock_state');
|
||||
|
||||
// global vars, injected and mocked in init()
|
||||
var kbnUrl;
|
||||
|
@ -29,8 +30,7 @@ define(function (require) {
|
|||
});
|
||||
|
||||
$provide.service('globalState', function () {
|
||||
globalStateMock = {};
|
||||
globalStateMock.on = globalStateMock.off = _.noop;
|
||||
globalStateMock = new MockState();
|
||||
globalStateMock.removeFromUrl = function (url) {
|
||||
return url;
|
||||
};
|
||||
|
|
15
test/unit/specs/utils/mixins/_push_all.js
Normal file
15
test/unit/specs/utils/mixins/_push_all.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
define(function (require) {
|
||||
return ['_.pushAll', function () {
|
||||
var _ = require('lodash');
|
||||
|
||||
it('pushes an entire array into another', function () {
|
||||
var a = [1, 2, 3, 4];
|
||||
var b = [5, 6, 7, 8];
|
||||
|
||||
var output = _.pushAll(b, a);
|
||||
expect(output).to.be(a);
|
||||
expect(a).to.eql([1, 2, 3, 4, 5, 6, 7, 8]);
|
||||
expect(b).to.eql([5, 6, 7, 8]);
|
||||
});
|
||||
}];
|
||||
});
|
|
@ -2,6 +2,7 @@ define(function (require) {
|
|||
describe('lodash mixins', function () {
|
||||
run(require('specs/utils/mixins/_move'));
|
||||
run(require('specs/utils/mixins/_organize_by'));
|
||||
run(require('specs/utils/mixins/_push_all'));
|
||||
function run(m) { describe(m[0], m[1]); }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue