Add frontend assets and plugin bundles

Add the legacy frontend themes, scripts, and plugin assets required by the main SPOTA interfaces.
This commit is contained in:
Power BI Dev
2026-05-02 10:09:32 +07:00
parent efdb11db3f
commit a52c2a8462
2061 changed files with 513282 additions and 0 deletions

View File

@@ -0,0 +1,283 @@
describe('Keyboard events feature', function() {
'use strict';
var $input1,
$input2,
$input3,
$timepicker1,
$timepicker2,
$timepicker3,
tp1,
tp2,
tp3;
beforeEach(function () {
loadFixtures('timepicker.html');
$input1 = $('#timepicker1');
$timepicker1 = $input1.timepicker();
tp1 = $timepicker1.data('timepicker');
$input2 = $('#timepicker2');
$timepicker2 = $input2.timepicker({
template: 'modal',
showSeconds: true,
minuteStep: 30,
secondStep: 30,
defaultTime: false
});
tp2 = $timepicker2.data('timepicker');
$input3 = $('#timepicker3');
$timepicker3 = $input3.timepicker({
defaultTime: '23:15:20',
showMeridian: false,
showSeconds: true,
template: false
});
tp3 = $timepicker3.data('timepicker');
});
afterEach(function () {
$input1.data('timepicker').remove();
$input2.data('timepicker').remove();
$input3.data('timepicker').remove();
$input1.remove();
$input2.remove();
$input3.remove();
});
it('should be able to control element by the arrow keys', function() {
tp1.setTime('11:30 AM');
tp1.update();
$input1.trigger('focus');
if (tp1.highlightedUnit !== 'hour') {
tp1.highlightHour();
}
expect(tp1.highlightedUnit).toBe('hour', 'hour should be highlighted by default');
// hours
$input1.trigger({
'type': 'keydown',
'keyCode': 38 //up
});
expect(tp1.getTime()).toBe('12:30 PM', '1');
$input1.trigger({
'type': 'keydown',
'keyCode': 40 //down
});
expect(tp1.getTime()).toBe('11:30 AM', '2');
expect(tp1.highlightedUnit).toBe('hour', 'hour should be highlighted');
$input1.trigger({
'type': 'keydown',
'keyCode': 39 //right
});
expect(tp1.highlightedUnit).toBe('minute', 'minute should be highlighted');
//minutes
$input1.trigger({
'type': 'keydown',
'keyCode': 38 //up
});
expect(tp1.getTime()).toBe('11:45 AM', '3');
expect(tp1.highlightedUnit).toBe('minute', 'minute should be highlighted 1');
$input1.trigger({
'type': 'keydown',
'keyCode': 40 //down
});
expect(tp1.getTime()).toBe('11:30 AM', '4');
expect(tp1.highlightedUnit).toBe('minute', 'minute should be highlighted 2');
$input1.trigger({
'type': 'keydown',
'keyCode': 39 //right
});
expect(tp1.highlightedUnit).toBe('meridian', 'meridian should be highlighted');
//meridian
$input1.trigger({
'type': 'keydown',
'keyCode': 38 //up
});
expect(tp1.getTime()).toBe('11:30 PM', '5');
expect(tp1.highlightedUnit).toBe('meridian', 'meridian should be highlighted');
$input1.trigger({
'type': 'keydown',
'keyCode': 40 //down
});
expect(tp1.getTime()).toBe('11:30 AM', '6');
expect(tp1.highlightedUnit).toBe('meridian', 'meridian should be highlighted');
$input1.trigger({
'type': 'keydown',
'keyCode': 37 //left
});
expect(tp1.highlightedUnit).toBe('minute', 'minutes should be highlighted');
// minutes
$input1.trigger({
'type': 'keydown',
'keyCode': 40 //down
});
expect(tp1.getTime()).toBe('11:15 AM', '7');
$input1.trigger({
'type': 'keydown',
'keyCode': 37 //left
});
expect(tp1.highlightedUnit).toBe('hour', 'hours should be highlighted');
// hours
$input1.trigger({
'type': 'keydown',
'keyCode': 40 //down
});
expect(tp1.getTime()).toBe('10:15 AM', '8');
$input1.trigger({
'type': 'keydown',
'keyCode': 37 //left
});
expect(tp1.highlightedUnit).toBe('meridian', 'meridian should be highlighted');
// meridian
$input1.trigger({
'type': 'keydown',
'keyCode': 40 //down
});
expect(tp1.getTime()).toBe('10:15 PM', '9');
});
it('should be able to change time via widget inputs in a dropdown', function() {
var $hourInput = tp1.$widget.find('input.bootstrap-timepicker-hour'),
$minuteInput = tp1.$widget.find('input.bootstrap-timepicker-minute'),
$meridianInput = tp1.$widget.find('input.bootstrap-timepicker-meridian'),
eventCount = 0,
time;
tp1.setTime('9:30 AM');
tp1.update();
$input1.parents('div').find('.add-on').click();
$input1.timepicker().on('changeTime.timepicker', function(e) {
eventCount++;
time = e.time.value;
});
expect(tp1.isOpen).toBe(true);
$hourInput.trigger('focus');
$hourInput.autotype('{{back}}{{back}}11{{tab}}');
expect(tp1.hour).toBe(11);
expect(eventCount).toBe(1, 'incorrect update events thrown');
expect(time).toBe('11:30 AM');
$minuteInput.autotype('{{back}}{{back}}45{{tab}}');
expect(tp1.minute).toBe(45);
expect(eventCount).toBe(2, 'incorrect update events thrown');
expect(time).toBe('11:45 AM');
$meridianInput.autotype('{{back}}{{back}}pm{{tab}}');
expect(tp1.meridian).toBe('PM');
expect(eventCount).toBe(3, 'incorrect update events thrown');
expect(time).toBe('11:45 PM');
});
it('should still be empty if input is empty', function() {
$input1.autotype('{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{tab}}');
expect($input1.val()).toBe('');
});
it('should allow time to be changed via widget inputs in a modal', function() {
//tp2.setTime('9:30 AM');
//tp2.update();
//$input2.parents('div').find('.add-on').click();
//var $hourInput = $('body').find('input.bootstrap-timepicker-hour'),
//$minuteInput = $('body').find('input.bootstrap-timepicker-minute'),
//$secondInput = $('body').find('input.bootstrap-timepicker-second'),
//$meridianInput = $('body').find('input.bootstrap-timepicker-meridian');
//$hourInput.autotype('{{back}}{{back}}2');
//$hourInput.trigger({
//'type': 'keydown',
//'keyCode': 9 //tab
//});
//expect(tp2.getTime()).toBe('02:30:00 AM');
//$minuteInput.autotype('{{back}}{{back}}0');
//$minuteInput.trigger({
//'type': 'keydown',
//'keyCode': 9 //tab
//});
//expect(tp2.getTime()).toBe('02:00:00 AM');
//$secondInput.autotype('{{back}}{{back}}30');
//$secondInput.trigger({
//'type': 'keydown',
//'keyCode': 9 //tab
//});
//expect(tp2.getTime()).toBe('02:00:30 AM');
//$meridianInput.autotype('{{back}}{{back}}p');
//$meridianInput.trigger({
//'type': 'keydown',
//'keyCode': 9 //tab
//});
//expect(tp2.getTime()).toBe('02:00:30 PM');
});
it('should be 12:00 AM if 00:00 AM is entered', function() {
//$input1.autotype('{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}0:0 AM');
//$input1.trigger({
//'type': 'keydown',
//'keyCode': 9 //tab
//});
//expect(tp1.getTime()).toBe('12:00 AM');
});
it('should validate input', function() {
//var $hourInput = tp1.$widget.find('input.bootstrap-timepicker-hour'),
//$minuteInput = tp1.$widget.find('input.bootstrap-timepicker-minute'),
//$meridianInput = tp1.$widget.find('input.bootstrap-timepicker-meridian'),
//$input3 = tp3.$element;
//tp1.setTime('11:30 AM');
//tp1.update();
//$hourInput.autotype('{{back}}{{back}}13');
//tp1.updateFromWidgetInputs();
//expect(tp1.getTime()).toBe('12:30 AM');
//$minuteInput.autotype('{{back}}{{back}}60');
//tp1.updateFromWidgetInputs();
//expect(tp1.getTime()).toBe('12:59 AM');
//$meridianInput.autotype('{{back}}{{back}}dk');
//tp1.updateFromWidgetInputs();
//expect(tp1.getTime()).toBe('12:59 AM');
//$meridianInput.autotype('{{back}}{{back}}p');
//tp1.updateFromWidgetInputs();
//expect(tp1.getTime()).toBe('12:59 PM');
//$input3.autotype('{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}{{back}}25:60:60');
//tp3.updateFromElementVal();
//expect(tp3.getTime()).toBe('23:59:59');
});
});

View File

@@ -0,0 +1,262 @@
describe('Mouse events feature', function() {
'use strict';
var $input1,
$input2,
$input3,
$input4,
$timepicker1,
$timepicker2,
$timepicker3,
$timepicker4,
tp1,
tp2,
tp3,
tp4;
beforeEach(function () {
loadFixtures('timepicker.html');
$input1 = $('#timepicker1');
$timepicker1 = $input1.timepicker();
tp1 = $timepicker1.data('timepicker');
$input2 = $('#timepicker2');
$timepicker2 = $input2.timepicker({
template: 'modal',
showSeconds: true,
minuteStep: 30,
secondStep: 30,
defaultTime: false
});
tp2 = $timepicker2.data('timepicker');
$input3 = $('#timepicker3');
$timepicker3 = $input3.timepicker({
defaultTime: '23:15:20',
showMeridian: false,
showSeconds: true
});
tp3 = $timepicker3.data('timepicker');
$input4 = $('#timepicker4');
$timepicker4 = $input4.timepicker({
minuteStep: 5,
showInputs: false,
showMeridian: true,
template: 'modal',
disableFocus: true
});
tp4 = $timepicker4.data('timepicker');
});
afterEach(function () {
$input1.data('timepicker').remove();
$input2.data('timepicker').remove();
$input3.data('timepicker').remove();
$input4.data('timepicker').remove();
$input1.remove();
$input2.remove();
$input3.remove();
$input4.remove();
});
it('should be shown and trigger show events on input click', function() {
var showEvents = 0;
$input1.on('show.timepicker', function() {
showEvents++;
});
$input1.parents('div').find('.add-on').trigger('click');
expect(tp1.isOpen).toBe(true);
expect(showEvents).toBe(1);
});
it('should be hidden and trigger hide events on click outside of widget', function() {
var hideEvents = 0,
time;
$input1.val('11:30 AM');
$input1.on('hide.timepicker', function(e) {
hideEvents++;
time = e.time.value;
});
$input1.parents('div').find('.add-on').trigger('click');
expect(tp1.isOpen).toBe(true);
tp1.$widget.find('.bootstrap-timepicker-hour').trigger('mousedown');
$('body').trigger('mousedown');
expect(tp1.isOpen).toBe(false, 'widget is still open');
expect(hideEvents).toBe(1, 'hide event was not thrown once');
expect(time).toBe('11:30 AM');
});
it('should increment hour on button click', function() {
tp1.setTime('11:30 AM');
tp1.update();
tp1.$widget.find('a[data-action="incrementHour"]').trigger('click');
expect(tp1.getTime()).toBe('12:30 PM');
tp2.$widget.find('a[data-action="incrementHour"]').trigger('click');
expect(tp2.getTime()).toBe('01:00:00 AM');
});
it('should decrement hour on button click', function() {
tp1.setTime('12:30 PM');
tp1.update();
tp1.$widget.find('a[data-action="decrementHour"]').trigger('click');
expect(tp1.getTime()).toBe('11:30 AM', 'meridian isnt toggling');
tp2.$widget.find('a[data-action="incrementHour"]').trigger('click');
tp2.$widget.find('a[data-action="incrementHour"]').trigger('click');
tp2.$widget.find('a[data-action="decrementHour"]').trigger('click');
expect(tp2.getTime()).toBe('01:00:00 AM');
});
it('should increment minute on button click', function() {
tp1.setTime('11:30 AM');
tp1.update();
tp4.setTime('11:30 AM');
tp4.update();
tp1.$widget.find('a[data-action="incrementMinute"]').trigger('click');
expect(tp1.getTime()).toBe('11:45 AM');
tp2.$widget.find('a[data-action="incrementMinute"]').trigger('click');
expect(tp2.getTime()).toBe('00:30:00 AM');
$input4.trigger('click');
tp4.$widget.find('a[data-action="incrementMinute"]').trigger('click');
tp4.$widget.find('a[data-action="decrementHour"]').trigger('click');
$input4.closest('modal').find('.btn-primary').trigger('click');
expect(tp4.getTime()).toBe('10:35 AM');
expect($input4.val()).toBe('10:35 AM');
});
it('should decrement minute on button click', function() {
tp1.setTime('12:30 PM');
tp1.update();
tp4.setTime('11:30 AM');
tp4.update();
tp1.$widget.find('a[data-action="decrementMinute"]').trigger('click');
expect(tp1.getTime()).toBe('12:15 PM');
tp4.$widget.find('a[data-action="decrementMinute"]').trigger('click');
expect(tp4.getTime()).toBe('11:25 AM');
});
it('should be 11:30:00 PM if minute is decremented on empty input', function() {
tp2.$widget.find('a[data-action="decrementMinute"]').trigger('click');
expect(tp2.getTime()).toBe('11:30:00 PM');
});
it('should increment second on button click', function() {
tp2.setTime('11:30:15 AM');
tp2.update();
tp2.$widget.find('a[data-action="incrementSecond"]').trigger('click');
expect(tp2.getTime()).toBe('11:30:30 AM');
});
it('should decrement second on button click', function() {
tp2.setTime('12:30:15 PM');
tp2.update();
});
it('should be 11:30:00 PM if minute is decremented on empty input', function() {
tp2.$widget.find('a[data-action="decrementMinute"]').trigger('click');
expect(tp2.getTime()).toBe('11:30:00 PM');
});
it('should increment second on button click', function() {
tp2.setTime('11:30:15 AM');
tp2.update();
tp2.$widget.find('a[data-action="incrementSecond"]').trigger('click');
expect(tp2.getTime()).toBe('11:30:30 AM');
});
it('should decrement second on button click', function() {
tp2.setTime('12:30:15 PM');
tp2.update();
tp2.$widget.find('a[data-action="decrementSecond"]').trigger('click');
expect(tp2.getTime()).toBe('12:29:45 PM');
});
it('should toggle meridian on button click', function() {
tp1.setTime('12:30 PM');
tp1.update();
tp1.$widget.find('a[data-action="toggleMeridian"]').first().trigger('click');
expect(tp1.getTime()).toBe('12:30 AM');
tp1.$widget.find('a[data-action="toggleMeridian"]').last().trigger('click');
expect(tp1.getTime()).toBe('12:30 PM');
});
it('should trigger changeTime event if time is changed', function() {
var eventCount = 0,
time;
$input1.timepicker().on('changeTime.timepicker', function(e) {
eventCount++;
time = e.time.value;
});
tp1.setTime('11:30 AM');
expect(eventCount).toBe(1);
expect(time).toBe('11:30 AM');
tp1.$widget.find('a[data-action="incrementHour"]').trigger('click');
expect(eventCount).toBe(2);
expect(tp1.getTime()).toBe('12:30 PM');
expect(time).toBe('12:30 PM');
tp1.$widget.find('a[data-action="incrementMinute"]').trigger('click');
expect(eventCount).toBe(3);
expect(tp1.getTime()).toBe('12:45 PM');
});
it('should highlight widget inputs on click', function() {
//TODO;
//tp1.setTime('11:55 AM');
//tp1.update();
//$input1.parents('.bootstrap-timepicker').find('.add-on').trigger('click');
//expect(tp1.isOpen).toBe(true);
//expect(tp1.$widget.find('.bootstrap-timepicker-hour').val()).toBe('11');
//tp1.$widget.find('.bootstrap-timepicker-hour').trigger('click');
//var hour1 = window.getSelection().toString();
////var range = window.getSelection().getRangeAt(0);
////var hour1 = range.extractContents();
//expect(hour1).toBe('11', 'hour input not being highlighted');
//tp1.$widget.find('.bootstrap-timepicker-minute').trigger('click');
//var minute1 = window.getSelection().toString();
//expect(minute1).toBe('55', 'minute input not being highlighted');
//tp1.$widget.find('.bootstrap-timepicker-meridian').trigger('click');
//var meridian1 = window.getSelection().toString();
//expect(meridian1).toBe('AM', 'meridian input not being highlighted');
});
});

View File

@@ -0,0 +1,400 @@
describe('Timepicker feature', function() {
'use strict';
var $input1,
$input2,
$input3,
$timepicker1,
$timepicker2,
$timepicker3,
tp1,
tp2,
tp3;
beforeEach(function () {
loadFixtures('timepicker.html');
$input1 = $('#timepicker1');
$timepicker1 = $input1.timepicker();
tp1 = $timepicker1.data('timepicker');
$input2 = $('#timepicker2');
$timepicker2 = $input2.timepicker({
template: 'modal',
showSeconds: true,
minuteStep: 30,
secondStep: 30,
defaultTime: false
});
tp2 = $timepicker2.data('timepicker');
$input3 = $('#timepicker3');
$timepicker3 = $input3.timepicker({
showMeridian: false,
showSeconds: true,
defaultTime: '13:25:15'
});
tp3 = $timepicker3.data('timepicker');
});
afterEach(function () {
if ($input1.data('timepicker') !== undefined) {
$input1.data('timepicker').remove();
}
if ($input2.data('timepicker') !== undefined) {
$input2.data('timepicker').remove();
}
if ($input3.data('timepicker') !== undefined) {
$input3.data('timepicker').remove();
}
$input1.remove();
$input2.remove();
$input3.remove();
});
it('should be available on the jquery object', function() {
expect($.fn.timepicker).toBeDefined();
});
it('should be chainable', function() {
expect($timepicker1).toBe($input1);
});
it('should have sensible defaults', function() {
expect(tp1.defaultTime).toBeTruthy();
expect(tp1.minuteStep).toBe(15);
expect(tp1.secondStep).toBe(15);
expect(tp1.disableFocus).toBe(false);
expect(tp1.showSeconds).toBe(false);
expect(tp1.showInputs).toBe(true);
expect(tp1.showMeridian).toBe(true);
expect(tp1.template).toBe('dropdown');
expect(tp1.modalBackdrop).toBe(false);
expect(tp1.modalBackdrop).toBe(false);
expect(tp1.isOpen).toBe(false);
});
it('should allow user to configure defaults', function() {
expect(tp2.template).toBe('modal');
expect(tp2.minuteStep).toBe(30);
});
it('should be configurable with data attributes', function() {
$('body').append('<div id="hi" class="bootstrap-timepicker"><input id="customTimepicker" data-template="modal" data-minute-step="30" data-modal-backdrop="true" data-show-meridian="true" type="text"/></div');
var $customInput = $('body').find('#customTimepicker'),
tpCustom = $customInput.timepicker().data('timepicker');
expect($('body').find('#customTimepicker').length).toBe(1);
expect(tpCustom.template).toBe('modal');
expect(tpCustom.minuteStep).toBe(30, 'data-minute-step not working');
expect(tpCustom.modalBackdrop).toBe(true, 'data-modal-backdrop not working');
expect(tpCustom.showMeridian).toBe(true, 'data-show-meridian not working');
tpCustom.remove();
});
it('should have current time by default', function() {
var dTime = new Date(),
hour = dTime.getHours(),
minute = Math.floor(dTime.getMinutes() / tp1.minuteStep) * tp1.minuteStep;
if (hour > 12) {
hour = hour - 12;
}
expect(tp1.hour).toBe(hour);
expect(tp1.minute).toBe(minute);
});
it('should not override time with current time if value is already set', function() {
$('body').append('<div id="timepickerCustom"><input id="timepickerCustomInput" type="text" value="12:15 AM" /></div>');
var $customInput = $('#timepickerCustomInput').timepicker(),
tpCustom = $customInput.data('timepicker');
expect($customInput.val()).toBe('12:15 AM');
tpCustom.remove();
$('#timepickerCustom').remove();
});
it('should have no value if defaultTime is set to false', function() {
expect($input2.val()).toBe('');
});
it('should be able to set default time with config option', function() {
expect(tp3.getTime()).toBe('13:25:15');
});
it('should update the element and widget with the setTime method', function() {
tp2.setTime('09:15:20 AM');
expect(tp2.hour).toBe(9);
expect(tp2.minute).toBe(15);
expect(tp2.second).toBe(20);
expect(tp2.meridian).toBe('AM');
expect($input2.val()).toBe('09:15:20 AM');
expect(tp2.$widget.find('.bootstrap-timepicker-hour').val()).toBe('09');
expect(tp2.$widget.find('.bootstrap-timepicker-minute').val()).toBe('15');
expect(tp2.$widget.find('.bootstrap-timepicker-second').val()).toBe('20');
expect(tp2.$widget.find('.bootstrap-timepicker-meridian').val()).toBe('AM');
});
it('should be able to format time values into a string', function() {
expect(tp2.formatTime(3, 15, 45, 'PM')).toBe('03:15:45 PM');
});
it('should be able get & set the pickers time', function() {
tp3.setTime('23:15:20');
expect(tp3.getTime()).toBe('23:15:20');
});
it('should update picker on blur', function() {
$input1.val('10:25 AM');
expect(tp1.getTime()).not.toBe('10:25 AM');
$input1.trigger('blur');
expect(tp1.getTime()).toBe('10:25 AM');
});
it('should update element with updateElement method', function() {
tp1.hour = 10;
tp1.minute = 30;
tp1.meridian = 'PM';
tp1.updateElement();
expect($input1.val()).toBe('10:30 PM');
});
it('should update widget with updateWidget method', function() {
tp2.hour = 10;
tp2.minute = 30;
tp2.second = 15;
expect(tp2.$widget.find('.bootstrap-timepicker-hour').val()).not.toBe('10');
expect(tp2.$widget.find('.bootstrap-timepicker-minute').val()).not.toBe('30');
expect(tp2.$widget.find('.bootstrap-timepicker-second').val()).not.toBe('15');
tp2.updateWidget();
expect(tp2.$widget.find('.bootstrap-timepicker-hour').val()).toBe('10');
expect(tp2.$widget.find('.bootstrap-timepicker-minute').val()).toBe('30');
expect(tp2.$widget.find('.bootstrap-timepicker-second').val()).toBe('15');
});
it('should update picker with updateFromElementVal method', function() {
tp1.hour = 12;
tp1.minute = 12;
tp1.meridian = 'PM';
tp1.update();
$input1.val('10:30 AM');
expect(tp1.$widget.find('.bootstrap-timepicker-hour').val()).not.toBe('10');
expect(tp1.$widget.find('.bootstrap-timepicker-minute').val()).not.toBe('30');
expect(tp1.$widget.find('.bootstrap-timepicker-meridian').val()).not.toBe('AM');
expect(tp1.hour).not.toBe(10);
expect(tp1.minute).not.toBe(30);
expect(tp1.meridian).not.toBe('AM');
tp1.updateFromElementVal();
expect(tp1.$widget.find('.bootstrap-timepicker-hour').val()).toBe('10');
expect(tp1.$widget.find('.bootstrap-timepicker-minute').val()).toBe('30');
expect(tp1.$widget.find('.bootstrap-timepicker-meridian').val()).toBe('AM');
expect(tp1.hour).toBe(10);
expect(tp1.minute).toBe(30);
expect(tp1.meridian).toBe('AM');
});
it('should update picker with updateFromWidgetInputs method', function() {
tp1.hour = 12;
tp1.minute = 12;
tp1.meridian = 'PM';
tp1.update();
tp1.$widget.find('.bootstrap-timepicker-hour').val(10);
tp1.$widget.find('.bootstrap-timepicker-minute').val(30);
tp1.$widget.find('.bootstrap-timepicker-meridian').val('AM');
expect(tp1.hour).not.toBe(10);
expect(tp1.minute).not.toBe(30);
expect(tp1.meridian).not.toBe('AM');
expect($input1.val()).not.toBe('10:30 AM');
tp1.updateFromWidgetInputs();
expect(tp1.hour).toBe(10);
expect(tp1.minute).toBe(30);
expect(tp1.meridian).toBe('AM');
expect($input1.val()).toBe('10:30 AM');
});
it('should increment hours with incrementHour method', function() {
tp1.hour = 9;
tp1.incrementHour();
expect(tp1.hour).toBe(10);
});
it('should decrement hours with decrementHour method', function() {
tp1.hour = 9;
tp1.decrementHour();
expect(tp1.hour).toBe(8);
});
it('should toggle meridian if hour goes past 12', function() {
$input1.val('11:00 AM');
tp1.updateFromElementVal();
tp1.incrementHour();
expect(tp1.hour).toBe(12);
expect(tp1.minute).toBe(0);
expect(tp1.meridian).toBe('PM');
});
it('should toggle meridian if hour goes below 1', function() {
$input1.val('11:00 AM');
tp1.updateFromElementVal();
tp1.incrementHour();
expect(tp1.hour).toBe(12);
expect(tp1.minute).toBe(0);
expect(tp1.meridian).toBe('PM');
});
it('should set hour to 1 if hour increments on 12 for 12h clock', function() {
$input1.val('11:15 PM');
tp1.updateFromElementVal();
tp1.incrementHour();
tp1.incrementHour();
expect(tp1.getTime()).toBe('01:15 AM');
});
it('should set hour to 0 if hour increments on 23 for 24h clock', function() {
$input3.val('22:15:30');
tp3.updateFromElementVal();
tp3.incrementHour();
tp3.incrementHour();
expect(tp3.hour).toBe(0);
expect(tp3.minute).toBe(15);
expect(tp3.second).toBe(30);
});
it('should increment minutes with incrementMinute method', function() {
tp1.minute = 10;
tp1.incrementMinute();
expect(tp1.minute).toBe(15);
tp2.minute = 0;
tp2.incrementMinute();
expect(tp2.minute).toBe(30);
});
it('should decrement minutes with decrementMinute method', function() {
tp1.hour = 11;
tp1.minute = 0;
tp1.decrementMinute();
expect(tp1.hour).toBe(10);
expect(tp1.minute).toBe(45);
tp2.hour = 11;
tp2.minute = 0;
tp2.decrementMinute();
expect(tp2.hour).toBe(10);
expect(tp2.minute).toBe(30);
});
it('should increment hour if minutes increment past 59', function() {
$input1.val('11:55 AM');
tp1.updateFromElementVal();
tp1.incrementMinute();
tp1.update();
expect(tp1.getTime()).toBe('12:00 PM');
});
it('should toggle meridian with toggleMeridian method', function() {
tp1.meridian = 'PM';
tp1.toggleMeridian();
expect(tp1.meridian).toBe('AM');
});
it('should increment seconds with incrementSecond method', function() {
tp1.second = 0;
tp1.incrementSecond();
expect(tp1.second).toBe(15);
tp2.second = 0;
tp2.incrementSecond();
expect(tp2.second).toBe(30);
});
it('should decrement seconds with decrementSecond method', function() {
tp2.hour = 11;
tp2.minute = 0;
tp2.second = 0;
tp2.decrementSecond();
expect(tp2.minute).toBe(59);
expect(tp2.second).toBe(30);
});
it('should increment minute by 1 if seconds increment past 59', function() {
$input2.val('11:55:30 AM');
tp2.updateFromElementVal();
tp2.incrementSecond();
tp2.update();
expect(tp2.getTime()).toBe('11:56:00 AM');
});
it('should not have any remaining events if remove is called', function() {
var hideEvents = 0;
$input1.on('hide.timepicker', function() {
hideEvents++;
});
$input1.parents('div').find('.add-on').trigger('click');
$('body').trigger('mousedown');
expect(hideEvents).toBe(1);
tp1.remove();
tp2.remove();
tp3.remove();
$('body').trigger('click');
expect(hideEvents).toBe(1);
});
it('should not have the widget in the DOM if remove method is called', function() {
expect($('body')).toContain('.bootstrap-timepicker-widget');
tp1.remove();
tp2.remove();
tp3.remove();
expect($('body')).not.toContain('.bootstrap-timepicker-widget');
});
it('should be able to set time from a script', function() {
$input1.timepicker('setTime', '12:35 PM');
tp1.update();
expect(tp1.getTime()).toBe('12:35 PM');
});
it('should be able to opened from script', function() {
expect(tp1.isOpen).toBe(false);
$input1.timepicker('showWidget');
expect(tp1.isOpen).toBe(true);
});
});

View File

@@ -0,0 +1,14 @@
<div class="input-append bootstrap-timepicker">
<input id="timepicker1" type="text" class="input-small">
<span class="add-on"><i class="icon-time"></i></span>
</div>
<div class="input-append bootstrap-timepicker">
<input id="timepicker2" type="text" class="input-small">
<span class="add-on"><i class="icon-time"></i></span>
</div>
<div class="bootstrap-timepicker">
<input id="timepicker3" type="text" />
</div>
<div class="bootstrap-timepicker">
<input id="timepicker4" type="text" />
</div>

View File

@@ -0,0 +1,552 @@
var readFixtures = function() {
return jasmine.getFixtures().proxyCallTo_('read', arguments)
}
var preloadFixtures = function() {
jasmine.getFixtures().proxyCallTo_('preload', arguments)
}
var loadFixtures = function() {
jasmine.getFixtures().proxyCallTo_('load', arguments)
}
var appendLoadFixtures = function() {
jasmine.getFixtures().proxyCallTo_('appendLoad', arguments)
}
var setFixtures = function(html) {
jasmine.getFixtures().proxyCallTo_('set', arguments)
}
var appendSetFixtures = function() {
jasmine.getFixtures().proxyCallTo_('appendSet', arguments)
}
var sandbox = function(attributes) {
return jasmine.getFixtures().sandbox(attributes)
}
var spyOnEvent = function(selector, eventName) {
return jasmine.JQuery.events.spyOn(selector, eventName)
}
var preloadStyleFixtures = function() {
jasmine.getStyleFixtures().proxyCallTo_('preload', arguments)
}
var loadStyleFixtures = function() {
jasmine.getStyleFixtures().proxyCallTo_('load', arguments)
}
var appendLoadStyleFixtures = function() {
jasmine.getStyleFixtures().proxyCallTo_('appendLoad', arguments)
}
var setStyleFixtures = function(html) {
jasmine.getStyleFixtures().proxyCallTo_('set', arguments)
}
var appendSetStyleFixtures = function(html) {
jasmine.getStyleFixtures().proxyCallTo_('appendSet', arguments)
}
var loadJSONFixtures = function() {
return jasmine.getJSONFixtures().proxyCallTo_('load', arguments)
}
var getJSONFixture = function(url) {
return jasmine.getJSONFixtures().proxyCallTo_('read', arguments)[url]
}
jasmine.spiedEventsKey = function (selector, eventName) {
return [$(selector).selector, eventName].toString()
}
jasmine.getFixtures = function() {
return jasmine.currentFixtures_ = jasmine.currentFixtures_ || new jasmine.Fixtures()
}
jasmine.getStyleFixtures = function() {
return jasmine.currentStyleFixtures_ = jasmine.currentStyleFixtures_ || new jasmine.StyleFixtures()
}
jasmine.Fixtures = function() {
this.containerId = 'jasmine-fixtures'
this.fixturesCache_ = {}
this.fixturesPath = 'spec/js/fixtures'
}
jasmine.Fixtures.prototype.set = function(html) {
this.cleanUp()
this.createContainer_(html)
}
jasmine.Fixtures.prototype.appendSet= function(html) {
this.addToContainer_(html)
}
jasmine.Fixtures.prototype.preload = function() {
this.read.apply(this, arguments)
}
jasmine.Fixtures.prototype.load = function() {
this.cleanUp()
this.createContainer_(this.read.apply(this, arguments))
}
jasmine.Fixtures.prototype.appendLoad = function() {
this.addToContainer_(this.read.apply(this, arguments))
}
jasmine.Fixtures.prototype.read = function() {
var htmlChunks = []
var fixtureUrls = arguments
for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) {
htmlChunks.push(this.getFixtureHtml_(fixtureUrls[urlIndex]))
}
return htmlChunks.join('')
}
jasmine.Fixtures.prototype.clearCache = function() {
this.fixturesCache_ = {}
}
jasmine.Fixtures.prototype.cleanUp = function() {
$('#' + this.containerId).remove()
}
jasmine.Fixtures.prototype.sandbox = function(attributes) {
var attributesToSet = attributes || {}
return $('<div id="sandbox" />').attr(attributesToSet)
}
jasmine.Fixtures.prototype.createContainer_ = function(html) {
var container
if(html instanceof $) {
container = $('<div id="' + this.containerId + '" />')
container.html(html)
} else {
container = '<div id="' + this.containerId + '">' + html + '</div>'
}
$(document.body).append(container)
}
jasmine.Fixtures.prototype.addToContainer_ = function(html){
var container = $(document.body).find('#'+this.containerId).append(html)
if(!container.length){
this.createContainer_(html)
}
}
jasmine.Fixtures.prototype.getFixtureHtml_ = function(url) {
if (typeof this.fixturesCache_[url] === 'undefined') {
this.loadFixtureIntoCache_(url)
}
return this.fixturesCache_[url]
}
jasmine.Fixtures.prototype.loadFixtureIntoCache_ = function(relativeUrl) {
var url = this.makeFixtureUrl_(relativeUrl)
var request = $.ajax({
type: "GET",
url: url + "?" + new Date().getTime(),
async: false
})
this.fixturesCache_[relativeUrl] = request.responseText
}
jasmine.Fixtures.prototype.makeFixtureUrl_ = function(relativeUrl){
return this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl
}
jasmine.Fixtures.prototype.proxyCallTo_ = function(methodName, passedArguments) {
return this[methodName].apply(this, passedArguments)
}
jasmine.StyleFixtures = function() {
this.fixturesCache_ = {}
this.fixturesNodes_ = []
this.fixturesPath = 'spec/javascripts/fixtures'
}
jasmine.StyleFixtures.prototype.set = function(css) {
this.cleanUp()
this.createStyle_(css)
}
jasmine.StyleFixtures.prototype.appendSet = function(css) {
this.createStyle_(css)
}
jasmine.StyleFixtures.prototype.preload = function() {
this.read_.apply(this, arguments)
}
jasmine.StyleFixtures.prototype.load = function() {
this.cleanUp()
this.createStyle_(this.read_.apply(this, arguments))
}
jasmine.StyleFixtures.prototype.appendLoad = function() {
this.createStyle_(this.read_.apply(this, arguments))
}
jasmine.StyleFixtures.prototype.cleanUp = function() {
while(this.fixturesNodes_.length) {
this.fixturesNodes_.pop().remove()
}
}
jasmine.StyleFixtures.prototype.createStyle_ = function(html) {
var styleText = $('<div></div>').html(html).text(),
style = $('<style>' + styleText + '</style>')
this.fixturesNodes_.push(style)
$('head').append(style)
}
jasmine.StyleFixtures.prototype.clearCache = jasmine.Fixtures.prototype.clearCache
jasmine.StyleFixtures.prototype.read_ = jasmine.Fixtures.prototype.read
jasmine.StyleFixtures.prototype.getFixtureHtml_ = jasmine.Fixtures.prototype.getFixtureHtml_
jasmine.StyleFixtures.prototype.loadFixtureIntoCache_ = jasmine.Fixtures.prototype.loadFixtureIntoCache_
jasmine.StyleFixtures.prototype.makeFixtureUrl_ = jasmine.Fixtures.prototype.makeFixtureUrl_
jasmine.StyleFixtures.prototype.proxyCallTo_ = jasmine.Fixtures.prototype.proxyCallTo_
jasmine.getJSONFixtures = function() {
return jasmine.currentJSONFixtures_ = jasmine.currentJSONFixtures_ || new jasmine.JSONFixtures()
}
jasmine.JSONFixtures = function() {
this.fixturesCache_ = {}
this.fixturesPath = 'spec/javascripts/fixtures/json'
}
jasmine.JSONFixtures.prototype.load = function() {
this.read.apply(this, arguments)
return this.fixturesCache_
}
jasmine.JSONFixtures.prototype.read = function() {
var fixtureUrls = arguments
for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) {
this.getFixtureData_(fixtureUrls[urlIndex])
}
return this.fixturesCache_
}
jasmine.JSONFixtures.prototype.clearCache = function() {
this.fixturesCache_ = {}
}
jasmine.JSONFixtures.prototype.getFixtureData_ = function(url) {
this.loadFixtureIntoCache_(url)
return this.fixturesCache_[url]
}
jasmine.JSONFixtures.prototype.loadFixtureIntoCache_ = function(relativeUrl) {
var self = this
var url = this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl
$.ajax({
async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded
cache: false,
dataType: 'json',
url: url,
success: function(data) {
self.fixturesCache_[relativeUrl] = data
},
fail: function(jqXHR, status, errorThrown) {
throw Error('JSONFixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + errorThrown.message + ')')
}
})
}
jasmine.JSONFixtures.prototype.proxyCallTo_ = function(methodName, passedArguments) {
return this[methodName].apply(this, passedArguments)
}
jasmine.JQuery = function() {}
jasmine.JQuery.browserTagCaseIndependentHtml = function(html) {
return $('<div/>').append(html).html()
}
jasmine.JQuery.elementToString = function(element) {
var domEl = $(element).get(0)
if (domEl == undefined || domEl.cloneNode)
return $('<div />').append($(element).clone()).html()
else
return element.toString()
}
jasmine.JQuery.matchersClass = {}
!function(namespace) {
var data = {
spiedEvents: {},
handlers: []
}
namespace.events = {
spyOn: function(selector, eventName) {
var handler = function(e) {
data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] = e
}
$(selector).bind(eventName, handler)
data.handlers.push(handler)
return {
selector: selector,
eventName: eventName,
handler: handler,
reset: function(){
delete data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)]
}
}
},
wasTriggered: function(selector, eventName) {
return !!(data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)])
},
wasPrevented: function(selector, eventName) {
var e;
return (e = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)]) && e.isDefaultPrevented()
},
cleanUp: function() {
data.spiedEvents = {}
data.handlers = []
}
}
}(jasmine.JQuery)
!function(){
var jQueryMatchers = {
toHaveClass: function(className) {
return this.actual.hasClass(className)
},
toHaveCss: function(css){
for (var prop in css){
if (this.actual.css(prop) !== css[prop]) return false
}
return true
},
toBeVisible: function() {
return this.actual.is(':visible')
},
toBeHidden: function() {
return this.actual.is(':hidden')
},
toBeSelected: function() {
return this.actual.is(':selected')
},
toBeChecked: function() {
return this.actual.is(':checked')
},
toBeEmpty: function() {
return this.actual.is(':empty')
},
toExist: function() {
return $(document).find(this.actual).length
},
toHaveLength: function(length) {
return this.actual.length === length
},
toHaveAttr: function(attributeName, expectedAttributeValue) {
return hasProperty(this.actual.attr(attributeName), expectedAttributeValue)
},
toHaveProp: function(propertyName, expectedPropertyValue) {
return hasProperty(this.actual.prop(propertyName), expectedPropertyValue)
},
toHaveId: function(id) {
return this.actual.attr('id') == id
},
toHaveHtml: function(html) {
return this.actual.html() == jasmine.JQuery.browserTagCaseIndependentHtml(html)
},
toContainHtml: function(html){
var actualHtml = this.actual.html()
var expectedHtml = jasmine.JQuery.browserTagCaseIndependentHtml(html)
return (actualHtml.indexOf(expectedHtml) >= 0)
},
toHaveText: function(text) {
var trimmedText = $.trim(this.actual.text())
if (text && $.isFunction(text.test)) {
return text.test(trimmedText)
} else {
return trimmedText == text
}
},
toHaveValue: function(value) {
return this.actual.val() == value
},
toHaveData: function(key, expectedValue) {
return hasProperty(this.actual.data(key), expectedValue)
},
toBe: function(selector) {
return this.actual.is(selector)
},
toContain: function(selector) {
return this.actual.find(selector).length
},
toBeDisabled: function(selector){
return this.actual.is(':disabled')
},
toBeFocused: function(selector) {
return this.actual[0] === this.actual[0].ownerDocument.activeElement
},
toHandle: function(event) {
var events = $._data(this.actual.get(0), "events")
if(!events || !event || typeof event !== "string") {
return false
}
var namespaces = event.split(".")
var eventType = namespaces.shift()
var sortedNamespaces = namespaces.slice(0).sort()
var namespaceRegExp = new RegExp("(^|\\.)" + sortedNamespaces.join("\\.(?:.*\\.)?") + "(\\.|$)")
if(events[eventType] && namespaces.length) {
for(var i = 0; i < events[eventType].length; i++) {
var namespace = events[eventType][i].namespace
if(namespaceRegExp.test(namespace)) {
return true
}
}
} else {
return events[eventType] && events[eventType].length > 0
}
},
// tests the existence of a specific event binding + handler
toHandleWith: function(eventName, eventHandler) {
var stack = $._data(this.actual.get(0), "events")[eventName]
for (var i = 0; i < stack.length; i++) {
if (stack[i].handler == eventHandler) return true
}
return false
}
}
var hasProperty = function(actualValue, expectedValue) {
if (expectedValue === undefined) return actualValue !== undefined
return actualValue == expectedValue
}
var bindMatcher = function(methodName) {
var builtInMatcher = jasmine.Matchers.prototype[methodName]
jasmine.JQuery.matchersClass[methodName] = function() {
if (this.actual
&& (this.actual instanceof $
|| jasmine.isDomNode(this.actual))) {
this.actual = $(this.actual)
var result = jQueryMatchers[methodName].apply(this, arguments)
var element
if (this.actual.get && (element = this.actual.get()[0]) && !$.isWindow(element) && element.tagName !== "HTML")
this.actual = jasmine.JQuery.elementToString(this.actual)
return result
}
if (builtInMatcher) {
return builtInMatcher.apply(this, arguments)
}
return false
}
}
for(var methodName in jQueryMatchers) {
bindMatcher(methodName)
}
}()
beforeEach(function() {
this.addMatchers(jasmine.JQuery.matchersClass)
this.addMatchers({
toHaveBeenTriggeredOn: function(selector) {
this.message = function() {
return [
"Expected event " + this.actual + " to have been triggered on " + selector,
"Expected event " + this.actual + " not to have been triggered on " + selector
]
}
return jasmine.JQuery.events.wasTriggered(selector, this.actual)
}
})
this.addMatchers({
toHaveBeenTriggered: function(){
var eventName = this.actual.eventName,
selector = this.actual.selector
this.message = function() {
return [
"Expected event " + eventName + " to have been triggered on " + selector,
"Expected event " + eventName + " not to have been triggered on " + selector
]
}
return jasmine.JQuery.events.wasTriggered(selector, eventName)
}
})
this.addMatchers({
toHaveBeenPreventedOn: function(selector) {
this.message = function() {
return [
"Expected event " + this.actual + " to have been prevented on " + selector,
"Expected event " + this.actual + " not to have been prevented on " + selector
]
}
return jasmine.JQuery.events.wasPrevented(selector, this.actual)
}
})
this.addMatchers({
toHaveBeenPrevented: function() {
var eventName = this.actual.eventName,
selector = this.actual.selector
this.message = function() {
return [
"Expected event " + eventName + " to have been prevented on " + selector,
"Expected event " + eventName + " not to have been prevented on " + selector
]
}
return jasmine.JQuery.events.wasPrevented(selector, eventName)
}
})
})
afterEach(function() {
jasmine.getFixtures().cleanUp()
jasmine.getStyleFixtures().cleanUp()
jasmine.JQuery.events.cleanUp()
})