blob: abf55f3da06bf49066e80e497255310f795221ee [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
suite('cr-searchable-drop-down', function() {
let dropDown;
/**
* @param {!Array<string>} items The list of items to be populated in the
* drop down.
*/
function setItems(items) {
dropDown.items = items;
Polymer.dom.flush();
}
/** @return {!NodeList} */
function getList() {
return dropDown.shadowRoot.querySelectorAll('.list-item');
}
/**
* @param {string} searchTerm The string used to filter the list of items in
* the drop down.
*/
function search(searchTerm) {
let input = dropDown.shadowRoot.querySelector('cr-input');
input.value = searchTerm;
input.fire('input');
Polymer.dom.flush();
}
function blur() {
let input = dropDown.shadowRoot.querySelector('cr-input');
input.fire('blur');
Polymer.dom.flush();
}
function down() {
MockInteractions.keyDownOn(searchInput, 'ArrowDown', [], 'ArrowDown');
}
function up() {
MockInteractions.keyDownOn(searchInput, 'ArrowUp', [], 'ArrowUp');
}
function enter() {
MockInteractions.keyDownOn(searchInput, 'Enter', [], 'Enter');
}
function tab() {
MockInteractions.keyDownOn(searchInput, 'Tab', [], 'Tab');
}
function getSelectedElement() {
return dropDown.shadowRoot.querySelector('[selected_]');
}
setup(function() {
PolymerTest.clearBody();
document.body.innerHTML = `
<p id="outside">Nothing to see here</p>
<cr-searchable-drop-down label="test drop down">
</cr-searchable-drop-down>
`;
dropDown = document.querySelector('cr-searchable-drop-down');
outsideElement = document.querySelector('#outside');
searchInput = dropDown.$.search;
Polymer.dom.flush();
});
test('correct list items', function() {
setItems(['one', 'two', 'three']);
let itemList = getList();
assertEquals(3, itemList.length);
assertEquals('one', itemList[0].textContent.trim());
assertEquals('two', itemList[1].textContent.trim());
assertEquals('three', itemList[2].textContent.trim());
});
test('filter works correctly', function() {
setItems(['cat', 'hat', 'rat', 'rake']);
search('c');
assertEquals(1, getList().length);
assertEquals('cat', getList()[0].textContent.trim());
search('at');
assertEquals(3, getList().length);
assertEquals('cat', getList()[0].textContent.trim());
assertEquals('hat', getList()[1].textContent.trim());
assertEquals('rat', getList()[2].textContent.trim());
search('ra');
assertEquals(2, getList().length);
assertEquals('rat', getList()[0].textContent.trim());
assertEquals('rake', getList()[1].textContent.trim());
});
test('value is set on click', function() {
setItems(['dog', 'cat', 'mouse']);
assertNotEquals('dog', dropDown.value);
getList()[0].click();
assertEquals('dog', dropDown.value);
// Make sure final value does not change while searching.
search('ta');
assertEquals('dog', dropDown.value);
});
// If the update-value-on-input flag is passed, final value should be whatever
// is in the search box.
test('value is set on click and on search', function() {
dropDown.updateValueOnInput = true;
setItems(['dog', 'cat', 'mouse']);
assertNotEquals('dog', dropDown.value);
getList()[0].click();
assertEquals('dog', dropDown.value);
// Make sure final value does change while searching.
search('ta');
assertEquals('ta', dropDown.value);
});
test('click closes dropdown', function() {
setItems(['dog', 'cat', 'mouse']);
// Dropdown opening is tied to focus.
dropDown.$.search.focus();
assertTrue(dropDown.$$('iron-dropdown').opened);
assertNotEquals('dog', dropDown.value);
getList()[0].click();
assertEquals('dog', dropDown.value);
assertFalse(dropDown.$$('iron-dropdown').opened);
});
test('click outside closes dropdown', function() {
setItems(['dog', 'cat', 'mouse']);
// Dropdown opening is tied to focus.
dropDown.$.search.focus();
assertTrue(dropDown.$$('iron-dropdown').opened);
assertNotEquals('dog', dropDown.value);
MockInteractions.downAndUp(outsideElement, null, null);
assertNotEquals('dog', dropDown.value);
assertFalse(dropDown.$$('iron-dropdown').opened);
});
test('tab closes dropdown', function() {
setItems(['dog', 'cat', 'mouse']);
// Dropdown opening is tied to focus.
dropDown.$.search.focus();
assertTrue(dropDown.$$('iron-dropdown').opened);
tab();
assertFalse(dropDown.$$('iron-dropdown').opened);
});
test('selected moves after up/down', function() {
setItems(['dog', 'cat', 'mouse']);
dropDown.$.search.focus();
assertTrue(dropDown.$$('iron-dropdown').opened);
assertEquals(null, getSelectedElement());
down();
assertEquals('dog', getSelectedElement().textContent.trim());
down();
assertEquals('cat', getSelectedElement().textContent.trim());
down();
assertEquals('mouse', getSelectedElement().textContent.trim());
down();
assertEquals('dog', getSelectedElement().textContent.trim());
up();
assertEquals('mouse', getSelectedElement().textContent.trim());
up();
assertEquals('cat', getSelectedElement().textContent.trim());
up();
assertEquals('dog', getSelectedElement().textContent.trim());
up();
assertEquals('mouse', getSelectedElement().textContent.trim());
enter();
assertEquals('mouse', dropDown.value);
assertFalse(dropDown.$$('iron-dropdown').opened);
});
test('focus and up selects last item', function() {
setItems(['dog', 'cat', 'mouse']);
dropDown.$.search.focus();
assertTrue(dropDown.$$('iron-dropdown').opened);
assertEquals(null, getSelectedElement());
up();
assertEquals('mouse', getSelectedElement().textContent.trim());
});
test('selected follows mouse', function() {
setItems(['dog', 'cat', 'mouse']);
dropDown.$.search.focus();
assertTrue(dropDown.$$('iron-dropdown').opened);
assertEquals(null, getSelectedElement());
MockInteractions.move(getList()[1], {x: 0, y: 0}, {x: 0, y: 0}, 1);
assertEquals('cat', getSelectedElement().textContent.trim());
MockInteractions.move(getList()[2], {x: 0, y: 0}, {x: 0, y: 0}, 1);
assertEquals('mouse', getSelectedElement().textContent.trim());
// Interacting with the keyboard should update the selected element.
up();
assertEquals('cat', getSelectedElement().textContent.trim());
// When the user moves the mouse again, the selected element should change.
MockInteractions.move(getList()[0], {x: 0, y: 0}, {x: 0, y: 0}, 1);
assertEquals('dog', getSelectedElement().textContent.trim());
});
test('input retains focus', function() {
setItems(['dog', 'cat', 'mouse']);
searchInput.focus();
assertTrue(dropDown.$$('iron-dropdown').opened);
assertEquals(searchInput, dropDown.shadowRoot.activeElement);
assertEquals(null, getSelectedElement());
down();
assertEquals('dog', getSelectedElement().textContent.trim());
assertEquals(searchInput, dropDown.shadowRoot.activeElement);
});
// If the error-message-allowed flag is passed and the |errorMessage| property
// is set, then the error message should be displayed. If the |errorMessage|
// property is not set or |errorMessageAllowed| is false, no error message
// should be displayed.
test('error message is displayed if set and allowed', function() {
dropDown.errorMessageAllowed = true;
dropDown.errorMessage = 'error message';
const input = dropDown.$.search;
assertEquals(dropDown.errorMessage, input.$.error.textContent);
assertTrue(input.invalid);
// Set |errorMessageAllowed| to false and verify no error message is shown.
dropDown.errorMessageAllowed = false;
assertNotEquals(dropDown.errorMessage, input.$.error.textContent);
assertFalse(input.invalid);
// Set |errorMessageAllowed| to true and verify it is displayed again.
dropDown.errorMessageAllowed = true;
assertEquals(dropDown.errorMessage, input.$.error.textContent);
assertTrue(input.invalid);
// Clearing |errorMessage| hides the error.
dropDown.errorMessage = '';
assertEquals(dropDown.errorMessage, input.$.error.textContent);
assertFalse(input.invalid);
});
// The show-loading attribute should determine whether or not the loading
// spinner and message are shown.
test('loading spinner is shown and hidden', function() {
const progress = dropDown.shadowRoot.querySelector('#loading-box');
assertTrue(progress.hidden);
dropDown.showLoading = true;
assertFalse(progress.hidden);
dropDown.showLoading = false;
assertTrue(progress.hidden);
});
// The readonly attribute is passed through to the inner cr-input.
test('readonly attribute', function() {
const input = dropDown.shadowRoot.querySelector('cr-input');
dropDown.readonly = true;
assertTrue(input.readonly);
dropDown.readonly = false;
assertFalse(input.readonly);
});
// When a user types in the dropdown but does not choose a valid option, the
// dropdown should revert to the previously selected option on loss of focus.
test('value resets on loss of focus', function() {
setItems(['dog', 'cat', 'mouse']);
getList()[0].click();
assertEquals('dog', searchInput.value);
// Make sure the search box value changes back to dog
search('ta');
blur();
assertEquals('dog', searchInput.value);
});
// When a user types in the dropdown but does not choose a valid option, the
// dropdown should keep the same text on loss of focus. (Only when
// isupdateValueOnInput is set to true).
test('value remains on loss of focus', function() {
dropDown.updateValueOnInput = true;
setItems(['dog', 'cat', 'mouse']);
getList()[0].click();
assertEquals('dog', searchInput.value);
// Make sure the search box value keeps the same text
search('ta');
blur();
assertEquals('ta', searchInput.value);
});
});