tor-browser

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

test_tree-view_01.html (12342B)


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