todo/public/vendor/seiyria-bootstrap-slider/test/specs/LockToTicksSpec.js

699 lines
20 KiB
JavaScript
Raw Normal View History

2020-06-23 04:40:37 +00:00
/*
*************************
Lock To Ticks Tests
*************************
Verify that the when lock_to_ticks is true, the selected values will be snapped to closest tick values
*/
describe("Slider with lock_to_ticks: true tests", function() {
var testSlider;
it("Should set `options.lock_to_ticks` to false when `ticks[]` is not set", function() {
testSlider = $('#testSlider1').slider({
lock_to_ticks: true
});
var lockAttr = testSlider.slider('getAttribute', 'lock_to_ticks');
expect(lockAttr).toBe(false);
});
it("Should set the slider value when `ticks[]` is not set", function() {
testSlider = $('#testSlider1').slider({
min: 0,
max: 10,
value: 1,
lock_to_ticks: true
});
testSlider.slider('setValue', 5);
var value = testSlider.slider('getValue');
expect(value).toBe(5);
});
it("Should return a value (after calling `setAttribute()`)", function() {
testSlider = $('#testSlider1').slider({
min: 0,
max: 10,
value: 1,
lock_to_ticks: true
});
// Try to lock to ticks when there are no ticks[]
testSlider.slider('setAttribute', 'lock_to_ticks', true);
testSlider.slider('setValue', 5);
// Without proper checking, getValue() is going to return NaN in this case
var value = testSlider.slider('getValue');
expect(isNaN(value)).toBe(false);
});
it("Should snap to closest tick value (single)", function() {
testSlider = $('#testSlider1').slider({
value: 1,
ticks: [1, 10, 50, 200, 500, 1000, 5000, 10000],
lock_to_ticks: true
});
// Set a value that is not a tick
testSlider.slider('setValue', 102);
var value = testSlider.slider('getValue');
expect(value).toBe(50);
});
it("Should snap to closest tick value (single, vertical)", function() {
testSlider = $('#testSlider1').slider({
value: 1,
ticks: [1, 10, 50, 200, 500, 1000, 5000, 10000],
lock_to_ticks: true,
orientation: 'vertical'
});
//selecting values that are not inside tickes
testSlider.slider('setValue', 102);
//getting the actual values. They should be the closest values from ticks (which are 1 and 50 on this case)
var value = testSlider.slider('getValue');
expect(value).toBe(50);
});
it("Should snap to closest tick value (range)", function() {
testSlider = $("#testSlider1").slider({
value: [1, 10000],
ticks: [1, 10, 50, 200, 500, 1000, 5000, 10000],
lock_to_ticks: true
});
//selecting values that are not inside tickes
testSlider.slider("setValue", [4, 102]);
//getting the actual values. They should be the closest values from ticks (which are 1 and 50 on this case)
var values = testSlider.slider("getValue");
expect(values[0]).toBe(1);
expect(values[1]).toBe(50);
});
it("Should snap to closest tick value (range, vertical)", function() {
testSlider = $("#testSlider1").slider({
value: [1, 10000],
ticks: [1, 10, 50, 200, 500, 1000, 5000, 10000],
lock_to_ticks: true,
orientation: 'vertical'
});
//selecting values that are not inside tickes
testSlider.slider("setValue", [4, 102]);
//getting the actual values. They should be the closest values from ticks (which are 1 and 50 on this case)
var values = testSlider.slider("getValue");
expect(values[0]).toBe(1);
expect(values[1]).toBe(50);
});
afterEach(function() {
if(testSlider) {
testSlider.slider('destroy');
testSlider = null;
}
});
});
/**
* The mouse navigation tests are based on the following slider properties:
*
* initial value: 3 or [3, 7]
* ticks: [0, 3, 5, 7, 10]
* step: 0.1
*
* When the values are in the range from 0 to 10 and the step is 0.1, every value
* can be represented as 1% of the range. For example, 5.5 is 55% and 5.6 is 56%.
* This makes it easier to test the behaviour of tick locking.
*
* The mouse clicks are based on a percentage that represents where the user clicked
* on the slider (60% = 6.0). The percentage is then used to calculate the coordinates
* for the mouse events (mousedown, mousemove, and mouseup).
*
* These tests are setup to test for all combinations of relevant slider configurations:
* single/range, horizontal/vertical orientation, LTR/RTL, and ordered/reversed.
* The tests also check that the handles have the correct positioning, which should
* match the ticks that the handles lock to.
*
* The test data was carefully chosen based on following the test logic below.
*
* The test logic for sliders:
* - Clicking to the left of the handle should not change its value
* - Clicking to the left of the handle should change its value and lock to another tick
* - Ditto for clicking to the right of the handle
*
* For range sliders, the same logic is applied except test both handle1 and handle2.
*/
describe("`lock_to_ticks: true` mouse navigation test cases", function() {
var initialValue = 3;
var initialRangeValues = [3, 7];
var tickValues = [0, 3, 5, 7, 10];
// Quick lookup from value to index
var tickIndexes = {0: 0, 3: 1, 5: 2, 7: 3, 10: 4};
var stepValue = 0.1;
var orientations = ['horizontal', 'vertical'];
var reversed = [false, true];
var sliderTypes = ['single', 'range'];
var rtl = [false, true];
var testCases = [];
var mouseEventArguments;
function calcMouseEventCoords(sliderElem, orientation, per) {
var sliderBB = sliderElem.getBoundingClientRect();
if (orientation === 'horizontal') {
return {
clientX: sliderBB.left + (sliderElem.offsetWidth * per / 100),
clientY: sliderBB.top
};
}
else if (orientation === 'vertical') {
return {
clientX: sliderBB.left,
clientY: sliderBB.top + (sliderElem.offsetHeight * per / 100)
};
}
}
beforeEach(function() {
// Set up default set of mouse event arguments
mouseEventArguments = [
'mousemove', // type
true, // canBubble
true, // cancelable
document, // view,
0, // detail
0, // screenX
0, // screenY
undefined, // clientX
undefined, // clientY,
false, // ctrlKey
false, // altKey
false, // shiftKey
false, // metaKey,
0, // button
null // relatedTarget
];
});
function createMouseEvent(type, clientX, clientY) {
var mouseEvent = document.createEvent('MouseEvent');
mouseEventArguments[0] = type;
mouseEventArguments[7] = clientX;
mouseEventArguments[8] = clientY;
mouseEvent.initMouseEvent.apply(mouseEvent, mouseEventArguments);
return mouseEvent;
}
var mouseTestData = {
single: {
testData: [{
willChange: false,
expectedValue: initialValue,
percent: 20
}, {
willChange: true,
expectedValue: 0,
percent: 10
}, {
willChange: false,
expectedValue: initialValue,
percent: 40
}, {
willChange: true,
expectedValue: 5,
percent: 45
}],
// Modify test data for RTL and reversed situations.
altTestData: [{
willChange: false,
expectedValue: initialValue,
percent: 80
}, {
willChange: true,
expectedValue: 0,
percent: 90
}, {
willChange: false,
expectedValue: initialValue,
percent: 60
}, {
willChange: true,
expectedValue: 5,
percent: 55
}],
},
range: {
testData: [{
willChange: false,
expectedValue: initialRangeValues,
percent: 20
}, {
willChange: true,
expectedValue: [0, 7],
percent: 10
}, {
willChange: false,
expectedValue: initialRangeValues,
percent: 40
}, {
willChange: true,
expectedValue: [5, 7],
percent: 45
},
// More test data that will affect handle2
{
willChange: true,
expectedValue: [3, 5],
percent: 60
}, {
willChange: false,
expectedValue: initialRangeValues,
percent: 65
}, {
willChange: false,
expectedValue: initialRangeValues,
percent: 80
}, {
willChange: true,
expectedValue: [3, 10],
percent: 90
}],
// Modify test data for RTL and reversed situations.
// Here, the order of the ticks matter when locking a handle to a tick
// when there are two ticks that are equal in distance to lock to.
altTestData: [{
willChange: false,
expectedValue: initialRangeValues,
percent: 20
}, {
willChange: true,
expectedValue: [3, 10],
percent: 10
}, {
willChange: true,
expectedValue: [3, 5],
percent: 40
}, {
willChange: false,
expectedValue: initialRangeValues,
percent: 35
},
// More test data that will affect handle2
{
willChange: false,
expectedValue: initialRangeValues,
percent: 60
}, {
willChange: true,
expectedValue: [5, 7],
percent: 55
}, {
willChange: false,
expectedValue: initialRangeValues,
percent: 80
}, {
willChange: true,
expectedValue: [0, 7],
percent: 90
}],
}
};
sliderTypes.forEach(function(sliderType) {
orientations.forEach(function(orientation) {
rtl.forEach(function(isRTL) {
reversed.forEach(function(isReversed) {
var isHorizontal = orientation === 'horizontal';
var isVertical = orientation === 'vertical';
var isRange = sliderType === 'range';
var whichData;
var whichStyle;
whichData = mouseTestData[sliderType].testData;
if (isHorizontal) {
// XOR
// One or the other needs to be true
if ((isRTL && !isReversed) || (isReversed && !isRTL)) {
whichData = mouseTestData[sliderType].altTestData;
}
}
else if (isVertical) {
if (isReversed) {
whichData = mouseTestData[sliderType].altTestData;
}
}
if (isHorizontal) {
if (isRTL) {
whichStyle = 'right';
}
else {
whichStyle = 'left';
}
}
else if (isVertical) {
whichStyle = 'top';
}
testCases.push({
value: isRange ? initialRangeValues : initialValue,
step: stepValue,
range: isRange,
orientation: orientation,
reversed: isReversed,
rtl: 'auto',
isRTL: isRTL,
inputId: isRTL ? 'rtlSlider' : 'testSlider1',
testData: whichData,
stylePos: whichStyle
});
});
});
});
});
testCases.forEach(function(testCase) {
describe("range=" + testCase.range + ", orientation=" + testCase.orientation +
", rtl=" + testCase.isRTL + ", reversed=" + testCase.reversed, function() {
var $testSlider;
var $handle1;
var $handle2;
var sliderId;
var sliderOptions;
var $tick1;
var $tick2;
var $ticks;
beforeEach(function() {
sliderId = testCase.range ? 'myRangeSlider' : 'mySlider';
sliderOptions = {
id: sliderId,
step: testCase.step,
orientation: testCase.orientation,
value: testCase.value,
range: testCase.range,
lock_to_ticks: true,
reversed: testCase.reversed,
rtl: 'auto',
ticks: tickValues,
};
});
afterEach(function() {
if ($testSlider) {
$testSlider.slider('destroy');
$testSlider = null;
}
});
testCase.testData.forEach(function(testData) {
it("Should snap to closest tick (percent=" + testData.percent + ", changed=" + testData.willChange + ")", function(done) {
$testSlider = $('#'+testCase.inputId).slider(sliderOptions);
var sliderElem = $('#'+sliderId)[0];
$ticks = $('#'+sliderId).find('.slider-tick');
$handle1 = $('#'+sliderId).find('.slider-handle:first');
$handle2 = $('#'+sliderId).find('.slider-handle:last');
var coords = calcMouseEventCoords(sliderElem, testCase.orientation, testData.percent);
var checkMouseMove = function() {
var value = $testSlider.slider('getValue');
expect(value).toEqual(testData.expectedValue);
if (testCase.range) {
$tick1 = $ticks.eq(tickIndexes[testData.expectedValue[0]]);
$tick2 = $ticks.eq(tickIndexes[testData.expectedValue[1]]);
expect($handle1.css(testCase.stylePos)).toBe($tick1.css(testCase.stylePos));
expect($handle2.css(testCase.stylePos)).toBe($tick2.css(testCase.stylePos));
}
else {
$tick1 = $ticks.eq(tickIndexes[testData.expectedValue]);
expect($handle1.css(testCase.stylePos)).toBe($tick1.css(testCase.stylePos));
}
};
var checkMouseUp = function() {
var value = $testSlider.slider('getValue');
expect(value).toEqual(testData.expectedValue);
if (testCase.range) {
$tick1 = $ticks.eq(tickIndexes[testData.expectedValue[0]]);
$tick2 = $ticks.eq(tickIndexes[testData.expectedValue[1]]);
expect($handle1.css(testCase.stylePos)).toBe($tick1.css(testCase.stylePos));
expect($handle2.css(testCase.stylePos)).toBe($tick2.css(testCase.stylePos));
}
else {
$tick1 = $ticks.eq(tickIndexes[testData.expectedValue]);
expect($handle1.css(testCase.stylePos)).toBe($tick1.css(testCase.stylePos));
}
document.removeEventListener('mousemove', checkMouseMove, false);
document.removeEventListener('mouseup', checkMouseUp, false);
done();
};
sliderElem.addEventListener('mousedown', function() {
var value = $testSlider.slider('getValue');
expect(value).toEqual(testData.expectedValue);
// Check position of handles
if (testCase.range) {
$tick1 = $ticks.eq(tickIndexes[testData.expectedValue[0]]);
$tick2 = $ticks.eq(tickIndexes[testData.expectedValue[1]]);
expect($handle1.css(testCase.stylePos)).toBe($tick1.css(testCase.stylePos));
expect($handle2.css(testCase.stylePos)).toBe($tick2.css(testCase.stylePos));
}
else {
$tick1 = $ticks.eq(tickIndexes[testData.expectedValue]);
expect($handle1.css(testCase.stylePos)).toBe($tick1.css(testCase.stylePos));
}
document.addEventListener('mousemove', checkMouseMove, false);
document.addEventListener('mouseup', checkMouseUp, false);
});
sliderElem.dispatchEvent(createMouseEvent('mousedown', coords.clientX, coords.clientY));
document.dispatchEvent(createMouseEvent('mousemove', coords.clientX, coords.clientY));
document.dispatchEvent(createMouseEvent('mouseup', coords.clientX, coords.clientY));
});
});
});
});
});
/**
* The keyboard navigation tests are based on the following slider properties:
*
* initial value: 3 or [3, 7]
* ticks: [0, 3, 5, 7, 10]
* step: 0.1
*
* See description for mouse navigation tests for explanation for test setup values.
*
* These tests are setup to test for all combinations of relevant slider configurations:
* single/range, horizontal/vertical orientation, LTR/RTL, natural keys navigation,
* ordered/reversed, and all key presses (left, up, right, down).
*
* The test data is fairly straightforward and passes most configurations except for
* special cases when using natural key navigation. For these cases, the test data
* had to be 'inverted'. For example, the test data had to be inverted when the
* slider has a horizontal orientation with either RTL or reversed option set (but not both).
*
* The test logic for sliders:
* - Key to the left should change the value and lock to the tick to the left
* - Key to the right should change the value and lock to the tick to the right
*
* For range sliders, the same logic is applied except test both handle1 and handle2.
*/
describe("`lock_to_ticks: true` keyboard navigation test cases", function() {
var initialValue = 3;
var initialRangeValues = [3, 7];
var tickValues = [0, 3, 5, 7, 10];
var stepValue = 0.1;
var keyData = {
left: 37,
up: 38,
right: 39,
down: 40
};
var keys = ['left', 'up', 'right', 'down'];
var orientations = ['horizontal', 'vertical'];
var reversed = [false, true];
var naturalKeys = [false, true];
var ranged = [false, true];
var rtl = [false, true];
var rangeHandles = ['handle1', 'handle2'];
var testCases = [];
orientations.forEach(function(orientation) {
ranged.forEach(function(isRange) {
rtl.forEach(function(isRTL) {
naturalKeys.forEach(function(isNatural) {
reversed.forEach(function(isReversed) {
testCases.push({
value: isRange ? initialRangeValues : initialValue,
step: stepValue,
range: isRange,
orientation: orientation,
reversed: isReversed,
rtl: 'auto',
natural_arrow_keys: isNatural,
isRTL: isRTL,
inputId: isRTL ? 'rtlSlider' : 'testSlider1',
});
});
});
});
});
});
testCases.forEach(function(testCase) {
var handles = testCase.range ? rangeHandles : ['handle1'];
describe("orientation=" + testCase.orientation + ", range=" + testCase.range + ", rtl=" + testCase.isRTL +
", natural_arrow_keys=" + testCase.natural_arrow_keys +
", reversed=" + testCase.reversed, function() {
var $testSlider;
var $handle;
var keyboardEvent;
var sliderId;
var sliderOptions;
beforeEach(function() {
sliderId = testCase.range ? 'myRangeSlider' : 'mySlider';
sliderOptions = {
id: sliderId,
step: testCase.step,
orientation: testCase.orientation,
value: testCase.value,
range: testCase.range,
lock_to_ticks: true,
reversed: testCase.reversed,
rtl: 'auto',
natural_arrow_keys: testCase.natural_arrow_keys,
ticks: tickValues,
};
// Create keyboard event
keyboardEvent = document.createEvent('Event');
keyboardEvent.initEvent('keydown', true, true);
});
afterEach(function() {
keyboardEvent = null;
if ($testSlider) {
$testSlider.slider('destroy');
$testSlider = null;
}
});
handles.forEach(function(handleKey) {
keys.forEach(function(key) {
var isHorizontal = testCase.orientation === 'horizontal';
var isVertical = testCase.orientation === 'vertical';
var isRange = testCase.range;
var isRTL = testCase.isRTL;
var isReversed = testCase.reversed;
var isNatural = testCase.natural_arrow_keys;
var testData = {
keyCode: keyData[key],
handle1: {
expectedValue: null
},
handle2: {
expectedValue: null
}
};
if (isRange) {
// If initial value is [3, 7] Then
// These expected values will pass 96/128 tests (32 failures)
if (key === 'left' || key === 'down') {
testData.handle1.expectedValue = [0, 7];
testData.handle2.expectedValue = [3, 5];
}
else if (key === 'right' || key === 'up') {
testData.handle1.expectedValue = [5, 7];
testData.handle2.expectedValue = [3, 10];
}
// Special cases when using natural keys
if (isNatural) {
if ((isHorizontal && isReversed && !isRTL) ||
(isHorizontal && isRTL && !isReversed) ||
(isVertical && !isReversed))
{
if (key === 'left' || key === 'down') {
testData.handle1.expectedValue = [5, 7];
testData.handle2.expectedValue = [3, 10];
}
else if (key === 'right' || key === 'up') {
testData.handle1.expectedValue = [0, 7];
testData.handle2.expectedValue = [3, 5];
}
}
}
}
else {
// If initial value is 3 Then
// These expected values will pass 48/64 tests (16 failures)
if (key === 'left' || key === 'down') {
testData.handle1.expectedValue = 0;
}
else if (key === 'right' || key === 'up') {
testData.handle1.expectedValue = 5;
}
// Special cases when using natural keys
if (isNatural) {
// XOR
// One or the other needs to be true
if ((isHorizontal && isReversed && !isRTL) ||
(isHorizontal && isRTL && !isReversed) ||
(isVertical && !isReversed))
{
if (key === 'left' || key === 'down') {
testData.handle1.expectedValue = 5;
}
else if (key === 'right' || key === 'up') {
testData.handle1.expectedValue = 0;
}
}
}
}
it("Should lock to tick (" + handleKey + ", key=" + key + ")", function(done) {
$testSlider = $('#'+testCase.inputId).slider(sliderOptions);
$handle = $('#'+sliderId).find('.slider-handle:' + (handleKey === 'handle1' ? 'first' : 'last'));
$handle.on('keydown', function() {
var value = $testSlider.slider('getValue');
expect(value).toEqual(testData[handleKey].expectedValue);
done();
});
keyboardEvent.keyCode = keyboardEvent.which = testData.keyCode;
$handle[0].dispatchEvent(keyboardEvent);
});
});
});
});
});
});