tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_searchbox-with-autocomplete.html (8928B)


      1 <!-- This Source Code Form is subject to the terms of the Mozilla Public
      2   - License, v. 2.0. If a copy of the MPL was not distributed with this
      3   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
      4 <!DOCTYPE html>
      5 <html>
      6 <!--
      7 Test the searchbox and autocomplete-popup components
      8 -->
      9 <head>
     10  <meta charset="utf-8">
     11  <title>SearchBox component test</title>
     12  <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
     13  <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
     14  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
     15 </head>
     16 <body>
     17 <script src="head.js"></script>
     18 <script>
     19 "use strict";
     20 window.onload = async function () {
     21  /**
     22   * Takes a DOMNode with its children as list items,
     23   * Typically UL > LI and each item's text value is
     24   * compared with the reference item's value as a test
     25   *
     26   * @param {Node} list - Node to be compared
     27   * @param {Array} reference - Reference array for comparison. The selected index is
     28   * highlighted as a single element array ie. ["[abc]", "ab", "abcPQR"],
     29   * Here the element "abc" is highlighted
     30   */
     31  function compareAutocompleteList(list, reference) {
     32    const delimiter = " - ";
     33    const observedList = [...list.children].map(el => {
     34      return el.classList.contains("autocomplete-selected")
     35        ? `[${el.textContent}]`
     36        : el.textContent
     37    });
     38    is(observedList.join(delimiter), reference.join(delimiter),
     39      "Autocomplete items are rendered as expected");
     40  }
     41 
     42  function compareCursorPosition(initialElement) {
     43    const initialPosition = initialElement.selectionStart;
     44    return (element) => {
     45      is(element.selectionStart, initialPosition, "Input cursor position is not changed");
     46    }
     47  }
     48 
     49  const React = browserRequire("devtools/client/shared/vendor/react");
     50  const SearchBox = React.createFactory(
     51    browserRequire("devtools/client/shared/components/SearchBox")
     52  );
     53  const { component, $ } = await createComponentTest(SearchBox, {
     54    type: "search",
     55    autocompleteProvider: (filter) => {
     56      const baseList = [
     57        "foo",
     58        "BAR",
     59        "baZ",
     60        "abc",
     61        "pqr",
     62        "xyz",
     63        "ABC",
     64        "a1",
     65        "a2",
     66        "a3",
     67        "a4",
     68        "a5",
     69      ];
     70      if (!filter) {
     71        return [];
     72      }
     73 
     74      const tokens = filter.split(/\s+/g);
     75      const lastToken = tokens[tokens.length - 1];
     76      const previousTokens = tokens.slice(0, tokens.length - 1);
     77 
     78      if (!lastToken) {
     79        return [];
     80      }
     81 
     82      return baseList
     83        .filter((item) => {
     84          return item.toLowerCase().startsWith(lastToken.toLowerCase())
     85            && item.toLowerCase() !== lastToken.toLowerCase();
     86        })
     87        .sort()
     88        .map(item => ({
     89          value: [...previousTokens, item].join(" "),
     90          displayValue: item,
     91        }));
     92    },
     93    onChange: () => null,
     94  });
     95 
     96  async function testSearchBoxWithAutocomplete() {
     97    ok(!$(".devtools-autocomplete-popup"), "Autocomplete list not visible");
     98 
     99    $(".devtools-searchinput").focus();
    100    await forceRender(component); // Wait for state update
    101    ok(!$(".devtools-autocomplete-popup"), "Autocomplete list not visible");
    102 
    103    sendString("a");
    104    await forceRender(component);
    105 
    106    compareAutocompleteList($(".devtools-autocomplete-listbox"), [
    107      "[ABC]",
    108      "a1",
    109      "a2",
    110      "a3",
    111      "a4",
    112      "a5",
    113      "abc",
    114    ]);
    115 
    116    // Blur event
    117    $(".devtools-searchinput").blur();
    118    await forceRender(component);
    119    ok(!component.state.focused, "focused state was properly set");
    120    ok(!$(".devtools-autocomplete-popup"), "Autocomplete list removed from DOM");
    121  }
    122 
    123  async function testKeyEventsWithAutocomplete() {
    124    // Clear the initial input
    125    $(".devtools-searchinput").focus();
    126    const cursorPositionIsNotChanged = compareCursorPosition($(".devtools-searchinput"));
    127 
    128    // ArrowDown
    129    synthesizeKey("KEY_ArrowDown");
    130    await forceRender(component);
    131    compareAutocompleteList($(".devtools-autocomplete-listbox"), [
    132      "ABC",
    133      "[a1]",
    134      "a2",
    135      "a3",
    136      "a4",
    137      "a5",
    138      "abc",
    139    ]);
    140    ok($(".devtools-autocomplete-listbox .autocomplete-item:nth-child(2)")
    141      .className.includes("autocomplete-selected"),
    142      "Selection class applied");
    143 
    144    // A double ArrowUp should roll back to the bottom of the list
    145    synthesizeKey("KEY_ArrowUp");
    146    synthesizeKey("KEY_ArrowUp");
    147    await forceRender(component);
    148    compareAutocompleteList($(".devtools-autocomplete-listbox"), [
    149      "ABC",
    150      "a1",
    151      "a2",
    152      "a3",
    153      "a4",
    154      "a5",
    155      "[abc]",
    156    ]);
    157    cursorPositionIsNotChanged($(".devtools-searchinput"));
    158 
    159    // PageDown should take -5 places up
    160    synthesizeKey("KEY_PageUp");
    161    await forceRender(component);
    162    compareAutocompleteList($(".devtools-autocomplete-listbox"), [
    163      "ABC",
    164      "[a1]",
    165      "a2",
    166      "a3",
    167      "a4",
    168      "a5",
    169      "abc",
    170    ]);
    171    cursorPositionIsNotChanged($(".devtools-searchinput"));
    172 
    173    // PageDown should take +5 places down
    174    synthesizeKey("KEY_PageDown");
    175    await forceRender(component);
    176    compareAutocompleteList($(".devtools-autocomplete-listbox"), [
    177      "ABC",
    178      "a1",
    179      "a2",
    180      "a3",
    181      "a4",
    182      "a5",
    183      "[abc]",
    184    ]);
    185    cursorPositionIsNotChanged($(".devtools-searchinput"));
    186 
    187    // Home should take to the top of the list
    188    synthesizeKey("KEY_Home");
    189    await forceRender(component);
    190    compareAutocompleteList($(".devtools-autocomplete-listbox"), [
    191      "[ABC]",
    192      "a1",
    193      "a2",
    194      "a3",
    195      "a4",
    196      "a5",
    197      "abc",
    198    ]);
    199    cursorPositionIsNotChanged($(".devtools-searchinput"));
    200 
    201    // End should take to the bottom of the list
    202    synthesizeKey("KEY_End");
    203    await forceRender(component);
    204    compareAutocompleteList($(".devtools-autocomplete-listbox"), [
    205      "ABC",
    206      "a1",
    207      "a2",
    208      "a3",
    209      "a4",
    210      "a5",
    211      "[abc]",
    212    ]);
    213    cursorPositionIsNotChanged($(".devtools-searchinput"));
    214 
    215    // Key down in existing state should rollover to the top
    216    synthesizeKey("KEY_ArrowDown");
    217    await forceRender(component);
    218    // Tab should select the component and hide popup
    219    synthesizeKey("KEY_Tab");
    220    await forceRender(component);
    221    is(component.state.value, "ABC", "Tab hit selects the item");
    222    ok(!$(".devtools-autocomplete-popup"), "Tab hit hides the popup");
    223 
    224    // Activate popup by removing a key
    225    synthesizeKey("KEY_Backspace");
    226    await forceRender(component);
    227    ok($(".devtools-autocomplete-popup"), "Popup is up");
    228    compareAutocompleteList($(".devtools-autocomplete-listbox"), [
    229      "[ABC]",
    230      "abc"
    231    ]);
    232 
    233    // Enter key selection
    234    synthesizeKey("KEY_ArrowUp");
    235    await forceRender(component);
    236    synthesizeKey("KEY_Enter");
    237    is(component.state.value, "abc", "Enter selection");
    238    ok(!$(".devtools-autocomplete-popup"), "Enter/Return hides the popup");
    239 
    240    // Escape should remove the autocomplete component
    241    synthesizeKey("KEY_Backspace");
    242    await forceRender(component);
    243    synthesizeKey("KEY_Escape");
    244    await forceRender(component);
    245    ok(!$(".devtools-autocomplete-popup"),
    246      "Autocomplete list removed from DOM on Escape");
    247  }
    248 
    249  async function testMouseEventsWithAutocomplete() {
    250    $(".devtools-searchinput").focus();
    251    await setState(component, {
    252      value: "",
    253      focused: true,
    254    });
    255    await forceRender(component);
    256 
    257    // ArrowDown
    258    synthesizeKey("KEY_ArrowDown");
    259    await forceRender(component);
    260    synthesizeMouseAtCenter($(".devtools-searchinput"), {}, window);
    261    await forceRender(component);
    262    is(component.state.focused, true, "Component should now be focused");
    263 
    264    sendString("pq");
    265    await forceRender(component);
    266    synthesizeMouseAtCenter(
    267      $(".devtools-autocomplete-listbox .autocomplete-item:nth-child(1)"),
    268      {}, window
    269    );
    270    await forceRender(component);
    271    is(component.state.value, "pqr", "Mouse click selects the item.");
    272    ok(!$(".devtools-autocomplete-popup"), "Mouse click on item hides the popup");
    273  }
    274 
    275  async function testTokenizedAutocomplete() {
    276    // Test for string "pqr ab" which should show list of ABC, abc
    277    sendString(" ab");
    278    await forceRender(component);
    279    compareAutocompleteList($(".devtools-autocomplete-listbox"), [
    280      "[ABC]",
    281      "abc"
    282    ]);
    283 
    284    // Select the first element, value now should be "pqr ABC"
    285    synthesizeMouseAtCenter(
    286      $(".devtools-autocomplete-listbox .autocomplete-item:nth-child(1)"),
    287      {}, window
    288    );
    289    is(component.state.value, "pqr ABC", "Post Tokenization value selection");
    290  }
    291 
    292  add_task(async function () {
    293    await testSearchBoxWithAutocomplete();
    294    await testKeyEventsWithAutocomplete();
    295    await testMouseEventsWithAutocomplete();
    296    await testTokenizedAutocomplete();
    297  });
    298 };
    299 </script>
    300 </body>
    301 </html>