2021-02-11 21:31:41 +08:00

496 lines
14 KiB
JavaScript

'use strict';
/* eslint-env mocha, jasmine */
describe('Input', function() {
var $ = require('jquery');
require('jasmine-jquery');
require('../../src/common/dom.js').element = $;
require('../../src/jquery/plugin.js');
var Input = require('../../src/autocomplete/input.js');
var _ = require('../../src/common/utils.js');
var fixtures = require('../fixtures.js');
var waitsForAndRuns = require('../helpers/waits_for.js');
var KEYS;
KEYS = {
enter: 13,
esc: 27,
tab: 9,
left: 37,
right: 39,
up: 38,
down: 40,
normal: 65 // "A" key
};
beforeEach(function() {
var $fixture;
setFixtures(fixtures.html.input + fixtures.html.hint);
$fixture = $('#jasmine-fixtures');
this.$input = $fixture.find('.aa-input');
this.$hint = $fixture.find('.aa-hint');
this.view = new Input({input: this.$input, hint: this.$hint});
});
it('should throw an error if no hint and/or input is provided', function() {
expect(noInput).toThrow();
function noInput() {
new Input({hint: '.hint'});
}
});
describe('when the blur DOM event is triggered', function() {
it('should reset the input value', function() {
this.view.setQuery('wine');
this.view.setInputValue('cheese', true);
this.$input.blur();
expect(this.$input.val()).toBe('wine');
});
it('should trigger blurred', function() {
var spy;
this.view.onSync('blurred', spy = jasmine.createSpy());
this.$input.blur();
expect(spy).toHaveBeenCalled();
});
});
describe('when the focus DOM event is triggered', function() {
it('should trigger focused', function() {
var spy;
this.view.onSync('focused', spy = jasmine.createSpy());
this.$input.focus();
expect(spy).toHaveBeenCalled();
});
});
describe('when the keydown DOM event is triggered by tab', function() {
it('should trigger tabKeyed if no modifiers were pressed', function() {
var spy;
this.view.onSync('tabKeyed', spy = jasmine.createSpy());
simulateKeyEvent(this.$input, 'keydown', KEYS.tab);
expect(spy).toHaveBeenCalled();
});
it('should not trigger tabKeyed if modifiers were pressed', function() {
var spy;
this.view.onSync('tabKeyed', spy = jasmine.createSpy());
simulateKeyEvent(this.$input, 'keydown', KEYS.tab, true);
expect(spy).not.toHaveBeenCalled();
});
it('should prevent default behavior if there is a hint', function() {
var $e;
this.view.setHint('good');
this.view.setInputValue('goo');
$e = simulateKeyEvent(this.$input, 'keydown', KEYS.tab);
expect($e.preventDefault).toHaveBeenCalled();
});
});
describe('when the keydown DOM event is triggered by esc', function() {
it('should trigger escKeyed', function() {
var spy;
this.view.onSync('escKeyed', spy = jasmine.createSpy());
simulateKeyEvent(this.$input, 'keydown', KEYS.esc);
expect(spy).toHaveBeenCalled();
});
});
describe('when the keydown DOM event is triggered by left', function() {
it('should trigger leftKeyed', function() {
var spy;
this.view.onSync('leftKeyed', spy = jasmine.createSpy());
simulateKeyEvent(this.$input, 'keydown', KEYS.left);
expect(spy).toHaveBeenCalled();
});
});
describe('when the keydown DOM event is triggered by right', function() {
it('should trigger rightKeyed', function() {
var spy;
this.view.onSync('rightKeyed', spy = jasmine.createSpy());
simulateKeyEvent(this.$input, 'keydown', KEYS.right);
expect(spy).toHaveBeenCalled();
});
});
describe('when the keydown DOM event is triggered by enter', function() {
it('should trigger enterKeyed', function() {
var spy;
this.view.onSync('enterKeyed', spy = jasmine.createSpy());
simulateKeyEvent(this.$input, 'keydown', KEYS.enter);
expect(spy).toHaveBeenCalled();
});
});
describe('when the keydown DOM event is triggered by up', function() {
it('should trigger upKeyed', function() {
var spy;
this.view.onSync('upKeyed', spy = jasmine.createSpy());
simulateKeyEvent(this.$input, 'keydown', KEYS.up);
expect(spy).toHaveBeenCalled();
});
it('should prevent default if no modifers were pressed', function() {
var $e = simulateKeyEvent(this.$input, 'keydown', KEYS.up);
expect($e.preventDefault).toHaveBeenCalled();
});
it('should not prevent default if modifers were pressed', function() {
var $e = simulateKeyEvent(this.$input, 'keydown', KEYS.up, true);
expect($e.preventDefault).not.toHaveBeenCalled();
});
});
describe('when the keydown DOM event is triggered by down', function() {
it('should trigger downKeyed', function() {
var spy;
this.view.onSync('downKeyed', spy = jasmine.createSpy());
simulateKeyEvent(this.$input, 'keydown', KEYS.down);
expect(spy).toHaveBeenCalled();
});
it('should prevent default if no modifers were pressed', function() {
var $e = simulateKeyEvent(this.$input, 'keydown', KEYS.down);
expect($e.preventDefault).toHaveBeenCalled();
});
it('should not prevent default if modifers were pressed', function() {
var $e = simulateKeyEvent(this.$input, 'keydown', KEYS.down, true);
expect($e.preventDefault).not.toHaveBeenCalled();
});
});
// NOTE: have to treat these as async because the ie polyfill acts
// in a async manner
describe('when the input DOM event is triggered', function() {
it('should update query', function(done) {
this.view.setQuery('wine');
this.view.setInputValue('cheese', true);
simulateInputEvent(this.$input);
var that = this;
waitsForAndRuns(function() { return that.view.getQuery() === 'cheese'; }, done, 100);
});
it('should trigger queryChanged if the query changed', function() {
var spy;
this.view.setQuery('wine');
this.view.setInputValue('cheese', true);
this.view.onSync('queryChanged', spy = jasmine.createSpy());
simulateInputEvent(this.$input);
expect(spy).toHaveBeenCalled();
});
it('should trigger whitespaceChagned if whitespace changed', function() {
var spy;
this.view.setQuery('wine bar');
this.view.setInputValue('wine bar', true);
this.view.onSync('whitespaceChanged', spy = jasmine.createSpy());
simulateInputEvent(this.$input);
expect(spy).toHaveBeenCalled();
});
});
describe('#focus', function() {
it('should focus the input', function() {
this.$input.blur();
this.view.focus();
expect(this.$input).toBeFocused();
});
});
describe('#blur', function() {
it('should blur the input', function() {
this.$input.focus();
this.view.blur();
expect(this.$input).not.toBeFocused();
});
});
describe('#getQuery/#setQuery', function() {
it('should act as getter/setter to the query property', function() {
this.view.setQuery('mouse');
expect(this.view.getQuery()).toBe('mouse');
});
});
describe('#getInputValue', function() {
it('should act as getter to the input value', function() {
this.$input.val('cheese');
expect(this.view.getInputValue()).toBe('cheese');
});
});
describe('#setInputValue', function() {
it('should act as setter to the input value', function() {
this.view.setInputValue('cheese');
expect(this.view.getInputValue()).toBe('cheese');
});
it('should not set the current query if null', function() {
this.view.setQuery('cheese');
this.view.setInputValue(null);
expect(this.view.getInputValue()).toBe('');
});
it('should set the current query if undefined', function() {
this.view.setQuery('cheese');
this.view.setInputValue(undefined);
expect(this.view.getInputValue()).toBe('cheese');
});
it('should trigger {query|whitespace}Changed when applicable', function() {
var spy1;
var spy2;
this.view.onSync('queryChanged', spy1 = jasmine.createSpy());
this.view.onSync('whitespaceChanged', spy2 = jasmine.createSpy());
this.view.setInputValue('cheese head');
expect(spy1).toHaveBeenCalled();
expect(spy2).not.toHaveBeenCalled();
this.view.setInputValue('cheese head');
expect(spy1.calls.count()).toBe(1);
expect(spy2).toHaveBeenCalled();
});
});
describe('#setActiveDescendant', function() {
it('should set the aria-activedescendant attribute', function() {
this.view.setActiveDescendant('abc');
expect(this.$input.attr('aria-activedescendant')).toBe('abc');
});
});
describe('#removeActiveDescendant', function() {
it('should remove the aria-activedescendant attribute', function() {
this.view.setActiveDescendant('foo');
expect(this.$input.attr('aria-activedescendant')).toBe('foo');
this.view.removeActiveDescendant('bar');
expect(this.$input.attr('aria-activedescendant')).toBeUndefined();
});
});
describe('#getHint/#setHint', function() {
it('should act as getter/setter to value of hint', function() {
this.view.setHint('mountain');
expect(this.view.getHint()).toBe('mountain');
});
});
describe('#resetInputValue', function() {
it('should reset input value to last query', function() {
this.view.setQuery('cheese');
this.view.setInputValue('wine', true);
this.view.resetInputValue();
expect(this.view.getInputValue()).toBe('cheese');
});
});
describe('#clearHint', function() {
it('should set the hint value to the empty string', function() {
this.view.setHint('cheese');
this.view.clearHint();
expect(this.view.getHint()).toBe('');
});
});
describe('#clearHintIfInvalid', function() {
it('should clear hint if input value is empty string', function() {
this.view.setInputValue('', true);
this.view.setHint('cheese');
this.view.clearHintIfInvalid();
expect(this.view.getHint()).toBe('');
});
it('should clear hint if input value is not prefix of input', function() {
this.view.setInputValue('milk', true);
this.view.setHint('cheese');
this.view.clearHintIfInvalid();
expect(this.view.getHint()).toBe('');
});
it('should clear hint if overflow exists', function() {
spyOn(this.view, 'hasOverflow').and.returnValue(true);
this.view.setInputValue('che', true);
this.view.setHint('cheese');
this.view.clearHintIfInvalid();
expect(this.view.getHint()).toBe('');
});
it('should not clear hint if input value is prefix of input', function() {
this.view.setInputValue('che', true);
this.view.setHint('cheese');
this.view.clearHintIfInvalid();
expect(this.view.getHint()).toBe('cheese');
});
});
describe('#getLanguageDirection', function() {
it('should return the language direction of the input', function() {
this.$input.css('direction', 'ltr');
expect(this.view.getLanguageDirection()).toBe('ltr');
this.$input.css('direction', 'rtl');
expect(this.view.getLanguageDirection()).toBe('rtl');
});
});
describe('#hasOverflow', function() {
it('should return true if the input has overflow text', function() {
var longStr = new Array(1000).join('a');
this.view.setInputValue(longStr);
expect(this.view.hasOverflow()).toBe(true);
});
it('should return false if the input has no overflow text', function() {
var shortStr = 'aah';
this.view.setInputValue(shortStr);
expect(this.view.hasOverflow()).toBe(false);
});
});
describe('#isCursorAtEnd', function() {
it('should return true if the text cursor is at the end', function() {
this.view.setInputValue('boo');
setCursorPosition(this.$input, 3);
expect(this.view.isCursorAtEnd()).toBe(true);
});
it('should return false if the text cursor is not at the end', function() {
this.view.setInputValue('boo');
setCursorPosition(this.$input, 1);
expect(this.view.isCursorAtEnd()).toBe(false);
});
});
describe('#destroy', function() {
it('should remove event handlers', function() {
var $input;
var $hint;
$hint = this.view.$hint;
$input = this.view.$input;
spyOn($hint, 'off');
spyOn($input, 'off');
this.view.destroy();
expect($hint.off).toHaveBeenCalledWith('.aa');
expect($input.off).toHaveBeenCalledWith('.aa');
});
it('should null out its reference to DOM elements', function() {
this.view.destroy();
expect(this.view.$hint).toBeNull();
expect(this.view.$input).toBeNull();
expect(this.view.$overflowHelper).toBeNull();
});
});
// helper functions
// ----------------
function simulateInputEvent($node) {
var $e;
var type;
type = _.isMsie() ? 'keypress' : 'input';
$e = $.Event(type);
$node.trigger($e);
}
function simulateKeyEvent($node, type, key, withModifier) {
var $e;
$e = $.Event(type, {
keyCode: key,
altKey: !!withModifier,
ctrlKey: !!withModifier,
metaKey: !!withModifier,
shiftKey: !!withModifier
});
spyOn($e, 'preventDefault');
$node.trigger($e);
return $e;
}
function setCursorPosition($input, pos) {
var input = $input[0];
var range;
if (input.setSelectionRange) {
input.focus();
input.setSelectionRange(pos, pos);
} else if (input.createTextRange) {
range = input.createTextRange();
range.collapse(true);
range.moveEnd('character', pos);
range.moveStart('character', pos);
range.select();
}
}
});