Minimal field_chooser tests

This commit is contained in:
Rashid Khan 2014-09-12 09:19:45 -07:00
parent 82e58269ec
commit d0a8d5d495
17 changed files with 728 additions and 142 deletions

View file

@ -1,8 +1,8 @@
define(function (require) {
var $ = require('jquery');
var app = require('modules').get('apps/discover');
var html = require('text!apps/discover/partials/discover_field.html');
var detailsHtml = require('text!apps/discover/partials/discover_field_details.html');
var html = require('text!apps/discover/components/field_chooser/discover_field.html');
var detailsHtml = require('text!apps/discover/components/field_chooser/discover_field_details.html');
var _ = require('lodash');
require('directives/css_truncate');

View file

@ -15,7 +15,7 @@
ng-click="showFields = !showFields"
class="fa visible-xs visible-sm pull-right field-collapse-toggle" ></i>
<button
ng-class="{ 'btn-defualt': !filter.active, 'btn-success': filter.active, 'hidden-xs': !showFields, 'hidden-sm': !showFields }"
ng-class="{ 'btn-default': !filter.active, 'btn-success': filter.active, 'hidden-xs': !showFields, 'hidden-sm': !showFields }"
class="btn btn-xs btn-default pull-right discover-field-filter-toggle"
ng-click="showFilter = !showFilter">
<i class="fa fa-gear"></i>
@ -33,7 +33,6 @@
Analyzed
</label>
<select
required
ng-options="opt.value as opt.label for opt in filter.boolOpts"
ng-model="filter.vals.analyzed"
class="form-control">
@ -44,7 +43,6 @@
Indexed
</label>
<select
required
ng-options="opt.value as opt.label for opt in filter.boolOpts"
ng-model="filter.vals.indexed"
class="form-control">
@ -55,7 +53,6 @@
Type
</label>
<select
required
ng-options="field as field for field in fieldTypes"
ng-model="filter.vals.type"
class="form-control">
@ -84,7 +81,7 @@
<ul bindonce
ng-show="(fields | filter:filter.popularity).length > 0"
class="list-unstyled sidebar-well" ng-class="{ 'hidden-sm': !showFields, 'hidden-xs': !showFields }">
class="list-unstyled sidebar-well discover-popular-fields" ng-class="{ 'hidden-sm': !showFields, 'hidden-xs': !showFields }">
<li class="sidebar-item sidebar-list-header"><h6>Popular fields</h6></li>
<discover-field
ng-repeat="field in popularFields | filter:filter.isFieldFiltered"
@ -93,7 +90,7 @@
</discover-field>
</ul>
<ul bindonce class="list-unstyled" ng-class="{ 'hidden-sm': !showFields, 'hidden-xs': !showFields }">
<ul bindonce class="list-unstyled discover-unpopular-fields" ng-class="{ 'hidden-sm': !showFields, 'hidden-xs': !showFields }">
<discover-field
ng-repeat="field in unpopularFields | filter:filter.isFieldFiltered"
field="field"

View file

@ -1,17 +1,17 @@
define(function (require) {
var app = require('modules').get('apps/discover');
var html = require('text!apps/discover/partials/field_chooser.html');
var html = require('text!apps/discover/components/field_chooser/field_chooser.html');
var _ = require('lodash');
var jsonPath = require('jsonpath');
var rison = require('utils/rison');
var qs = require('utils/query_string');
var fieldCalculator = require('apps/discover/components/field_chooser/lib/field_calculator');
require('directives/css_truncate');
require('directives/field_name');
require('filters/unique');
require('apps/discover/directives/discover_field');
require('apps/discover/components/field_chooser/discover_field');
app.directive('discFieldChooser', function ($location, globalState, config) {
return {
@ -21,7 +21,7 @@ define(function (require) {
toggle: '=',
data: '=',
state: '=',
searchSource: '=',
indexPattern: '=',
updateFilterInQuery: '=filter'
},
template: html,
@ -104,8 +104,7 @@ define(function (require) {
});
$scope.increaseFieldCounter = function (field) {
var indexPattern = $scope.searchSource.get('index');
indexPattern.popularizeField(field.name, 1);
$scope.indexPattern.popularizeField(field.name, 1);
field.count++;
};
@ -113,7 +112,7 @@ define(function (require) {
var agg = {};
// If we're visualizing a date field, and our index is time based (and thus has a time filter),
// then run a date histogram
if (field.type === 'date' && $scope.searchSource.get('index').timeFieldName) {
if (field.type === 'date' && $scope.indexPattern.timeFieldName) {
agg = {
type: 'date_histogram',
schema: 'segment',
@ -134,7 +133,6 @@ define(function (require) {
}
$location.path('/visualize/create').search({
//(query:(query_string:(query:'*')),vis:(aggs:!((params:(field:'@tags',order:desc,size:5)
indexPattern: $scope.state.index,
type: 'histogram',
_a: rison.encode({
@ -158,7 +156,7 @@ define(function (require) {
$scope.details = function (field, recompute) {
if (_.isUndefined(field.details) || recompute) {
field.details = getFieldValueCounts({
field.details = fieldCalculator.getFieldValueCounts({
data: $scope.data,
field: field,
count: 5,
@ -170,102 +168,6 @@ define(function (require) {
}
};
var getFieldValues = function (data, field) {
var name = field.name;
var normalize = field.format && field.format.normalize;
return _.map(data, function (row) {
var val;
val = _.isUndefined(row._source[name]) ? row[name] : row._source[name];
// for fields that come back in weird formats like geo_point
if (val != null && normalize) val = normalize(val);
return val;
});
};
var getFieldValueCounts = function (params) {
params = _.defaults(params, {
count: 5,
grouped: false
});
if (
params.field.type === 'geo_point'
|| params.field.type === 'geo_shape'
|| params.field.type === 'attachment'
) {
return { error: 'Analysis is not available for geo fields.' };
}
var allValues = getFieldValues(params.data, params.field),
groups = {},
hasArrays = false,
exists = 0,
missing = 0,
counts;
var value, k;
for (var i = 0; i < allValues.length; ++i) {
value = allValues[i];
if (_.isUndefined(value)) {
missing++;
}
if (_.isArray(value)) {
hasArrays = true;
}
else if (_.isObject(value)) {
return { error: 'Analysis is not available for object fields' };
}
if (_.isArray(value) && !params.grouped) {
k = value;
} else {
k = _.isUndefined(value) ? '' : [value.toString()];
}
/* jshint -W083 */
_.each(k, function (key) {
if (_.has(groups, key)) {
groups[key].count++;
} else {
groups[key] = {
value: (params.grouped ? value : key),
count: 1
};
}
});
}
counts = _.map(
_.sortBy(groups, 'count').reverse().slice(0, params.count),
function (bucket) {
return {
value: bucket.value,
count: bucket.count,
percent: (bucket.count / (params.data.length - missing) * 100).toFixed(1)
};
});
if (params.data.length - missing === 0) {
return {error: 'This is field is present in your elasticsearch mapping,' +
' but not in any documents in the search results. You may still be able to visualize or search on it'};
}
return {
total: params.data.length,
exists: params.data.length - missing,
missing: missing,
buckets: counts,
hasArrays : hasArrays,
};
};
}
};
});

View file

@ -0,0 +1,114 @@
define(function (require) {
var _ = require('lodash');
var getFieldValues = function (data, field) {
var name = field.name;
var normalize = field.format && field.format.normalize;
return _.map(data, function (row) {
var val;
val = _.isUndefined(row._source[name]) ? row[name] : row._source[name];
// for fields that come back in weird formats like geo_point
if (val != null && normalize) val = normalize(val);
return val;
});
};
var getFieldValueCounts = function (params) {
params = _.defaults(params, {
count: 5,
grouped: false
});
if (
params.field.type === 'geo_point'
|| params.field.type === 'geo_shape'
|| params.field.type === 'attachment'
) {
return { error: 'Analysis is not available for geo fields.' };
}
var allValues = getFieldValues(params.data, params.field),
exists = 0,
counts;
var missing = _countMissing(allValues);
try {
var groups = _groupValues(allValues, params);
counts = _.map(
_.sortBy(groups, 'count').reverse().slice(0, params.count),
function (bucket) {
return {
value: bucket.value,
count: bucket.count,
percent: (bucket.count / (params.data.length - missing) * 100).toFixed(1)
};
});
if (params.data.length - missing === 0) {
return {error: 'This is field is present in your elasticsearch mapping,' +
' but not in any documents in the search results. You may still be able to visualize or search on it'};
}
return {
total: params.data.length,
exists: params.data.length - missing,
missing: missing,
buckets: counts,
};
} catch (e) {
return { error: e };
}
};
// returns a count of fields in the array that are undefined or null
var _countMissing = function (array) {
return array.length - _.without(array, undefined, null).length;
};
var _groupValues = function (allValues, params) {
var groups = {},
value, k;
for (var i = 0; i < allValues.length; ++i) {
value = allValues[i];
if (_.isObject(value) && !_.isArray(value)) {
throw new Error('Analysis is not available for object fields');
}
if (_.isArray(value) && !params.grouped) {
k = value;
} else {
k = _.isUndefined(value) || _.isNull(value) ? undefined : [value.toString()];
}
/* jshint -W083 */
_.each(k, function (key) {
if (_.has(groups, key)) {
groups[key].count++;
} else {
groups[key] = {
value: (params.grouped ? value : key),
count: 1
};
}
});
}
return groups;
};
return {
_groupValues: _groupValues,
_countMissing: _countMissing,
getFieldValues: getFieldValues,
getFieldValueCounts: getFieldValueCounts
};
});

View file

@ -307,7 +307,6 @@ define(function (require) {
if (sortFn && hit._formatted) return;
// Flatten the fields
var indexPattern = $scope.searchSource.get('index');
hit._source = indexPattern.flattenSearchResponse(hit._source);
@ -328,6 +327,7 @@ define(function (require) {
});
// apply the field counts to the field list
// We could do this in the field_chooser but it would us to iterate the array again
$scope.fields.forEach(function (field) {
field.rowCount = counts[field.name] || 0;
});

View file

@ -44,7 +44,7 @@
refresh="refreshFieldList"
data="rows"
filter="filterQuery"
search-source="searchSource"
index-pattern="searchSource.get('index')"
state="state">
</disc-field-chooser>
</div>

View file

@ -2,7 +2,7 @@ define(function (require, module, exports) {
require('apps/discover/directives/table');
require('apps/discover/saved_searches/saved_searches');
require('apps/discover/directives/timechart');
require('apps/discover/directives/field_chooser');
require('apps/discover/components/field_chooser/field_chooser');
require('apps/discover/controllers/discover');
require('css!apps/discover/styles/main.css');
});

View file

@ -0,0 +1,15 @@
define(function (require) {
var _ = require('lodash');
var longString = Array(200).join('_');
return function (id, mapping) {
var columns = _.keys(mapping);
return {
_formatted: _.zipObject(_.map(columns, function (c) { return [c, c + '_formatted_' + id + longString]; })),
_source: _.zipObject(_.map(columns, function (c) { return [c, c + '_original_' + id + longString]; })),
_id: id,
_index: 'test',
sort: [id]
};
};
});

View file

@ -0,0 +1,22 @@
define(function (require) {
var _ = require('lodash');
return _.map([
{_source: {timestamp: 0, bytes: 10, request: 'foo'}},
{_source: {timestamp: 1, bytes: 20, request: 'bar'}},
{_source: {timestamp: 2, bytes: 30, request: 'bar'}},
{_source: {timestamp: 3, bytes: 30, request: 'baz'}},
{_source: {timestamp: 4, bytes: 30, request: 'baz'}},
{_source: {timestamp: 5, bytes: 30, request: 'baz'}},
{_source: {timestamp: 6, bytes: 40, request: 'bat'}},
{_source: {timestamp: 7, bytes: 40, request: 'bat'}},
{_source: {timestamp: 8, bytes: 40, request: 'bat'}},
{_source: {timestamp: 9, bytes: 40, request: 'bat'}},
], function (p, i) {
return _.merge({}, p, {
_score: 1,
_id: 1000 + i,
_type: 'test',
_index: 'test-index'
});
});
});

View file

@ -0,0 +1,224 @@
define(function (require) {
/* Extensions:
html: 8
php: 5 (thus 5 with phpmemory fields)
gif: 5
png: 2
_type:
apache: 18
nginx: 2
Bytes (all unique except):
374: 2
All have the same index, ids are unique
*/
return [
{
'_index': 'logstash-2014.09.09',
'_type': 'apache',
'_id': '61',
'_score': 1,
'_source': {
'extension': 'html',
'bytes': 360.20000000000005
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'apache',
'_id': '388',
'_score': 1,
'_source': {
'extension': 'gif',
'bytes': 5848.700000000001
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'apache',
'_id': '403',
'_score': 1,
'_source': {
'extension': 'png',
'bytes': 841.6
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'apache',
'_id': '415',
'_score': 1,
'_source': {
'extension': 'html',
'bytes': 1626.4
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'apache',
'_id': '460',
'_score': 1,
'_source': {
'extension': 'php',
'bytes': 2070.6,
'phpmemory': 276080
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'apache',
'_id': '496',
'_score': 1,
'_source': {
'extension': 'gif',
'bytes': 8421.6
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'apache',
'_id': '511',
'_score': 1,
'_source': {
'extension': 'html',
'bytes': 994.8000000000001
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'apache',
'_id': '701',
'_score': 1,
'_source': {
'extension': 'html',
'bytes': 374
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'apache',
'_id': '838',
'_score': 1,
'_source': {
'extension': 'php',
'bytes': 506.09999999999997,
'phpmemory': 67480
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'apache',
'_id': '890',
'_score': 1,
'_source': {
'extension': 'php',
'bytes': 506.09999999999997,
'phpmemory': 67480
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'nginx',
'_id': '927',
'_score': 1,
'_source': {
'extension': 'php',
'bytes': 2591.1,
'phpmemory': 345480
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'apache',
'_id': '1034',
'_score': 1,
'_source': {
'extension': 'html',
'bytes': 1450
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'apache',
'_id': '1142',
'_score': 1,
'_source': {
'extension': 'php',
'bytes': 1803.8999999999999,
'phpmemory': 240520
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'apache',
'_id': '1180',
'_score': 1,
'_source': {
'extension': 'html',
'bytes': 1626.4
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'nginx',
'_id': '1224',
'_score': 1,
'_source': {
'extension': 'gif',
'bytes': 10617.2
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'apache',
'_id': '1243',
'_score': 1,
'_source': {
'extension': 'gif',
'bytes': 10961.5
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'apache',
'_id': '1510',
'_score': 1,
'_source': {
'extension': 'html',
'bytes': 382.8
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'apache',
'_id': '1628',
'_score': 1,
'_source': {
'extension': 'html',
'bytes': 374
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'apache',
'_id': '1729',
'_score': 1,
'_source': {
'extension': 'png',
'bytes': 3059.2000000000003
}
},
{
'_index': 'logstash-2014.09.09',
'_type': 'apache',
'_id': '1945',
'_score': 1,
'_source': {
'extension': 'gif',
'bytes': 10617.2
}
}
];
});

View file

@ -2,16 +2,18 @@ define(function (require) {
return function stubbedLogstashIndexPatternService(Private) {
var StubIndexPattern = Private(require('test_utils/stub_index_pattern'));
return new StubIndexPattern('logstash-*', 'time', [
{ type: 'number', name: 'bytes' },
{ type: 'boolean', name: 'ssl' },
{ type: 'date', name: '@timestamp' },
{ type: 'ip', name: 'ip' },
{ type: 'attachment', name: 'request_body' },
{ type: 'string', name: 'extension' },
{ type: 'geo_point', name: 'point' },
{ type: 'geo_shape', name: 'area' },
{ type: 'string', name: 'extension' },
{ type: 'conflict', name: 'custom_user_field' }
{ type: 'number', indexed: true, analyzed: true, count: 10, name: 'bytes' },
{ type: 'boolean', indexed: true, analyzed: true, count: 20, name: 'ssl' },
{ type: 'date', indexed: true, analyzed: true, count: 30, name: '@timestamp' },
{ type: 'number', indexed: true, analyzed: true, count: 0, name: 'phpmemory' },
{ type: 'ip', indexed: true, analyzed: true, count: 0, name: 'ip' },
{ type: 'attachment', indexed: true, analyzed: true, count: 0, name: 'request_body' },
{ type: 'string', indexed: true, analyzed: true, count: 0, name: 'extension' },
{ type: 'geo_point', indexed: true, analyzed: true, count: 0, name: 'point' },
{ type: 'geo_shape', indexed: true, analyzed: true, count: 0, name: 'area' },
{ type: 'string', indexed: true, analyzed: true, count: 0, name: 'extension' },
{ type: 'string', indexed: true, analyzed: true, count: 0, name: '_type' },
{ type: 'conflict', indexed: false, analyzed: false, count: 0, name: 'custom_user_field' }
]);
};
});

View file

@ -69,6 +69,7 @@
'sinon/sinon',
'specs/apps/discover/hit_sort_fn',
'specs/apps/discover/directives/table',
'specs/apps/discover/directives/field_chooser',
'specs/apps/discover/segmented_fetch',
'specs/directives/confirm-click',
'specs/directives/timepicker',

View file

@ -0,0 +1,318 @@
define(function (require) {
var angular = require('angular');
var $ = require('jquery');
var _ = require('lodash');
var sinon = require('test_utils/auto_release_sinon');
var fieldCalculator = require('apps/discover/components/field_chooser/lib/field_calculator');
// Load the kibana app dependencies.
require('services/private');
require('apps/discover/components/field_chooser/field_chooser');
var $parentScope, $scope, config, indexPattern;
// Sets up the directive, take an element, and a list of properties to attach to the parent scope.
var init = function ($elem, props) {
inject(function ($rootScope, $compile, _config_) {
config = _config_;
$parentScope = $rootScope;
_.assign($parentScope, props);
$compile($elem)($parentScope);
$elem.scope().$digest();
$scope = $elem.isolateScope();
});
};
var destroy = function () {
$scope.$destroy();
$parentScope.$destroy();
};
describe('discover field chooser directives', function () {
var $elem = angular.element(
'<disc-field-chooser' +
' fields="fields"' +
' toggle="toggle"' +
' data="data"' +
' filter="filter"' +
' index-pattern="indexPattern"' +
' state="state">' +
'</disc-field-chooser>'
);
beforeEach(module('kibana'));
beforeEach(function () {
inject(function (Private) {
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
});
init($elem, {
fields: _.map(indexPattern.fields.raw, function (v, i) { return _.merge(v, {display: false, rowCount: i}); }),
toggle: sinon.spy(),
data: require('fixtures/hits'),
filter: sinon.spy(),
indexPattern: indexPattern
});
$scope.$digest();
});
afterEach(function () {
destroy();
});
var getSections = function (ctx) {
return {
selected: $('.discover-selected-fields', ctx),
popular: $('.discover-popular-fields', ctx),
unpopular: $('.discover-unpopular-fields', ctx),
};
};
describe('Field listing', function () {
it('should have Selected Fields, Fields and Popular Fields sections', function (done) {
var headers = $elem.find('.sidebar-list-header');
expect(headers.length).to.be(3);
done();
});
it('should have 2 popular fields, 1 unpopular field and no selected fields', function (done) {
var section = getSections($elem);
expect(section.selected.find('li').length).to.be(0);
expect(section.popular.text()).to.contain('ssl');
expect(section.popular.text()).to.contain('@timestamp');
expect(section.popular.text()).to.not.contain('ip');
expect(section.unpopular.text()).to.contain('extension');
expect(section.unpopular.text()).to.contain('area');
expect(section.unpopular.text()).to.not.contain('ssl');
done();
});
it('setting field.display should move the field into selected', function (done) {
var section = getSections($elem);
indexPattern.fields.byName.bytes.display = true;
$scope.$digest();
expect(section.selected.text()).to.contain('bytes');
expect(section.popular.text()).to.not.contain('bytes');
indexPattern.fields.byName.ip.display = true;
$scope.$digest();
expect(section.selected.text()).to.contain('ip');
expect(section.unpopular.text()).to.not.contain('ip');
expect(section.popular.text()).to.contain('ssl');
done();
});
});
describe('details processing', function () {
var field;
beforeEach(function () {
field = indexPattern.fields.byName.bytes;
});
afterEach(function () {
delete field.details;
});
it('should have a details function', function (done) {
expect($scope.details).to.be.a(Function);
done();
});
it('should increase the field popularity when called', function (done) {
var counter = field.count;
indexPattern.popularizeField = sinon.spy();
$scope.details(field);
expect(indexPattern.popularizeField.called).to.be(true);
expect(field.count).to.be(counter + 1);
done();
});
it('should append a details object to the field', function (done) {
$scope.details(field);
expect(field.details).to.not.be(undefined);
done();
});
it('should delete the field details if they already exist', function (done) {
$scope.details(field);
expect(field.details).to.not.be(undefined);
$scope.details(field);
expect(field.details).to.be(undefined);
done();
});
it('... unless recompute is true', function (done) {
$scope.details(field);
expect(field.details).to.not.be(undefined);
$scope.details(field, true);
expect(field.details).to.not.be(undefined);
done();
});
it('should recalculate the details on open fields if the data changes', function () {
$scope.details(field);
sinon.stub($scope, 'details');
$scope.data = [];
$scope.$apply();
expect($scope.details.called).to.be(true);
$scope.details.restore();
// close the field, make sure details isnt called again
$scope.details(field);
sinon.stub($scope, 'details');
$scope.data = ['foo'];
$scope.$apply();
expect($scope.details.called).to.be(false);
});
});
describe('fieldCalculator', function (done) {
it('should have a _countMissing that counts nulls & undefineds in an array', function (done) {
var values = [['foo', 'bar'], 'foo', 'foo', undefined, ['foo', 'bar'], 'bar', 'baz', null, null, null, 'foo', undefined];
expect(fieldCalculator._countMissing(values)).to.be(5);
done();
});
describe('_groupValues', function () {
var groups, params, values;
beforeEach(function () {
values = [['foo', 'bar'], 'foo', 'foo', undefined, ['foo', 'bar'], 'bar', 'baz', null, null, null, 'foo', undefined];
params = {};
groups = fieldCalculator._groupValues(values, params);
});
it('should have a _groupValues that counts values', function (done) {
expect(groups).to.be.an(Object);
done();
});
it('should throw an error if any value is a plain object', function (done) {
expect(function () { fieldCalculator._groupValues([{}, true, false], params); })
.to.throwError();
done();
});
it('should have a a key for value in the array when not grouping array terms', function (done) {
expect(_.keys(groups).length).to.be(3);
expect(groups['foo']).to.be.a(Object);
expect(groups['bar']).to.be.a(Object);
expect(groups['baz']).to.be.a(Object);
done();
});
it('should count array terms independently', function (done) {
expect(groups['foo,bar']).to.be(undefined);
expect(groups['foo'].count).to.be(5);
expect(groups['bar'].count).to.be(3);
expect(groups['baz'].count).to.be(1);
done();
});
describe('grouped array terms', function (done) {
beforeEach(function () {
params.grouped = true;
groups = fieldCalculator._groupValues(values, params);
});
it('should group array terms when passed params.grouped', function (done) {
expect(_.keys(groups).length).to.be(4);
expect(groups['foo,bar']).to.be.a(Object);
done();
});
it('should contain the original array as the value', function (done) {
expect(groups['foo,bar'].value).to.eql(['foo', 'bar']);
done();
});
it('should count the pairs seperately from the values they contain', function (done) {
expect(groups['foo,bar'].count).to.be(2);
expect(groups['foo'].count).to.be(3);
expect(groups['bar'].count).to.be(1);
done();
});
});
});
describe('getFieldValues', function () {
var hits = require('fixtures/real_hits.js');
it('Should return an array of values for _source fields', function () {
var extensions = fieldCalculator.getFieldValues(hits, indexPattern.fields.byName.extension);
expect(extensions).to.be.an(Array);
expect(_.filter(extensions, function (v) { return v === 'html'; }).length).to.be(8);
expect(_.uniq(_.clone(extensions)).sort()).to.eql(['gif', 'html', 'php', 'png']);
});
it('Should return an array of values for core meta fields', function () {
var types = fieldCalculator.getFieldValues(hits, indexPattern.fields.byName._type);
expect(types).to.be.an(Array);
expect(_.filter(types, function (v) { return v === 'apache'; }).length).to.be(18);
expect(_.uniq(_.clone(types)).sort()).to.eql(['apache', 'nginx']);
});
});
describe('getFieldValueCounts', function () {
var params;
beforeEach(function () {
params = {
data: require('fixtures/real_hits.js'),
field: indexPattern.fields.byName.extension,
count: 3
};
});
it('counts the top 3 values', function () {
var extensions = fieldCalculator.getFieldValueCounts(params);
expect(extensions).to.be.an(Object);
expect(extensions.buckets).to.be.an(Array);
expect(extensions.buckets.length).to.be(3);
expect(_.pluck(extensions.buckets, 'value')).to.eql(['html', 'php', 'gif']);
expect(extensions.error).to.be(undefined);
});
it('fails to analyze geo and attachment types', function () {
params.field = indexPattern.fields.byName.point;
expect(fieldCalculator.getFieldValueCounts(params).error).to.not.be(undefined);
params.field = indexPattern.fields.byName.area;
expect(fieldCalculator.getFieldValueCounts(params).error).to.not.be(undefined);
params.field = indexPattern.fields.byName.request_body;
expect(fieldCalculator.getFieldValueCounts(params).error).to.not.be(undefined);
});
it('fails to analyze fields that are in the mapping, but not the data', function () {
params.field = indexPattern.fields.byName.ip;
expect(fieldCalculator.getFieldValueCounts(params).error).to.not.be(undefined);
});
it('counts the total hits', function () {
expect(fieldCalculator.getFieldValueCounts(params).total).to.be(params.data.length);
});
it('counts the hits the field exists in', function () {
params.field = indexPattern.fields.byName.phpmemory;
expect(fieldCalculator.getFieldValueCounts(params).exists).to.be(5);
});
});
});
});
});

View file

@ -3,6 +3,8 @@ define(function (require) {
var $ = require('jquery');
var _ = require('lodash');
var sinon = require('test_utils/auto_release_sinon');
var getFakeRow = require('fixtures/fake_row');
// Load the kibana app dependencies.
require('angular-route');
@ -196,19 +198,6 @@ define(function (require) {
});
var longString = Array(50).join('_');
var getFakeRow = function (id) {
var columns = _.keys(mapping);
return {
_formatted: _.zipObject(_.map(columns, function (c) { return [c, c + '_formatted_' + id + longString]; })),
_source: _.zipObject(_.map(columns, function (c) { return [c, c + '_original_' + id + longString]; })),
_id: id,
_index: 'test',
sort: [id]
};
};
describe('kbnTable', function () {
var $elem = angular.element(
@ -232,7 +221,7 @@ define(function (require) {
sinon.stub($.prototype, 'scrollTop', function () { return -200; });
var rows = _.times(200, function (i) {
return getFakeRow(i);
return getFakeRow(i, mapping);
});
init($elem, {
columns: ['bytes'],
@ -290,7 +279,7 @@ define(function (require) {
beforeEach(function () {
init($elem, {
row: getFakeRow(0),
row: getFakeRow(0, mapping),
columns: [],
sorting: [],
filtering: sinon.spy(),

View file

@ -1,9 +1,11 @@
define(function (require) {
return function (Private) {
var sinon = require('sinon/sinon');
var Registry = require('utils/registry/registry');
var fieldFormats = Private(require('components/index_patterns/_field_formats'));
function StubIndexPattern(pattern, timeField, fields) {
this.popularizeField = sinon.spy();
this.fields = new Registry({
index: ['name'],
group: ['type'],