Skip to content

Commit

Permalink
[aggTypes/percentile] updated tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Spencer Alger committed Jan 9, 2015
1 parent f6f883c commit f9ee0b2
Show file tree
Hide file tree
Showing 3 changed files with 268 additions and 53 deletions.
28 changes: 21 additions & 7 deletions src/kibana/components/agg_types/controls/_percent_list.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ define(function (require) {
restrict: 'A',
require: 'ngModel',
link: function ($scope, $el, attrs, ngModelController) {
var $setModel = $parse(attrs.ngModel).assign;
var $repeater = $el.closest('[ng-repeat]');
var $listGetter = $parse(attrs.percentList);

Expand Down Expand Up @@ -73,7 +74,11 @@ define(function (require) {

if (dec < 0 || dec > 9) {
int += Math.floor(dec / 10);
dec = dec % 10;
if (dec < 0) {
dec = 10 + (dec % 10);
} else {
dec = dec % 10;
}
}

return parse(int + '.' + dec);
Expand Down Expand Up @@ -138,12 +143,21 @@ define(function (require) {
ngModelController.$setValidity('percentList', valid);
});

ngModelController.$parsers.push(function (viewValue) {
var value = parse(viewValue);
var valid = value !== INVALID;
ngModelController.$setValidity('percentList', valid);
return valid ? value : void 0;
});
function validate(then) {
return function (input) {
var value = parse(input);
var valid = value !== INVALID;
value = valid ? value : void 0;
ngModelController.$setValidity('percentList', valid);
then && then(input, value);
return value;
};
}

ngModelController.$parsers.push(validate());
ngModelController.$formatters.push(validate(function (input, value) {
if (input !== value) $setModel($scope, value);
}));
}
};
});
Expand Down
188 changes: 142 additions & 46 deletions test/unit/specs/components/agg_types/controls/percent_list.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,79 +2,175 @@ define(function (require) {
describe('PercentList directive', function () {
var $ = require('jquery');
var _ = require('lodash');
var simulateKeys = require('test_utils/simulate_keys');

require('components/agg_types/controls/_percent_list');

var $el;
var $scope;
var $compile;
var $rootScope;
var compile;

beforeEach(module('kibana'));
beforeEach(inject(function ($injector) {
$rootScope = $injector.get('$rootScope');
$compile = $injector.get('$compile');
var $compile = $injector.get('$compile');
var $rootScope = $injector.get('$rootScope');

$el = $('<input>').attr('ng-model', 'val').attr('percent-list', '');
$scope = $rootScope.$new();
$el = $('<div>').append(
$('<input>')
.attr('ng-model', 'vals[$index]')
.attr('ng-repeat', 'val in vals')
.attr('percent-list', 'vals')
);
compile = function (vals) {
$scope.vals = vals || [];
$compile($el)($scope);
$scope.$apply();
};
}));

afterEach(function () {
$el.remove();
$scope.$destroy();
});

it('filters out empty entries', function () {
$compile($el)($scope);

$el.val(',,1, ,, ,2, ,\n,');
$el.change();
expect($scope.val).to.eql([1, 2]);
});

it('fails on invalid numbers', function () {
$compile($el)($scope);

$el.val('foo,bar');
$el.change();
expect($scope.val).to.be(undefined);
expect($el.hasClass('ng-invalid')).to.be(true);
compile([1, 'foo']);
expect($scope.vals).to.eql([1, undefined]);
expect($el.find('.ng-invalid').size()).to.be(1);
});

it('supports decimals', function () {
$compile($el)($scope);

$el.val('1.2,000001.6,99.10');
$el.change();
expect($scope.val).to.eql([1.2, 1.6, 99.10]);
compile(['1.2', '000001.6', '99.10']);
expect($scope.vals).to.eql([1.2, 1.6, 99.1]);
});

it('ensures that the values are in order', function () {
$compile($el)($scope);

$el.val('1, 2, 3, 10, 4, 5');
$el.change();
expect($scope.val).to.be(undefined);
expect($el.hasClass('ng-invalid')).to.be(true);
compile([1, 2, 3, 10, 4, 5]);
expect($scope.vals).to.eql([1, 2, 3, undefined, 4, 5]);
expect($el.find('.ng-invalid').size()).to.be(1);
});

it('ensures that the values are less between 0 and 100', function () {
$compile($el)($scope);

$el.val('-1, 0, 1');
$el.change();
expect($scope.val).to.be(undefined);
expect($el.hasClass('ng-invalid')).to.be(true);

$el.val('0, 1');
$el.change();
expect($scope.val).to.eql([0, 1]);
expect($el.hasClass('ng-invalid')).to.be(false);
describe('ensures that the values are between 0 and 100', function () {
it(': -1', function () {
compile([-1, 1]);
expect($scope.vals).to.eql([undefined, 1]);
expect($el.find('.ng-invalid').size()).to.be(1);
});

it(': 0', function () {
compile([0, 1]);
expect($scope.vals).to.eql([undefined, 1]);
expect($el.find('.ng-invalid').size()).to.be(1);
});

it(': 0.0001', function () {
compile([0.0001, 1]);
expect($scope.vals).to.eql([0.0001, 1]);
expect($el.find('.ng-invalid').size()).to.be(0);
});

it(': 99.9999999', function () {
compile([1, 99.9999999]);
expect($scope.vals).to.eql([1, 99.9999999]);
expect($el.find('.ng-invalid').size()).to.be(0);
});

it(': 101', function () {
compile([1, 101]);
expect($scope.vals).to.eql([1, undefined]);
expect($el.find('.ng-invalid').size()).to.be(1);
});
});

$el.val('1, 101');
$el.change();
expect($scope.val).to.be(undefined);
expect($el.hasClass('ng-invalid')).to.be(true);
describe('listens for keyboard events', function () {
it('up arrow increases by 1', function () {
compile([1]);

return simulateKeys(
function () { return $el.find('input').first(); },
['up', 'up', 'up']
)
.then(function () {
expect($scope.vals).to.eql([4]);
});
});

it('shift-up increases by 0.1', function () {
compile([4.8]);

var seq = [
{
type: 'press',
key: 'shift',
events: [
'up',
'up',
'up'
]
}
];

return simulateKeys(
function () { return $el.find('input').first(); },
seq
)
.then(function () {
expect($scope.vals).to.eql([5.1]);
});
});

it('down arrow decreases by 1', function () {
compile([5]);

return simulateKeys(
function () { return $el.find('input').first(); },
['down', 'down', 'down']
)
.then(function () {
expect($scope.vals).to.eql([2]);
});
});

it('shift-down decreases by 0.1', function () {
compile([5.1]);

var seq = [
{
type: 'press',
key: 'shift',
events: [
'down',
'down',
'down'
]
}
];

return simulateKeys(
function () { return $el.find('input').first(); },
seq
)
.then(function () {
expect($scope.vals).to.eql([4.8]);
});
});

it('maintains valid number', function () {
compile([9, 11, 13]);

var seq = [
'down', // 10 (11 - 1)
'down' // 10 (limited by 9)
];

var getEl = function () { return $el.find('input').eq(1); };

return simulateKeys(getEl, seq)
.then(function () {
expect($scope.vals).to.eql([9, 10, 13]);
});
});
});
});
});
105 changes: 105 additions & 0 deletions test/utils/simulate_keys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
define(function (require) {
var $ = require('jquery');
var _ = require('lodash');
var Promise = require('bluebird');
var keyMap = require('utils/key_map');
var reverseKeyMap = _.mapValues(_.invert(keyMap), _.limit(_.parseInt, 1));
var KeyboardEvent = window.KeyboardEvent;

/**
* Simulate keyboard events in an element. This allows testing the way that
* elements respond to keyboard input.
*
* # sequence style
* keyboard events occur in a sequence, this array of events describe that sequence.
*
* ## event
* an object with a type property, or a string which will be turned into a single press
*
* ## event types
* ### press
* represents a key press
* - `key`: the key for the button pressed
* - `events`: optional list of events that occur before this press completes
*
* ### wait
* represents a pause in a sequence
* - `ms`: the number of milliseconds that the pause takes
*
* ### repeat
* represents a key being repeated because it is held down. Should only exist as a
* sub event of `press` events.
* - `count`: the number of times the repeat occurs
*
* @param {element} $el - jQuery element where events should occur
* @param {[type]} sequence - an array of events
* @async
*/
return function ($el, sequence) {
var modifierState = {
ctrlKey: false,
shiftKey: false,
altKey: false,
metaKey: false
};

return doList(_.clone(sequence));

function setModifier(key, state) {
var name = key + 'Key';
if (modifierState.hasOwnProperty(name)) {
modifierState[name] = !!state;
}
}

function doList(list) {
return Promise.try(function () {
if (!list || !list.length) return;

var event = list[0];
if (_.isString(event)) {
event = { type: 'press', key: event };
}

switch (event.type) {
case 'press':
return Promise.resolve()
.then(_.partial(fire, 'keydown', event.key))
.then(_.partial(fire, 'keypress', event.key))
.then(_.partial(doList, event.events))
.then(_.partial(fire, 'keyup', event.key));

case 'wait':
return Promise.delay(event.ms);

case 'repeat':
return (function again(remaining) {
if (!remaining) return Promise.resolve();
remaining = remaining - 1;
return Promise.resolve()
.then(_.partial(fire, 'keydown', event.key, true))
.then(_.partial(fire, 'keypress', event.key, true))
.then(_.partial(again, remaining));
}(event.count));

default:
throw new TypeError('invalid event type "' + event.type + '"');
}
})
.then(function () {
if (_.size(list) > 1) return doList(list.slice(1));
});
}

function fire(type, key, repeat) {
var keyCode = reverseKeyMap[key];
if (!keyCode) throw new TypeError('invalid key "' + key + '"');

if (type === 'keydown') setModifier(key, true);
if (type === 'keyup') setModifier(key, false);

var $target = _.isFunction($el) ? $el() : $el;
$target.trigger($.Event(type, _.defaults({ keyCode: keyCode }, modifierState)));
}
};
});

0 comments on commit f9ee0b2

Please sign in to comment.