mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[fieldEditor] added some tests
also fixed a bug here and bug there. No biggy
This commit is contained in:
parent
0977e26e82
commit
a3bf70a85c
9 changed files with 220 additions and 51 deletions
|
@ -2,18 +2,18 @@
|
|||
<div ng-if="editor.creating" class="form-group">
|
||||
<label>Name</label>
|
||||
<input
|
||||
ng-model="editor.fieldSpec.name"
|
||||
ng-model="editor.fieldProps.name"
|
||||
required
|
||||
placeholder="New Scripted Field"
|
||||
input-focus
|
||||
class="form-control"
|
||||
>
|
||||
</div>
|
||||
<div ng-if="editor.creating && editor.indexPattern.fields.byName[editor.fieldSpec.name]" class="hintbox">
|
||||
<div ng-if="editor.creating && editor.indexPattern.fields.byName[editor.fieldProps.name]" class="hintbox">
|
||||
<p>
|
||||
<i class="fa fa-danger text-danger"></i>
|
||||
<strong>Mapping Conflict:</strong>
|
||||
You already have a field with the name {{ editor.fieldSpec.name }}. Naming your scripted
|
||||
You already have a field with the name {{ editor.fieldProps.name }}. Naming your scripted
|
||||
field with the same name means you won't be able to query both fields at the same time.
|
||||
</p>
|
||||
</div>
|
||||
|
@ -88,6 +88,7 @@
|
|||
|
||||
<div class="form-group">
|
||||
<button
|
||||
type="button"
|
||||
ng-click="editor.cancel()"
|
||||
aria-label="Cancel"
|
||||
class="btn btn-primary">
|
||||
|
|
|
@ -4,11 +4,14 @@ define(function (require) {
|
|||
|
||||
require('modules')
|
||||
.get('kibana')
|
||||
.directive('fieldEditor', function (Private) {
|
||||
.directive('fieldEditor', function (Private, $sce) {
|
||||
var _ = require('lodash');
|
||||
var fieldFormats = Private(require('registry/field_formats'));
|
||||
var Field = Private(require('components/index_patterns/_field'));
|
||||
|
||||
var scriptingInfo = $sce.trustAsHtml(require('text!components/field_editor/scripting_info.html'));
|
||||
var scriptingWarning = $sce.trustAsHtml(require('text!components/field_editor/scripting_warning.html'));
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: require('text!components/field_editor/field_editor.html'),
|
||||
|
@ -17,21 +20,17 @@ define(function (require) {
|
|||
getField: '&field'
|
||||
},
|
||||
controllerAs: 'editor',
|
||||
controller: function ($sce, $scope, Notifier, kbnUrl) {
|
||||
controller: function ($scope, Notifier, kbnUrl) {
|
||||
var self = this;
|
||||
var notify = new Notifier({ location: 'Field Editor' });
|
||||
|
||||
self.indexPattern = $scope.getIndexPattern();
|
||||
self.fieldSpec = Object.create($scope.getField().$$spec);
|
||||
self.field = mutatedField();
|
||||
self.selectedFormatId = _.get(self.indexPattern, ['fieldFormatMap', self.field.name, 'type', 'id']);
|
||||
self.formatParams = self.field.format.params();
|
||||
self.defFormatType = initDefaultFormat();
|
||||
self.fieldFormatTypes = [self.defFormatType].concat(fieldFormats.byFieldType[self.field.type] || []);
|
||||
self.creating = !self.indexPattern.fields.byName[self.field.name];
|
||||
self.scriptingInfo = scriptingInfo;
|
||||
self.scriptingWarning = scriptingWarning;
|
||||
|
||||
self.scriptingInfo = $sce.trustAsHtml(require('text!components/field_editor/scripting_info.html'));
|
||||
self.scriptingWarning = $sce.trustAsHtml(require('text!components/field_editor/scripting_warning.html'));
|
||||
self.indexPattern = $scope.getIndexPattern();
|
||||
self.fieldProps = Object.create($scope.getField().$$spec);
|
||||
createField();
|
||||
self.formatParams = self.field.format.params();
|
||||
|
||||
self.cancel = function () {
|
||||
kbnUrl.change(self.indexPattern.editRoute);
|
||||
|
@ -48,7 +47,7 @@ define(function (require) {
|
|||
if (!self.selectedFormatId) {
|
||||
delete indexPattern.fieldFormatMap[field.name];
|
||||
} else {
|
||||
indexPattern.fieldFormatMap[field.name] = field.format;
|
||||
indexPattern.fieldFormatMap[field.name] = self.format;
|
||||
}
|
||||
|
||||
return indexPattern.save()
|
||||
|
@ -73,26 +72,52 @@ define(function (require) {
|
|||
$scope.$watchMulti([
|
||||
'editor.selectedFormatId',
|
||||
'=editor.formatParams',
|
||||
'=editor.fieldSpec'
|
||||
'=editor.fieldProps'
|
||||
], function (cur, prev) {
|
||||
var formatId = cur[0];
|
||||
var updatedFormat = cur[0] !== prev[0];
|
||||
var changedFormat = cur[0] !== prev[0];
|
||||
var missingFormat = cur[0] && (!self.format || self.format.type.id !== cur[0]);
|
||||
var changedParams = cur[1] !== prev[1];
|
||||
var FieldFormat = getFieldFormatType();
|
||||
|
||||
if (updatedFormat) {
|
||||
var FieldFormat = fieldFormats.byId[formatId];
|
||||
if (FieldFormat) {
|
||||
self.formatParams = _.cloneDeep(FieldFormat.paramDefaults || {});
|
||||
self.fieldSpec.format = new FieldFormat(self.formatParams);
|
||||
if (changedFormat) {
|
||||
// the old params are no longer valid
|
||||
self.formatParams = {};
|
||||
}
|
||||
|
||||
if (!changedParams && (changedFormat || missingFormat)) {
|
||||
self.formatParams = _.defaults(self.formatParams || {}, FieldFormat.paramDefaults);
|
||||
if (!_.isEqual(self.formatParams, cur[1])) return;
|
||||
}
|
||||
|
||||
if (changedParams || changedFormat || missingFormat) {
|
||||
if (self.selectedFormatId) {
|
||||
self.format = new FieldFormat(self.formatParams);
|
||||
self.formatParams = self.format.params();
|
||||
} else {
|
||||
self.formatParams = self.fieldSpec.format = undefined;
|
||||
self.format = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
self.field = mutatedField();
|
||||
createField();
|
||||
});
|
||||
|
||||
function mutatedField() {
|
||||
return new Field(self.indexPattern, self.fieldSpec);
|
||||
function createField() {
|
||||
var first = !self.field;
|
||||
var spec = _.assign(Object.create(self.fieldProps), { format: self.format });
|
||||
self.field = new Field(self.indexPattern, spec);
|
||||
|
||||
if (!first) return;
|
||||
// only init on first create
|
||||
self.creating = !self.indexPattern.fields.byName[self.field.name];
|
||||
self.selectedFormatId = _.get(self.indexPattern, ['fieldFormatMap', self.field.name, 'type', 'id']);
|
||||
if (self.selectedFormatId) self.format = self.field.format;
|
||||
self.defFormatType = initDefaultFormat();
|
||||
self.fieldFormatTypes = [self.defFormatType].concat(fieldFormats.byFieldType[self.field.type] || []);
|
||||
}
|
||||
|
||||
function getFieldFormatType() {
|
||||
if (self.selectedFormatId) return fieldFormats.getType(self.selectedFormatId);
|
||||
else return fieldFormats.getDefaultType(self.field.type);
|
||||
}
|
||||
|
||||
function initDefaultFormat() {
|
||||
|
|
|
@ -102,7 +102,7 @@ define(function (require) {
|
|||
|
||||
FieldFormat.prototype.toJSON = function () {
|
||||
var type = this.type;
|
||||
var defaults = type.paramDefaults;
|
||||
var defaults = this._paramDefaults;
|
||||
|
||||
var params = _.transform(this._params, function (uniqParams, val, param) {
|
||||
if (val !== defaults[param]) {
|
||||
|
|
|
@ -3,6 +3,7 @@ define(function (require) {
|
|||
var _ = require('lodash');
|
||||
|
||||
var FieldFormat = Private(require('components/index_patterns/_field_format'));
|
||||
var StringFormat = Private(require('components/stringify/types/String'));
|
||||
require('components/stringify/pattern/pattern');
|
||||
|
||||
_(Url).inherits(FieldFormat);
|
||||
|
@ -13,7 +14,7 @@ define(function (require) {
|
|||
|
||||
Url.id = 'url';
|
||||
Url.title = 'Url';
|
||||
Url.fieldType = 'string';
|
||||
Url.fieldType = StringFormat.fieldType; // anything that can be serialized to a string is g2g!
|
||||
Url.editor = {
|
||||
template: require('text!components/stringify/editors/url.html'),
|
||||
controllerAs: 'url',
|
||||
|
|
|
@ -16,7 +16,7 @@ define(function (require) {
|
|||
kbnSetup(),
|
||||
config.init(),
|
||||
courier.SearchSource.ready,
|
||||
$rootScope.kibana.ready
|
||||
$rootScope.kibana && $rootScope.kibana.ready
|
||||
])
|
||||
.then(function () {
|
||||
var path = $route.current.$$route.originalPath;
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
<script src="/node_modules/mocha/mocha.js"></script>
|
||||
<script src="/src/kibana/bower_components/requirejs/require.js"></script>
|
||||
<script src="/src/kibana/require.config.js"></script>
|
||||
|
||||
<script>(function () {
|
||||
var COVERAGE = !!(/coverage/i.test(window.location.search));
|
||||
var SAUCELABS = !!(/saucelabs/i.test(window.location.search));
|
||||
|
@ -43,6 +42,7 @@
|
|||
],
|
||||
exports: 'angular'
|
||||
},
|
||||
'angular-route': ['angular'],
|
||||
'sinon/sinon': {
|
||||
deps: [
|
||||
'sinon/sinon-timers-1.8.2'
|
||||
|
|
147
test/unit/specs/components/field_editor/field_editor.js
Normal file
147
test/unit/specs/components/field_editor/field_editor.js
Normal file
|
@ -0,0 +1,147 @@
|
|||
define(function (require) {
|
||||
describe('FieldEditor directive', function () {
|
||||
var _ = require('lodash');
|
||||
var $ = require('jquery');
|
||||
|
||||
var Field;
|
||||
var StringFormat;
|
||||
var $rootScope;
|
||||
|
||||
var compile;
|
||||
var $scope;
|
||||
var $el;
|
||||
|
||||
// just some properties of field that we can compare
|
||||
var fieldProps = [
|
||||
'name', 'type', 'count', 'scripted', 'script', 'lang',
|
||||
'indexed', 'analyzed', 'doc_values', 'format', 'sortable',
|
||||
'bucketable', 'filterable', 'indexPattern', 'displayName',
|
||||
'editRoute'
|
||||
];
|
||||
|
||||
var fieldToPrimativeVals = function (field) {
|
||||
return _(field).pick(fieldProps).omit(function (v) {
|
||||
return v && (typeof v === 'object' || typeof v === 'function');
|
||||
}).value();
|
||||
};
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
beforeEach(inject(function ($compile, $injector, Private) {
|
||||
$rootScope = $injector.get('$rootScope');
|
||||
Field = Private(require('components/index_patterns/_field'));
|
||||
StringFormat = Private(require('registry/field_formats')).getType('string');
|
||||
|
||||
$rootScope.indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
|
||||
$rootScope.field = $rootScope.indexPattern.fields.byName.time;
|
||||
// set the field format for this field
|
||||
$rootScope.indexPattern.fieldFormatMap[$rootScope.field.name] = new StringFormat({ foo: 1, bar: 2 });
|
||||
|
||||
|
||||
compile = function () {
|
||||
$el = $compile($('<field-editor field="field" index-pattern="indexPattern">'))($rootScope);
|
||||
$scope = $el.data('$isolateScope');
|
||||
};
|
||||
}));
|
||||
|
||||
describe('$scope', function () {
|
||||
it('is isolated', function () {
|
||||
compile();
|
||||
expect($scope.parent == null).to.be.ok();
|
||||
expect($scope).to.not.be($rootScope);
|
||||
});
|
||||
|
||||
it('exposes $scope.editor, a controller for the editor', function () {
|
||||
compile();
|
||||
var editor = $scope.editor;
|
||||
expect(editor).to.be.an('object');
|
||||
});
|
||||
});
|
||||
|
||||
describe('$scope.editor', function () {
|
||||
var editor;
|
||||
|
||||
beforeEach(function () {
|
||||
compile();
|
||||
editor = $scope.editor;
|
||||
});
|
||||
|
||||
it('exposes editor.indexPattern', function () {
|
||||
expect(editor.indexPattern).to.be($rootScope.indexPattern);
|
||||
});
|
||||
|
||||
it('exposes editor.fieldProps', function () {
|
||||
expect(editor.fieldProps).to.be.an('object');
|
||||
});
|
||||
|
||||
describe('editor.fieldProps', function () {
|
||||
it('is a shadow copy of the index patterns field spec', function () {
|
||||
var actual = $rootScope.field;
|
||||
var spec = editor.fieldProps;
|
||||
|
||||
expect(spec).to.not.be(actual.$$spec);
|
||||
expect(actual.$$spec.isPrototypeOf(spec)).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('exposes editor.field', function () {
|
||||
expect(editor.field).to.be.an('object');
|
||||
});
|
||||
|
||||
describe('editor.field', function () {
|
||||
it('looks like the field from the index pattern, but isn\'t', function () {
|
||||
var field = editor.field;
|
||||
var actual = $rootScope.field;
|
||||
|
||||
expect(field).to.not.be(actual);
|
||||
expect(field).to.be.a(Field);
|
||||
expect(fieldToPrimativeVals(field)).to.eql(fieldToPrimativeVals(actual));
|
||||
});
|
||||
|
||||
it('is built to match the editor.fieldProps', function () {
|
||||
expect(editor.field).to.have.property('name', editor.fieldProps.name);
|
||||
expect(editor.field).to.have.property('type', editor.fieldProps.type);
|
||||
|
||||
editor.fieldProps.name = 'smith';
|
||||
$rootScope.$apply();
|
||||
expect(editor.field.name).to.be('smith');
|
||||
|
||||
});
|
||||
|
||||
it('is rebuilt when the fieldProps changes', function () {
|
||||
expect(editor.field.name).to.be(editor.fieldProps.name);
|
||||
var newName = editor.fieldProps.name = editor.fieldProps.name + 'foo';
|
||||
expect(editor.field.name).to.not.be(newName); // rebuilt after $digest
|
||||
$rootScope.$apply();
|
||||
expect(editor.field.name).to.be(newName);
|
||||
expect(editor.fieldProps.name).to.be(newName);
|
||||
});
|
||||
});
|
||||
|
||||
it('exposes editor.formatParams', function () {
|
||||
expect(editor).to.have.property('formatParams');
|
||||
expect(editor.field.format.params()).to.eql(editor.formatParams);
|
||||
});
|
||||
|
||||
describe('editor.formatParams', function () {
|
||||
it('initializes with all of the formats current params', function () {
|
||||
// rebuild the editor
|
||||
compile();
|
||||
editor = $scope.editor;
|
||||
|
||||
expect(editor.formatParams).to.have.property('foo', 1);
|
||||
expect(editor.formatParams).to.have.property('bar', 2);
|
||||
});
|
||||
|
||||
it('updates the fields format when changed', function () {
|
||||
$rootScope.$apply(); // initial apply in order to pick up change
|
||||
editor.formatParams.foo = 200;
|
||||
$rootScope.$apply();
|
||||
expect(editor.field.format.param('foo')).to.be(200);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
|
@ -49,7 +49,7 @@ define(function (require) {
|
|||
expect(results[2].meta).to.have.property('key', 'query');
|
||||
expect(results[2].meta).to.have.property('value', 'foo:bar');
|
||||
expect(results[3].meta).to.have.property('key', 'bytes');
|
||||
expect(results[3].meta).to.have.property('value', '1024 to 2048');
|
||||
expect(results[3].meta).to.have.property('value', '1,024 to 2,048');
|
||||
expect(results[4].meta).to.have.property('key', '_type');
|
||||
expect(results[4].meta).to.have.property('value', 'apache');
|
||||
done();
|
||||
|
|
|
@ -3,35 +3,30 @@ define(function (require) {
|
|||
var _ = require('lodash');
|
||||
var sinon = require('sinon/sinon');
|
||||
var IndexedArray = require('utils/indexed_array/index');
|
||||
var fieldFormats = Private(require('registry/field_formats'));
|
||||
var flattenHit = require('components/index_patterns/_flatten_hit');
|
||||
var getComputedFields = require('components/index_patterns/_get_computed_fields');
|
||||
|
||||
var fieldFormats = Private(require('registry/field_formats'));
|
||||
var Field = Private(require('components/index_patterns/_field'));
|
||||
|
||||
function StubIndexPattern(pattern, timeField, fields) {
|
||||
this.id = pattern;
|
||||
this.popularizeField = sinon.spy();
|
||||
this.timeFieldName = timeField;
|
||||
this.fields = new IndexedArray({
|
||||
index: ['name'],
|
||||
group: ['type'],
|
||||
initialSet: fields.map(function (field) {
|
||||
field.count = field.count || 0;
|
||||
|
||||
// non-enumerable type so that it does not get included in the JSON
|
||||
Object.defineProperty(field, 'format', {
|
||||
enumerable: false,
|
||||
get: function () {
|
||||
return fieldFormats.getDefaultInstance(field.type);
|
||||
}
|
||||
});
|
||||
|
||||
return field;
|
||||
})
|
||||
});
|
||||
this.getFields = sinon.spy();
|
||||
this.toIndexList = _.constant([pattern]);
|
||||
this.getComputedFields = getComputedFields;
|
||||
this.flattenHit = _.partial(flattenHit, this);
|
||||
this.metaFields = ['_id', '_type', '_source'];
|
||||
this.fieldFormatMap = {};
|
||||
|
||||
this.fields = new IndexedArray({
|
||||
index: ['name'],
|
||||
group: ['type'],
|
||||
initialSet: fields.map(function (field) {
|
||||
return new Field(this, field);
|
||||
}, this)
|
||||
});
|
||||
}
|
||||
|
||||
return StubIndexPattern;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue