blob: e9753c9cd42a4e41ecc8444560bba346e80f2d5c [file] [log] [blame]
/**
* @license
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import '../../../test/common-test-setup-karma.js';
import './gr-autocomplete.js';
import {html} from '@polymer/polymer/lib/utils/html-tag.js';
import {dom, flush as flush$0} from '@polymer/polymer/lib/legacy/polymer.dom.js';
const basicFixture = fixtureFromTemplate(
html`<gr-autocomplete no-debounce></gr-autocomplete>`);
suite('gr-autocomplete tests', () => {
let element;
const focusOnInput = element => {
MockInteractions.pressAndReleaseKeyOn(element.$.input, 13, null,
'enter');
};
setup(() => {
element = basicFixture.instantiate();
});
test('renders', () => {
let promise;
const queryStub = sinon.spy(input => promise = Promise.resolve([
{name: input + ' 0', value: 0},
{name: input + ' 1', value: 1},
{name: input + ' 2', value: 2},
{name: input + ' 3', value: 3},
{name: input + ' 4', value: 4},
]));
element.query = queryStub;
assert.isTrue(element.$.suggestions.isHidden);
assert.equal(element.$.suggestions.$.cursor.index, -1);
focusOnInput(element);
element.text = 'blah';
assert.isTrue(queryStub.called);
element._focused = true;
return promise.then(() => {
assert.isFalse(element.$.suggestions.isHidden);
const suggestions =
dom(element.$.suggestions.root).querySelectorAll('li');
assert.equal(suggestions.length, 5);
for (let i = 0; i < 5; i++) {
assert.equal(suggestions[i].innerText.trim(), 'blah ' + i);
}
assert.notEqual(element.$.suggestions.$.cursor.index, -1);
});
});
test('selectAll', done => {
flush(() => {
const nativeInput = element._nativeInput;
const selectionStub = sinon.stub(nativeInput, 'setSelectionRange');
element.selectAll();
assert.isFalse(selectionStub.called);
element.$.input.value = 'test';
element.selectAll();
assert.isTrue(selectionStub.called);
done();
});
});
test('esc key behavior', done => {
let promise;
const queryStub = sinon.spy(() => promise = Promise.resolve([
{name: 'blah', value: 123},
]));
element.query = queryStub;
assert.isTrue(element.$.suggestions.isHidden);
element._focused = true;
element.text = 'blah';
promise.then(() => {
assert.isFalse(element.$.suggestions.isHidden);
const cancelHandler = sinon.spy();
element.addEventListener('cancel', cancelHandler);
MockInteractions.pressAndReleaseKeyOn(element.$.input, 27, null, 'esc');
assert.isFalse(cancelHandler.called);
assert.isTrue(element.$.suggestions.isHidden);
assert.equal(element._suggestions.length, 0);
MockInteractions.pressAndReleaseKeyOn(element.$.input, 27, null, 'esc');
assert.isTrue(cancelHandler.called);
done();
});
});
test('emits commit and handles cursor movement', done => {
let promise;
const queryStub = sinon.spy(input => promise = Promise.resolve([
{name: input + ' 0', value: 0},
{name: input + ' 1', value: 1},
{name: input + ' 2', value: 2},
{name: input + ' 3', value: 3},
{name: input + ' 4', value: 4},
]));
element.query = queryStub;
assert.isTrue(element.$.suggestions.isHidden);
assert.equal(element.$.suggestions.$.cursor.index, -1);
element._focused = true;
element.text = 'blah';
promise.then(() => {
assert.isFalse(element.$.suggestions.isHidden);
const commitHandler = sinon.spy();
element.addEventListener('commit', commitHandler);
assert.equal(element.$.suggestions.$.cursor.index, 0);
MockInteractions.pressAndReleaseKeyOn(element.$.input, 40, null,
'down');
assert.equal(element.$.suggestions.$.cursor.index, 1);
MockInteractions.pressAndReleaseKeyOn(element.$.input, 40, null,
'down');
assert.equal(element.$.suggestions.$.cursor.index, 2);
MockInteractions.pressAndReleaseKeyOn(element.$.input, 38, null, 'up');
assert.equal(element.$.suggestions.$.cursor.index, 1);
MockInteractions.pressAndReleaseKeyOn(element.$.input, 13, null,
'enter');
assert.equal(element.value, 1);
assert.isTrue(commitHandler.called);
assert.equal(commitHandler.getCall(0).args[0].detail.value, 1);
assert.isTrue(element.$.suggestions.isHidden);
assert.isTrue(element._focused);
done();
});
});
test('clear-on-commit behavior (off)', done => {
let promise;
const queryStub = sinon.spy(() => {
promise = Promise.resolve([{name: 'suggestion', value: 0}]);
return promise;
});
element.query = queryStub;
focusOnInput(element);
element.text = 'blah';
promise.then(() => {
const commitHandler = sinon.spy();
element.addEventListener('commit', commitHandler);
MockInteractions.pressAndReleaseKeyOn(element.$.input, 13, null,
'enter');
assert.isTrue(commitHandler.called);
assert.equal(element.text, 'suggestion');
done();
});
});
test('clear-on-commit behavior (on)', done => {
let promise;
const queryStub = sinon.spy(() => {
promise = Promise.resolve([{name: 'suggestion', value: 0}]);
return promise;
});
element.query = queryStub;
focusOnInput(element);
element.text = 'blah';
element.clearOnCommit = true;
promise.then(() => {
const commitHandler = sinon.spy();
element.addEventListener('commit', commitHandler);
MockInteractions.pressAndReleaseKeyOn(element.$.input, 13, null,
'enter');
assert.isTrue(commitHandler.called);
assert.equal(element.text, '');
done();
});
});
test('threshold guards the query', () => {
const queryStub = sinon.spy(() => Promise.resolve([]));
element.query = queryStub;
element.threshold = 2;
focusOnInput(element);
element.text = 'a';
assert.isFalse(queryStub.called);
element.text = 'ab';
assert.isTrue(queryStub.called);
});
test('noDebounce=false debounces the query', () => {
const queryStub = sinon.spy(() => Promise.resolve([]));
let callback;
const debounceStub = sinon.stub(element, 'debounce').callsFake(
(name, cb) => { callback = cb; });
element.query = queryStub;
element.noDebounce = false;
focusOnInput(element);
element.text = 'a';
assert.isFalse(queryStub.called);
assert.isTrue(debounceStub.called);
assert.equal(debounceStub.lastCall.args[2], 200);
assert.isFunction(callback);
callback();
assert.isTrue(queryStub.called);
});
test('_computeClass respects border property', () => {
assert.equal(element._computeClass(), '');
assert.equal(element._computeClass(false), '');
assert.equal(element._computeClass(true), 'borderless');
});
test('undefined or empty text results in no suggestions', () => {
element._updateSuggestions(undefined, 0, null);
assert.equal(element._suggestions.length, 0);
});
test('when focused', done => {
let promise;
const queryStub = sinon.stub()
.returns(promise = Promise.resolve([
{name: 'suggestion', value: 0},
]));
element.query = queryStub;
element.suggestOnlyWhenFocus = true;
focusOnInput(element);
element.text = 'bla';
assert.equal(element._focused, true);
flushAsynchronousOperations();
promise.then(() => {
assert.equal(element._suggestions.length, 1);
assert.equal(queryStub.notCalled, false);
done();
});
});
test('when not focused', done => {
let promise;
const queryStub = sinon.stub()
.returns(promise = Promise.resolve([
{name: 'suggestion', value: 0},
]));
element.query = queryStub;
element.suggestOnlyWhenFocus = true;
element.text = 'bla';
assert.equal(element._focused, false);
flushAsynchronousOperations();
promise.then(() => {
assert.equal(element._suggestions.length, 0);
done();
});
});
test('suggestions should not carry over', done => {
let promise;
const queryStub = sinon.stub()
.returns(promise = Promise.resolve([
{name: 'suggestion', value: 0},
]));
element.query = queryStub;
focusOnInput(element);
element.text = 'bla';
flushAsynchronousOperations();
promise.then(() => {
assert.equal(element._suggestions.length, 1);
element._updateSuggestions('', 0, false);
assert.equal(element._suggestions.length, 0);
done();
});
});
test('multi completes only the last part of the query', done => {
let promise;
const queryStub = sinon.stub()
.returns(promise = Promise.resolve([
{name: 'suggestion', value: 0},
]));
element.query = queryStub;
focusOnInput(element);
element.text = 'blah blah';
element.multi = true;
promise.then(() => {
const commitHandler = sinon.spy();
element.addEventListener('commit', commitHandler);
MockInteractions.pressAndReleaseKeyOn(element.$.input, 13, null,
'enter');
assert.isTrue(commitHandler.called);
assert.equal(element.text, 'blah 0');
done();
});
});
test('tabComplete flag functions', () => {
// commitHandler checks for the commit event, whereas commitSpy checks for
// the _commit function of the element.
const commitHandler = sinon.spy();
element.addEventListener('commit', commitHandler);
const commitSpy = sinon.spy(element, '_commit');
element._focused = true;
element._suggestions = ['tunnel snakes rule!'];
element.tabComplete = false;
MockInteractions.pressAndReleaseKeyOn(element.$.input, 9, null, 'tab');
assert.isFalse(commitHandler.called);
assert.isFalse(commitSpy.called);
assert.isFalse(element._focused);
element.tabComplete = true;
element._focused = true;
MockInteractions.pressAndReleaseKeyOn(element.$.input, 9, null, 'tab');
assert.isFalse(commitHandler.called);
assert.isTrue(commitSpy.called);
assert.isTrue(element._focused);
});
test('_focused flag properly triggered', done => {
flush(() => {
assert.isFalse(element._focused);
const input = element.shadowRoot
.querySelector('paper-input').inputElement;
MockInteractions.focus(input);
assert.isTrue(element._focused);
done();
});
});
test('search icon shows with showSearchIcon property', done => {
flush(() => {
assert.equal(getComputedStyle(element.shadowRoot
.querySelector('iron-icon')).display,
'none');
element.showSearchIcon = true;
assert.notEqual(getComputedStyle(element.shadowRoot
.querySelector('iron-icon')).display,
'none');
done();
});
});
test('vertical offset overridden by param if it exists', () => {
assert.equal(element.$.suggestions.verticalOffset, 31);
element.verticalOffset = 30;
assert.equal(element.$.suggestions.verticalOffset, 30);
});
test('_focused flag shows/hides the suggestions', () => {
const openStub = sinon.stub(element.$.suggestions, 'open');
const closedStub = sinon.stub(element.$.suggestions, 'close');
element._suggestions = ['hello', 'its me'];
assert.isFalse(openStub.called);
assert.isTrue(closedStub.calledOnce);
element._focused = true;
assert.isTrue(openStub.calledOnce);
element._suggestions = [];
assert.isTrue(closedStub.calledTwice);
assert.isTrue(openStub.calledOnce);
});
test('_handleInputCommit with autocomplete hidden does nothing without' +
'without allowNonSuggestedValues', () => {
const commitStub = sinon.stub(element, '_commit');
element.$.suggestions.isHidden = true;
element._handleInputCommit();
assert.isFalse(commitStub.called);
});
test('_handleInputCommit with autocomplete hidden with' +
'allowNonSuggestedValues', () => {
const commitStub = sinon.stub(element, '_commit');
element.allowNonSuggestedValues = true;
element.$.suggestions.isHidden = true;
element._handleInputCommit();
assert.isTrue(commitStub.called);
});
test('_handleInputCommit with autocomplete open calls commit', () => {
const commitStub = sinon.stub(element, '_commit');
element.$.suggestions.isHidden = false;
element._handleInputCommit();
assert.isTrue(commitStub.calledOnce);
});
test('_handleInputCommit with autocomplete open calls commit' +
'with allowNonSuggestedValues', () => {
const commitStub = sinon.stub(element, '_commit');
element.allowNonSuggestedValues = true;
element.$.suggestions.isHidden = false;
element._handleInputCommit();
assert.isTrue(commitStub.calledOnce);
});
test('issue 8655', () => {
function makeSuggestion(s) { return {name: s, text: s, value: s}; }
const keydownSpy = sinon.spy(element, '_handleKeydown');
element.setText('file:');
element._suggestions =
[makeSuggestion('file:'), makeSuggestion('-file:')];
MockInteractions.pressAndReleaseKeyOn(element.$.input, 88, null, 'x');
// Must set the value, because the MockInteraction does not.
element.$.input.value = 'file:x';
assert.isTrue(keydownSpy.calledOnce);
MockInteractions.pressAndReleaseKeyOn(
element.$.input,
13,
null,
'enter'
);
assert.isTrue(keydownSpy.calledTwice);
assert.equal(element.text, 'file:x');
});
suite('focus', () => {
let commitSpy;
let focusSpy;
setup(() => {
commitSpy = sinon.spy(element, '_commit');
});
test('enter does not call focus', () => {
element._suggestions = ['sugar bombs'];
focusSpy = sinon.spy(element, 'focus');
MockInteractions.pressAndReleaseKeyOn(element.$.input, 13, null,
'enter');
flushAsynchronousOperations();
assert.isTrue(commitSpy.called);
assert.isFalse(focusSpy.called);
assert.equal(element._suggestions.length, 0);
});
test('tab in input, tabComplete = true', () => {
focusSpy = sinon.spy(element, 'focus');
const commitHandler = sinon.stub();
element.addEventListener('commit', commitHandler);
element.tabComplete = true;
element._suggestions = ['tunnel snakes drool'];
MockInteractions.pressAndReleaseKeyOn(element.$.input, 9, null, 'tab');
flushAsynchronousOperations();
assert.isTrue(commitSpy.called);
assert.isTrue(focusSpy.called);
assert.isFalse(commitHandler.called);
assert.equal(element._suggestions.length, 0);
});
test('tab in input, tabComplete = false', () => {
element._suggestions = ['sugar bombs'];
focusSpy = sinon.spy(element, 'focus');
MockInteractions.pressAndReleaseKeyOn(element.$.input, 9, null, 'tab');
flushAsynchronousOperations();
assert.isFalse(commitSpy.called);
assert.isFalse(focusSpy.called);
assert.equal(element._suggestions.length, 1);
});
test('tab on suggestion, tabComplete = false', () => {
element._suggestions = [{name: 'sugar bombs'}];
element._focused = true;
// When tabComplete is false, do not focus.
element.tabComplete = false;
focusSpy = sinon.spy(element, 'focus');
flush$0();
assert.isFalse(element.$.suggestions.isHidden);
MockInteractions.pressAndReleaseKeyOn(
element.$.suggestions.shadowRoot
.querySelector('li:first-child'), 9, null, 'tab');
flushAsynchronousOperations();
assert.isFalse(commitSpy.called);
assert.isFalse(element._focused);
});
test('tab on suggestion, tabComplete = true', () => {
element._suggestions = [{name: 'sugar bombs'}];
element._focused = true;
// When tabComplete is true, focus.
element.tabComplete = true;
focusSpy = sinon.spy(element, 'focus');
flush$0();
assert.isFalse(element.$.suggestions.isHidden);
MockInteractions.pressAndReleaseKeyOn(
element.$.suggestions.shadowRoot
.querySelector('li:first-child'), 9, null, 'tab');
flushAsynchronousOperations();
assert.isTrue(commitSpy.called);
assert.isTrue(element._focused);
});
test('tap on suggestion commits, does not call focus', () => {
focusSpy = sinon.spy(element, 'focus');
element._focused = true;
element._suggestions = [{name: 'first suggestion'}];
flush$0();
assert.isFalse(element.$.suggestions.isHidden);
MockInteractions.tap(element.$.suggestions.shadowRoot
.querySelector('li:first-child'));
flushAsynchronousOperations();
assert.isFalse(focusSpy.called);
assert.isTrue(commitSpy.called);
assert.isTrue(element.$.suggestions.isHidden);
});
});
test('input-keydown event fired', () => {
const listener = sinon.spy();
element.addEventListener('input-keydown', listener);
MockInteractions.pressAndReleaseKeyOn(element.$.input, 9, null, 'tab');
flushAsynchronousOperations();
assert.isTrue(listener.called);
});
test('enter with modifier does not complete', () => {
const handleSpy = sinon.spy(element, '_handleKeydown');
const commitStub = sinon.stub(element, '_handleInputCommit');
MockInteractions.pressAndReleaseKeyOn(
element.$.input, 13, 'ctrl', 'enter');
assert.isTrue(handleSpy.called);
assert.isFalse(commitStub.called);
MockInteractions.pressAndReleaseKeyOn(
element.$.input, 13, null, 'enter');
assert.isTrue(commitStub.called);
});
suite('warnUncommitted', () => {
let inputClassList;
setup(() => {
inputClassList = element.$.input.classList;
});
test('enabled', () => {
element.warnUncommitted = true;
element.text = 'blah blah blah';
MockInteractions.blur(element.$.input);
assert.isTrue(inputClassList.contains('warnUncommitted'));
MockInteractions.focus(element.$.input);
assert.isFalse(inputClassList.contains('warnUncommitted'));
});
test('disabled', () => {
element.warnUncommitted = false;
element.text = 'blah blah blah';
MockInteractions.blur(element.$.input);
assert.isFalse(inputClassList.contains('warnUncommitted'));
});
test('no text', () => {
element.warnUncommitted = true;
element.text = '';
MockInteractions.blur(element.$.input);
assert.isFalse(inputClassList.contains('warnUncommitted'));
});
});
});