Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE html>
<html>
<!--
Test the searchbox and autocomplete-popup components
-->
<head>
<meta charset="utf-8">
<title>SearchBox component test</title>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
<body>
<script src="head.js"></script>
<script>
"use strict";
window.onload = async function () {
/**
* Takes a DOMNode with its children as list items,
* Typically UL > LI and each item's text value is
* compared with the reference item's value as a test
*
* @params {Node} - Node to be compared
* @reference {array} - Reference array for comparison. The selected index is
* highlighted as a single element array ie. ["[abc]", "ab", "abcPQR"],
* Here the element "abc" is highlighted
*/
function compareAutocompleteList(list, reference) {
const delimiter = " - ";
const observedList = [...list.children].map(el => {
return el.classList.contains("autocomplete-selected")
? `[${el.textContent}]`
: el.textContent
});
is(observedList.join(delimiter), reference.join(delimiter),
"Autocomplete items are rendered as expected");
}
function compareCursorPosition(initialElement) {
const initialPosition = initialElement.selectionStart;
return (element) => {
is(element.selectionStart, initialPosition, "Input cursor position is not changed");
}
}
const React = browserRequire("devtools/client/shared/vendor/react");
const SearchBox = React.createFactory(
browserRequire("devtools/client/shared/components/SearchBox")
);
const { component, $ } = await createComponentTest(SearchBox, {
type: "search",
autocompleteProvider: (filter) => {
const baseList = [
"foo",
"BAR",
"baZ",
"abc",
"pqr",
"xyz",
"ABC",
"a1",
"a2",
"a3",
"a4",
"a5",
];
if (!filter) {
return [];
}
const tokens = filter.split(/\s+/g);
const lastToken = tokens[tokens.length - 1];
const previousTokens = tokens.slice(0, tokens.length - 1);
if (!lastToken) {
return [];
}
return baseList
.filter((item) => {
return item.toLowerCase().startsWith(lastToken.toLowerCase())
&& item.toLowerCase() !== lastToken.toLowerCase();
})
.sort()
.map(item => ({
value: [...previousTokens, item].join(" "),
displayValue: item,
}));
},
onChange: () => null,
});
async function testSearchBoxWithAutocomplete() {
ok(!$(".devtools-autocomplete-popup"), "Autocomplete list not visible");
$(".devtools-searchinput").focus();
await forceRender(component); // Wait for state update
ok(!$(".devtools-autocomplete-popup"), "Autocomplete list not visible");
sendString("a");
await forceRender(component);
compareAutocompleteList($(".devtools-autocomplete-listbox"), [
"[ABC]",
"a1",
"a2",
"a3",
"a4",
"a5",
"abc",
]);
// Blur event
$(".devtools-searchinput").blur();
await forceRender(component);
ok(!component.state.focused, "focused state was properly set");
ok(!$(".devtools-autocomplete-popup"), "Autocomplete list removed from DOM");
}
async function testKeyEventsWithAutocomplete() {
// Clear the initial input
$(".devtools-searchinput").focus();
const cursorPositionIsNotChanged = compareCursorPosition($(".devtools-searchinput"));
// ArrowDown
synthesizeKey("KEY_ArrowDown");
await forceRender(component);
compareAutocompleteList($(".devtools-autocomplete-listbox"), [
"ABC",
"[a1]",
"a2",
"a3",
"a4",
"a5",
"abc",
]);
ok($(".devtools-autocomplete-listbox .autocomplete-item:nth-child(2)")
.className.includes("autocomplete-selected"),
"Selection class applied");
// A double ArrowUp should roll back to the bottom of the list
synthesizeKey("KEY_ArrowUp");
synthesizeKey("KEY_ArrowUp");
await forceRender(component);
compareAutocompleteList($(".devtools-autocomplete-listbox"), [
"ABC",
"a1",
"a2",
"a3",
"a4",
"a5",
"[abc]",
]);
cursorPositionIsNotChanged($(".devtools-searchinput"));
// PageDown should take -5 places up
synthesizeKey("KEY_PageUp");
await forceRender(component);
compareAutocompleteList($(".devtools-autocomplete-listbox"), [
"ABC",
"[a1]",
"a2",
"a3",
"a4",
"a5",
"abc",
]);
cursorPositionIsNotChanged($(".devtools-searchinput"));
// PageDown should take +5 places down
synthesizeKey("KEY_PageDown");
await forceRender(component);
compareAutocompleteList($(".devtools-autocomplete-listbox"), [
"ABC",
"a1",
"a2",
"a3",
"a4",
"a5",
"[abc]",
]);
cursorPositionIsNotChanged($(".devtools-searchinput"));
// Home should take to the top of the list
synthesizeKey("KEY_Home");
await forceRender(component);
compareAutocompleteList($(".devtools-autocomplete-listbox"), [
"[ABC]",
"a1",
"a2",
"a3",
"a4",
"a5",
"abc",
]);
cursorPositionIsNotChanged($(".devtools-searchinput"));
// End should take to the bottom of the list
synthesizeKey("KEY_End");
await forceRender(component);
compareAutocompleteList($(".devtools-autocomplete-listbox"), [
"ABC",
"a1",
"a2",
"a3",
"a4",
"a5",
"[abc]",
]);
cursorPositionIsNotChanged($(".devtools-searchinput"));
// Key down in existing state should rollover to the top
synthesizeKey("KEY_ArrowDown");
await forceRender(component);
// Tab should select the component and hide popup
synthesizeKey("KEY_Tab");
await forceRender(component);
is(component.state.value, "ABC", "Tab hit selects the item");
ok(!$(".devtools-autocomplete-popup"), "Tab hit hides the popup");
// Activate popup by removing a key
synthesizeKey("KEY_Backspace");
await forceRender(component);
ok($(".devtools-autocomplete-popup"), "Popup is up");
compareAutocompleteList($(".devtools-autocomplete-listbox"), [
"[ABC]",
"abc"
]);
// Enter key selection
synthesizeKey("KEY_ArrowUp");
await forceRender(component);
synthesizeKey("KEY_Enter");
is(component.state.value, "abc", "Enter selection");
ok(!$(".devtools-autocomplete-popup"), "Enter/Return hides the popup");
// Escape should remove the autocomplete component
synthesizeKey("KEY_Backspace");
await forceRender(component);
synthesizeKey("KEY_Escape");
await forceRender(component);
ok(!$(".devtools-autocomplete-popup"),
"Autocomplete list removed from DOM on Escape");
}
async function testMouseEventsWithAutocomplete() {
$(".devtools-searchinput").focus();
await setState(component, {
value: "",
focused: true,
});
await forceRender(component);
// ArrowDown
synthesizeKey("KEY_ArrowDown");
await forceRender(component);
synthesizeMouseAtCenter($(".devtools-searchinput"), {}, window);
await forceRender(component);
is(component.state.focused, true, "Component should now be focused");
sendString("pq");
await forceRender(component);
synthesizeMouseAtCenter(
$(".devtools-autocomplete-listbox .autocomplete-item:nth-child(1)"),
{}, window
);
await forceRender(component);
is(component.state.value, "pqr", "Mouse click selects the item.");
ok(!$(".devtools-autocomplete-popup"), "Mouse click on item hides the popup");
}
async function testTokenizedAutocomplete() {
// Test for string "pqr ab" which should show list of ABC, abc
sendString(" ab");
await forceRender(component);
compareAutocompleteList($(".devtools-autocomplete-listbox"), [
"[ABC]",
"abc"
]);
// Select the first element, value now should be "pqr ABC"
synthesizeMouseAtCenter(
$(".devtools-autocomplete-listbox .autocomplete-item:nth-child(1)"),
{}, window
);
is(component.state.value, "pqr ABC", "Post Tokenization value selection");
}
add_task(async function () {
await testSearchBoxWithAutocomplete();
await testKeyEventsWithAutocomplete();
await testMouseEventsWithAutocomplete();
await testTokenizedAutocomplete();
});
};
</script>
</body>
</html>