tor-browser

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

test_list_keyboard.html (11654B)


      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 that List component has working keyboard interactions.
      8 -->
      9 <head>
     10  <meta charset="utf-8">
     11  <title>List component keyboard 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  <link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
     16 </head>
     17 <body>
     18 <pre id="test">
     19 <script src="head.js" type="application/javascript"></script>
     20 <script type="application/javascript">
     21 
     22 "use strict";
     23 
     24 window.onload = async function() {
     25  try {
     26    const { a, button, div } =
     27      require("devtools/client/shared/vendor/react-dom-factories");
     28    const React = browserRequire("devtools/client/shared/vendor/react");
     29    const {
     30      Simulate,
     31      findRenderedDOMComponentWithClass,
     32      findRenderedDOMComponentWithTag,
     33      scryRenderedDOMComponentsWithTag,
     34    } = browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
     35    const { List } =
     36      browserRequire("devtools/client/shared/components/List");
     37 
     38    const testItems = [
     39      {
     40        component: div({}, "Test List Item 1"),
     41        className: "list-item-1",
     42        key: "list-item-1",
     43      },
     44      {
     45        component: div({},
     46          "Test List Item 2",
     47          a({ href: "#" }, "Focusable 1"),
     48          button({ }, "Focusable 2")),
     49        className: "list-item-2",
     50        key: "list-item-2",
     51      },
     52      {
     53        component: div({}, "Test List Item 3"),
     54        className: "list-item-3",
     55        key: "list-item-3",
     56      },
     57    ];
     58 
     59    const list = React.createElement(List, {
     60      items: testItems,
     61      labelledby: "test-labelledby",
     62    });
     63 
     64    const tree = ReactDOM.render(list, document.body);
     65    const listEl = findRenderedDOMComponentWithClass(tree, "list");
     66    scryRenderedDOMComponentsWithTag(tree, "li");
     67    const defaultFocus = listEl.ownerDocument.body;
     68 
     69    function blurEl(el) {
     70      const onBlur = new Promise(r => {
     71        el.addEventListener("blur", r, {once: true});
     72      })
     73      // Simulate.blur does not seem to update the activeElement.
     74      el.blur();
     75      return onBlur;
     76    }
     77 
     78    function focusEl(el) {
     79      const onFocus = new Promise(r => {
     80        el.addEventListener("focus", r, {once: true});
     81      })
     82      // Simulate.focus does not seem to update the activeElement.
     83      el.focus();
     84      return onFocus;
     85    }
     86 
     87    function getExpectedActiveElementForFinalShiftTab() {
     88      // When tab focus mode is applied, the "Run Chrome Tests" button is not
     89      // focusable, so this Shift+Tab moves the focus to a Chrome UI element
     90      // instead of the "Run Chrome Tests" button, which makes the focus to
     91      // move to a browsing context that has a different top level browsing context
     92      // than the current browsing context. Since top level browsing contexts are
     93      // different, the activeElement in the old document is not cleared. Also
     94      // this is only the case when e10s is enabled.
     95      if (SpecialPowers.getBoolPref("accessibility.tabfocus_applies_to_xul") &&
     96          SpecialPowers.Services.appinfo.browserTabsRemoteAutostart) {
     97        return listEl;
     98      }
     99 
    100      // <body>
    101      return defaultFocus;
    102    }
    103 
    104    const tests = [{
    105      name: "Test default List state. Keyboard focus is set to document body by default.",
    106      state: { current: null, active: null },
    107      activeElement: defaultFocus,
    108    }, {
    109      name: "Current item must be set to the first list item on initial focus. " +
    110            "Keyboard focus should be set on list's conatiner (<ul>).",
    111      action: () => focusEl(listEl),
    112      activeElement: listEl,
    113      state: { current: 0 },
    114    }, {
    115      name: "Current item should remain set even when the list is blured. " +
    116            "Keyboard focus should be set back to document body.",
    117      action: () => blurEl(listEl),
    118      state: { current: 0 },
    119      activeElement: defaultFocus,
    120    }, {
    121      name: "Unset list's current state.",
    122      action: () => tree.setState({ current: null }),
    123      state: { current: null },
    124    }, {
    125      name: "Current item must be re-set again to the first list item on initial " +
    126            "focus. Keyboard focus should be set on list's conatiner (<ul>).",
    127      action: () => focusEl(listEl),
    128      activeElement: listEl,
    129      state: { current: 0 },
    130    }, {
    131      name: "Current item should be updated to next on ArrowDown.",
    132      event: { type: "keyDown", el: listEl, options: { key: "ArrowDown" }},
    133      state: { current: 1 },
    134    }, {
    135      name: "Current item should be updated to last on ArrowDown.",
    136      event: { type: "keyDown", el: listEl, options: { key: "ArrowDown" }},
    137      state: { current: 2 },
    138    }, {
    139      name: "Current item should remain on last on ArrowDown.",
    140      event: { type: "keyDown", el: listEl, options: { key: "ArrowDown" }},
    141      state: { current: 2 },
    142    }, {
    143      name: "Current item should be updated to previous on ArrowUp.",
    144      event: { type: "keyDown", el: listEl, options: { key: "ArrowUp" }},
    145      state: { current: 1 },
    146    }, {
    147      name: "Current item should be updated to first on ArrowUp.",
    148      event: { type: "keyDown", el: listEl, options: { key: "ArrowUp" }},
    149      state: { current: 0 },
    150    }, {
    151      name: "Current item should remain on first on ArrowUp.",
    152      event: { type: "keyDown", el: listEl, options: { key: "ArrowUp" }},
    153      state: { current: 0 },
    154    }, {
    155      name: "Current item should be updated to last on End.",
    156      event: { type: "keyDown", el: listEl, options: { key: "End" }},
    157      state: { current: 2 },
    158    }, {
    159      name: "Current item should be updated to first on Home.",
    160      event: { type: "keyDown", el: listEl, options: { key: "Home" }},
    161      state: { current: 0 },
    162    }, {
    163      name: "Current item should be set as active on Enter.",
    164      event: { type: "keyDown", el: listEl, options: { key: "Enter" }},
    165      state: { current: 0, active: 0 },
    166      activeElement: listEl,
    167    }, {
    168      name: "Active item should be unset on Escape.",
    169      event: { type: "keyDown", el: listEl, options: { key: "Escape" }},
    170      state: { current: 0, active: null },
    171    }, {
    172      name: "Current item should be set as active on Space.",
    173      event: { type: "keyDown", el: listEl, options: { key: " " }},
    174      state: { current: 0, active: 0 },
    175      activeElement: listEl,
    176    }, {
    177      name: "Current item should unset when focus leaves the list.",
    178      action: () => blurEl(listEl),
    179      state: { current: 0, active: null },
    180      activeElement: defaultFocus,
    181    }, {
    182      name: "Keyboard focus should be set on list's conatiner (<ul>) on focus.",
    183      action: () => focusEl(listEl),
    184      activeElement: listEl,
    185    }, {
    186      name: "Current item should be updated to next on ArrowDown.",
    187      event: { type: "keyDown", el: listEl, options: { key: "ArrowDown" }},
    188      state: { current: 1, active: null },
    189    }, {
    190      name: "Current item should be set as active on Enter. Keyboard focus should be " +
    191            "set on the first focusable element inside the list item, if available.",
    192      event: { type: "keyDown", el: listEl, options: { key: "Enter" }},
    193      state: { current: 1, active: 1 },
    194      get activeElement() {
    195        // When list item becomes active/inactive, it is replaced with a newly rendered
    196        // one.
    197        return findRenderedDOMComponentWithTag(tree, "a");
    198      },
    199    }, {
    200      name: "Keyboard focus should be set to next tabbable element inside the active " +
    201            "list item on Tab.",
    202      action() {
    203        synthesizeKey("KEY_Tab");
    204      },
    205      state: { current: 1, active: 1 },
    206      get activeElement() {
    207        // When list item becomes active/inactive, it is replaced with a newly rendered
    208        // one.
    209        return findRenderedDOMComponentWithTag(tree, "button");
    210      },
    211    }, {
    212      name: "Keyboard focus should wrap inside the list item when focused on last " +
    213            "tabbable element.",
    214      action() {
    215        synthesizeKey("KEY_Tab");
    216      },
    217      state: { current: 1, active: 1 },
    218      get activeElement() {
    219        return findRenderedDOMComponentWithTag(tree, "a");
    220      },
    221    }, {
    222      name: "Keyboard focus should wrap inside the list item when focused on first " +
    223            "tabbable element.",
    224      action() {
    225        synthesizeKey("KEY_Tab", { shiftKey: true });
    226      },
    227      state: { current: 1, active: 1 },
    228      get activeElement() {
    229        return findRenderedDOMComponentWithTag(tree, "button");
    230      },
    231    }, {
    232      name: "Active item should be unset on Escape. Focus should move back to the " +
    233            "list container.",
    234      event: { type: "keyDown", el: listEl, options: { key: "Escape" }},
    235      state: { current: 1, active: null },
    236      activeElement: listEl,
    237    }, {
    238      name: "Current item should be set as active on Space. Keyboard focus should be " +
    239            "set on the first focusable element inside the list item, if available.",
    240      event: { type: "keyDown", el: listEl, options: { key: " " }},
    241      state: { current: 1, active: 1 },
    242      get activeElement() {
    243        // When list item becomes active/inactive, it is replaced with a newly rendered
    244        // one.
    245        return findRenderedDOMComponentWithTag(tree, "a");
    246      },
    247    }, {
    248      name: "Current item should remain set even when the list is blured. " +
    249            "Keyboard focus should be set back to document body.",
    250      action: () => blurEl(listEl.ownerDocument.activeElement),
    251      state: { current: 1, active: null, },
    252      activeElement: defaultFocus,
    253    }, {
    254      name: "Keyboard focus should be set on list's conatiner (<ul>) on focus.",
    255      action: () => focusEl(listEl),
    256      state: { current: 1, active: null },
    257      activeElement: listEl,
    258    }, {
    259      name: "Current item should be updated to previous on ArrowUp.",
    260      event: { type: "keyDown", el: listEl, options: { key: "ArrowUp" }},
    261      state: { current: 0, active: null },
    262    }, {
    263      name: "Current item should be set as active on Enter.",
    264      event: { type: "keyDown", el: listEl, options: { key: "Enter" }},
    265      state: { current: 0, active: 0 },
    266      activeElement: listEl,
    267    }, {
    268      name: "Keyboard focus should move to another focusable element outside of the " +
    269            "list when there's nothing to focus on inside the list item.",
    270      action() {
    271        synthesizeKey("KEY_Tab", { shiftKey: true });
    272      },
    273      state: { current: 0, active: null },
    274      activeElement: getExpectedActiveElementForFinalShiftTab(),
    275    }];
    276 
    277    for (const test of tests) {
    278      const { action, event, state, name } = test;
    279 
    280      is(listEl, findRenderedDOMComponentWithClass(tree, "list"), "Sanity check");
    281 
    282      info(name);
    283      if (event) {
    284        const { type, options, el } = event;
    285        Simulate[type](el, options);
    286      } else if (action) {
    287        await action();
    288      }
    289 
    290      if (test.activeElement) {
    291        is(listEl.ownerDocument.activeElement, test.activeElement,
    292           "Focus is set correctly.");
    293      }
    294 
    295      for (const key in state) {
    296        is(tree.state[key], state[key], `${key} state is correct.`);
    297      }
    298    }
    299  } catch (e) {
    300    ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
    301  } finally {
    302    SimpleTest.finish();
    303  }
    304 };
    305 </script>
    306 </pre>
    307 </body>
    308 </html>