431 lines
12 KiB
JavaScript
431 lines
12 KiB
JavaScript
describe('Calculator', function() {
|
|
|
|
const KEY = {
|
|
TAB : 9,
|
|
ENTER : 13,
|
|
ALT : 18,
|
|
ESC : 27,
|
|
SPACE : 32,
|
|
LEFT : 37,
|
|
UP : 38,
|
|
RIGHT : 39,
|
|
DOWN : 40
|
|
};
|
|
|
|
beforeEach(function() {
|
|
loadFixtures('coffee/fixtures/calculator.html');
|
|
this.calculator = new Calculator;
|
|
});
|
|
|
|
describe('bind', function() {
|
|
it('bind the calculator button', function() {
|
|
expect($('.calc')).toHandleWith('click', this.calculator.toggle);
|
|
});
|
|
|
|
it('bind key up on calculator', function() {
|
|
expect($('#calculator_wrapper')).toHandle('keyup', this.calculator.handleKeyUpOnHint);
|
|
});
|
|
|
|
it('bind the help button', () =>
|
|
// This events is bind by $.click()
|
|
expect($('#calculator_hint')).toHandle('click')
|
|
);
|
|
|
|
it('bind the calculator submit', function() {
|
|
expect($('form#calculator')).toHandleWith('submit', this.calculator.calculate);
|
|
});
|
|
|
|
xit('prevent default behavior on form submit', function() {
|
|
jasmine.stubRequests();
|
|
$('form#calculator').submit(function(e) {
|
|
expect(e.isDefaultPrevented()).toBeTruthy();
|
|
return e.preventDefault();
|
|
});
|
|
return $('form#calculator').submit();
|
|
});
|
|
});
|
|
|
|
describe('toggle', function() {
|
|
it('focuses the input when toggled', function(done){
|
|
|
|
const self = this;
|
|
const focus = function(){
|
|
const deferred = $.Deferred();
|
|
|
|
// Since the focus is called asynchronously, we need to
|
|
// wait until focus() is called.
|
|
spyOn($.fn, 'focus').and.callFake(elementName => deferred.resolve());
|
|
|
|
self.calculator.toggle(jQuery.Event("click"));
|
|
|
|
return deferred.promise();
|
|
};
|
|
|
|
focus().then(
|
|
() => expect($('#calculator_wrapper #calculator_input').focus).toHaveBeenCalled()).always(done);
|
|
});
|
|
|
|
it('toggle the close button on the calculator button', function() {
|
|
this.calculator.toggle(jQuery.Event("click"));
|
|
expect($('.calc')).toHaveClass('closed');
|
|
|
|
this.calculator.toggle(jQuery.Event("click"));
|
|
expect($('.calc')).not.toHaveClass('closed');
|
|
});
|
|
});
|
|
|
|
describe('showHint', () =>
|
|
it('show the help overlay', function() {
|
|
this.calculator.showHint();
|
|
expect($('.help')).toHaveClass('shown');
|
|
expect($('.help')).toHaveAttr('aria-hidden', 'false');
|
|
})
|
|
);
|
|
|
|
|
|
describe('hideHint', () =>
|
|
it('show the help overlay', function() {
|
|
this.calculator.hideHint();
|
|
expect($('.help')).not.toHaveClass('shown');
|
|
expect($('.help')).toHaveAttr('aria-hidden', 'true');
|
|
})
|
|
);
|
|
|
|
describe('handleClickOnHintButton', () =>
|
|
it('on click hint button hint popup becomes visible ', function() {
|
|
const e = jQuery.Event('click');
|
|
$('#calculator_hint').trigger(e);
|
|
expect($('.help')).toHaveClass('shown');
|
|
})
|
|
);
|
|
|
|
describe('handleClickOnDocument', () =>
|
|
it('on click out of the hint popup it becomes hidden', function() {
|
|
this.calculator.showHint();
|
|
const e = jQuery.Event('click');
|
|
$(document).trigger(e);
|
|
expect($('.help')).not.toHaveClass('shown');
|
|
})
|
|
);
|
|
|
|
describe('handleClickOnHintPopup', () =>
|
|
it('on click of hint popup it remains visible', function() {
|
|
this.calculator.showHint();
|
|
const e = jQuery.Event('click');
|
|
$('#calculator_input_help').trigger(e);
|
|
expect($('.help')).toHaveClass('shown');
|
|
})
|
|
);
|
|
|
|
describe('selectHint', function() {
|
|
it('select correct hint item', function() {
|
|
spyOn($.fn, 'focus');
|
|
const element = $('.hint-item').eq(1);
|
|
this.calculator.selectHint(element);
|
|
|
|
expect(element.focus).toHaveBeenCalled();
|
|
expect(this.calculator.activeHint).toEqual(element);
|
|
expect(this.calculator.hintPopup).toHaveAttr('data-calculator-hint', element.attr('id'));
|
|
});
|
|
|
|
it('select the first hint if argument element is not passed', function() {
|
|
this.calculator.selectHint();
|
|
expect(this.calculator.activeHint.attr('id')).toEqual($('.hint-item').first().attr('id'));
|
|
});
|
|
|
|
it('select the first hint if argument element is empty', function() {
|
|
this.calculator.selectHint([]);
|
|
expect(this.calculator.activeHint.attr('id')).toBe($('.hint-item').first().attr('id'));
|
|
});
|
|
});
|
|
|
|
describe('prevHint', function() {
|
|
|
|
it('Prev hint item is selected', function() {
|
|
this.calculator.activeHint = $('.hint-item').eq(1);
|
|
this.calculator.prevHint();
|
|
|
|
expect(this.calculator.activeHint.attr('id')).toBe($('.hint-item').eq(0).attr('id'));
|
|
});
|
|
|
|
it('if this was the second item, select the first one', function() {
|
|
this.calculator.activeHint = $('.hint-item').eq(1);
|
|
this.calculator.prevHint();
|
|
|
|
expect(this.calculator.activeHint.attr('id')).toBe($('.hint-item').eq(0).attr('id'));
|
|
});
|
|
|
|
it('if this was the first item, select the last one', function() {
|
|
this.calculator.activeHint = $('.hint-item').eq(0);
|
|
this.calculator.prevHint();
|
|
|
|
expect(this.calculator.activeHint.attr('id')).toBe($('.hint-item').eq(2).attr('id'));
|
|
});
|
|
|
|
it('if this was the last item, select the second last', function() {
|
|
this.calculator.activeHint = $('.hint-item').eq(2);
|
|
this.calculator.prevHint();
|
|
|
|
expect(this.calculator.activeHint.attr('id')).toBe($('.hint-item').eq(1).attr('id'));
|
|
});
|
|
});
|
|
|
|
describe('nextHint', function() {
|
|
|
|
it('if this was the first item, select the second one', function() {
|
|
this.calculator.activeHint = $('.hint-item').eq(0);
|
|
this.calculator.nextHint();
|
|
|
|
expect(this.calculator.activeHint.attr('id')).toBe($('.hint-item').eq(1).attr('id'));
|
|
});
|
|
|
|
it('If this was the second item, select the last one', function() {
|
|
this.calculator.activeHint = $('.hint-item').eq(1);
|
|
this.calculator.nextHint();
|
|
|
|
expect(this.calculator.activeHint.attr('id')).toBe($('.hint-item').eq(2).attr('id'));
|
|
});
|
|
|
|
it('If this was the last item, select the first one', function() {
|
|
this.calculator.activeHint = $('.hint-item').eq(2);
|
|
this.calculator.nextHint();
|
|
|
|
expect(this.calculator.activeHint.attr('id')).toBe($('.hint-item').eq(0).attr('id'));
|
|
});
|
|
});
|
|
|
|
describe('handleKeyDown', function() {
|
|
const assertHintIsHidden = function(calc, key) {
|
|
spyOn(calc, 'hideHint');
|
|
calc.showHint();
|
|
const e = jQuery.Event('keydown', { keyCode: key });
|
|
const value = calc.handleKeyDown(e);
|
|
|
|
expect(calc.hideHint).toHaveBeenCalled;
|
|
expect(value).toBeFalsy();
|
|
expect(e.isDefaultPrevented()).toBeTruthy();
|
|
};
|
|
|
|
const assertHintIsVisible = function(calc, key) {
|
|
spyOn(calc, 'showHint');
|
|
spyOn($.fn, 'focus');
|
|
const e = jQuery.Event('keydown', { keyCode: key });
|
|
const value = calc.handleKeyDown(e);
|
|
|
|
expect(calc.showHint).toHaveBeenCalled;
|
|
expect(value).toBeFalsy();
|
|
expect(e.isDefaultPrevented()).toBeTruthy();
|
|
expect(calc.activeHint.focus).toHaveBeenCalled();
|
|
};
|
|
|
|
const assertNothingHappens = function(calc, key) {
|
|
spyOn(calc, 'showHint');
|
|
const e = jQuery.Event('keydown', { keyCode: key });
|
|
const value = calc.handleKeyDown(e);
|
|
|
|
expect(calc.showHint).not.toHaveBeenCalled;
|
|
expect(value).toBeTruthy();
|
|
expect(e.isDefaultPrevented()).toBeFalsy();
|
|
};
|
|
|
|
it('hint popup becomes hidden on press ENTER', function() {
|
|
assertHintIsHidden(this.calculator, KEY.ENTER);
|
|
});
|
|
|
|
it('hint popup becomes visible on press ENTER', function() {
|
|
assertHintIsVisible(this.calculator, KEY.ENTER);
|
|
});
|
|
|
|
it('hint popup becomes hidden on press SPACE', function() {
|
|
assertHintIsHidden(this.calculator, KEY.SPACE);
|
|
});
|
|
|
|
it('hint popup becomes visible on press SPACE', function() {
|
|
assertHintIsVisible(this.calculator, KEY.SPACE);
|
|
});
|
|
|
|
it('Nothing happens on press ALT', function() {
|
|
assertNothingHappens(this.calculator, KEY.ALT);
|
|
});
|
|
|
|
it('Nothing happens on press any other button', function() {
|
|
assertNothingHappens(this.calculator, KEY.DOWN);
|
|
});
|
|
});
|
|
|
|
describe('handleKeyDownOnHint', () =>
|
|
it('Navigation works in proper way', function() {
|
|
const calc = this.calculator;
|
|
|
|
const eventToShowHint = jQuery.Event('keydown', { keyCode: KEY.ENTER } );
|
|
$('#calculator_hint').trigger(eventToShowHint);
|
|
|
|
spyOn(calc, 'hideHint');
|
|
spyOn(calc, 'prevHint');
|
|
spyOn(calc, 'nextHint');
|
|
spyOn($.fn, 'focus');
|
|
|
|
const cases = {
|
|
left: {
|
|
event: {
|
|
keyCode: KEY.LEFT,
|
|
shiftKey: false
|
|
},
|
|
returnedValue: false,
|
|
called: {
|
|
'prevHint': calc
|
|
},
|
|
isPropagationStopped: true
|
|
},
|
|
|
|
leftWithShift: {
|
|
returnedValue: true,
|
|
event: {
|
|
keyCode: KEY.LEFT,
|
|
shiftKey: true
|
|
},
|
|
not_called: {
|
|
'prevHint': calc
|
|
}
|
|
},
|
|
|
|
up: {
|
|
event: {
|
|
keyCode: KEY.UP,
|
|
shiftKey: false
|
|
},
|
|
returnedValue: false,
|
|
called: {
|
|
'prevHint': calc
|
|
},
|
|
isPropagationStopped: true
|
|
},
|
|
|
|
upWithShift: {
|
|
returnedValue: true,
|
|
event: {
|
|
keyCode: KEY.UP,
|
|
shiftKey: true
|
|
},
|
|
not_called: {
|
|
'prevHint': calc
|
|
}
|
|
},
|
|
|
|
right: {
|
|
event: {
|
|
keyCode: KEY.RIGHT,
|
|
shiftKey: false
|
|
},
|
|
returnedValue: false,
|
|
called: {
|
|
'nextHint': calc
|
|
},
|
|
isPropagationStopped: true
|
|
},
|
|
|
|
rightWithShift: {
|
|
returnedValue: true,
|
|
event: {
|
|
keyCode: KEY.RIGHT,
|
|
shiftKey: true
|
|
},
|
|
not_called: {
|
|
'nextHint': calc
|
|
}
|
|
},
|
|
|
|
down: {
|
|
event: {
|
|
keyCode: KEY.DOWN,
|
|
shiftKey: false
|
|
},
|
|
returnedValue: false,
|
|
called: {
|
|
'nextHint': calc
|
|
},
|
|
isPropagationStopped: true
|
|
},
|
|
|
|
downWithShift: {
|
|
returnedValue: true,
|
|
event: {
|
|
keyCode: KEY.DOWN,
|
|
shiftKey: true
|
|
},
|
|
not_called: {
|
|
'nextHint': calc
|
|
}
|
|
},
|
|
|
|
esc: {
|
|
returnedValue: false,
|
|
event: {
|
|
keyCode: KEY.ESC,
|
|
shiftKey: false
|
|
},
|
|
called: {
|
|
'hideHint': calc,
|
|
'focus': $.fn
|
|
},
|
|
isPropagationStopped: true
|
|
},
|
|
|
|
alt: {
|
|
returnedValue: true,
|
|
event: {
|
|
which: KEY.ALT
|
|
},
|
|
not_called: {
|
|
'hideHint': calc,
|
|
'nextHint': calc,
|
|
'prevHint': calc
|
|
}
|
|
}
|
|
};
|
|
|
|
$.each(cases, function(key, data) {
|
|
calc.hideHint.calls.reset();
|
|
calc.prevHint.calls.reset();
|
|
calc.nextHint.calls.reset();
|
|
$.fn.focus.calls.reset();
|
|
|
|
const e = jQuery.Event('keydown', data.event || {});
|
|
const value = calc.handleKeyDownOnHint(e);
|
|
|
|
if (data.called) {
|
|
$.each(data.called, (method, obj) => expect(obj[method]).toHaveBeenCalled());
|
|
}
|
|
|
|
if (data.not_called) {
|
|
$.each(data.not_called, (method, obj) => expect(obj[method]).not.toHaveBeenCalled());
|
|
}
|
|
|
|
if (data.isPropagationStopped) {
|
|
expect(e.isPropagationStopped()).toBeTruthy();
|
|
} else {
|
|
expect(e.isPropagationStopped()).toBeFalsy();
|
|
}
|
|
|
|
expect(value).toBe(data.returnedValue);
|
|
});
|
|
})
|
|
);
|
|
|
|
describe('calculate', function() {
|
|
beforeEach(function() {
|
|
$('#calculator_input').val('1+2');
|
|
spyOn($, 'getWithPrefix').and.callFake((url, data, callback) => callback({ result: 3 }));
|
|
this.calculator.calculate();
|
|
});
|
|
|
|
it('send data to /calculate', () =>
|
|
expect($.getWithPrefix).toHaveBeenCalledWith('/calculate',
|
|
{equation: '1+2'}
|
|
, jasmine.any(Function))
|
|
);
|
|
|
|
it('update the calculator output', () => expect($('#calculator_output').val()).toEqual('3'));
|
|
});
|
|
});
|