test_tree_14.html (9846B)
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 Tree component has working keyboard interactions. 8 --> 9 <head> 10 <meta charset="utf-8"> 11 <title>Tree 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 </head> 16 <body> 17 <pre id="test"> 18 <script src="head.js" type="application/javascript"></script> 19 <script type="application/javascript"> 20 21 "use strict"; 22 23 window.onload = async function() { 24 try { 25 const { a, button, div } = 26 require("devtools/client/shared/vendor/react-dom-factories"); 27 const { createFactory } = browserRequire("devtools/client/shared/vendor/react"); 28 const { 29 Simulate, 30 findRenderedDOMComponentWithClass, 31 findRenderedDOMComponentWithTag, 32 } = browserRequire("devtools/client/shared/vendor/react-dom-test-utils"); 33 const Tree = createFactory( 34 browserRequire("devtools/client/shared/components/VirtualizedTree")); 35 36 let gTree, gFocused, gActive; 37 function renderTree(props = {}) { 38 let toggle = true; 39 const treeProps = { 40 ...TEST_TREE_INTERFACE, 41 onFocus: x => { 42 gFocused = x; 43 renderTree({ focused: gFocused, active: gActive }); 44 }, 45 onActivate: x => { 46 gActive = x; 47 renderTree({ focused: gFocused, active: gActive }); 48 }, 49 renderItem: (x, depth, focused) => { 50 toggle = !toggle; 51 return toggle ? 52 (div( 53 {}, 54 `${"-".repeat(depth)}${x}:${focused}`, 55 a({ href: "#" }, "Focusable 1"), 56 button({ }, "Focusable 2"), 57 "\n", 58 ) 59 ) : `${"-".repeat(depth)}${x}:${focused}`; 60 }, 61 ...props 62 }; 63 64 gTree = ReactDOM.render(Tree(treeProps), document.body); 65 } 66 67 renderTree(); 68 const els = { 69 get tree() { 70 // React will replace the tree via renderTree. 71 return findRenderedDOMComponentWithClass(gTree, "tree"); 72 }, 73 get anchor() { 74 // When tree node becomes active/inactive, it is replaced with a newly rendered 75 // one. 76 return findRenderedDOMComponentWithTag(gTree, "a"); 77 }, 78 get button() { 79 // When tree node becomes active/inactive, it is replaced with a newly rendered 80 // one. 81 return findRenderedDOMComponentWithTag(gTree, "button"); 82 }, 83 }; 84 85 function getExpectedActiveElementForFinalShiftTab() { 86 // When tab focus mode is applied, the "Run Chrome Tests" button is not 87 // focusable, so this Shift+Tab moves the focus to a Chrome UI element 88 // instead of the "Run Chrome Tests" button, which makes the focus to 89 // move to a browsing context that has a different top level browsing context 90 // than the current browsing context. Since top level browsing contexts are 91 // different, the activeElement in the old document is not cleared. Also 92 // this is only the case when e10s is enabled. 93 if (SpecialPowers.getBoolPref("accessibility.tabfocus_applies_to_xul") && 94 SpecialPowers.Services.appinfo.browserTabsRemoteAutostart) { 95 return "tree"; 96 } 97 98 return document.body; 99 } 100 101 const tests = [{ 102 name: "Test default Tree props. Keyboard focus is set to document body by default.", 103 props: { focused: undefined, active: undefined }, 104 activeElement: document.body, 105 }, { 106 name: "Focused props must be set to the first node on initial focus. " + 107 "Keyboard focus should be set on the tree.", 108 action: () => els.tree.focus(), 109 activeElement: "tree", 110 props: { focused: "A" }, 111 }, { 112 name: "Focused node should remain set even when the tree is blured. " + 113 "Keyboard focus should be set back to document body.", 114 action: () => els.tree.blur(), 115 props: { focused: "A" }, 116 activeElement: document.body, 117 }, { 118 name: "Unset tree's focused prop.", 119 action: () => renderTree({ focused: null }), 120 props: { focused: null }, 121 }, { 122 name: "Focused node must be re-set again to the first tree node on initial " + 123 "focus. Keyboard focus should be set on tree's conatiner.", 124 action: () => els.tree.focus(), 125 activeElement: "tree", 126 props: { focused: "A" }, 127 }, { 128 name: "Focused node should be set as active on Enter.", 129 event: { type: "keyDown", el: "tree", options: { key: "Enter" }}, 130 props: { focused: "A", active: "A" }, 131 activeElement: "tree", 132 }, { 133 name: "Active node should be unset on Escape.", 134 event: { type: "keyDown", el: "tree", options: { key: "Escape" }}, 135 props: { focused: "A", active: null }, 136 }, { 137 name: "Focused node should be set as active on Space.", 138 event: { type: "keyDown", el: "tree", options: { key: " " }}, 139 props: { focused: "A", active: "A" }, 140 activeElement: "tree", 141 }, { 142 name: "Active node should unset when focus leaves the tree.", 143 action: () => els.tree.blur(), 144 props: { focused: "A", active: null }, 145 activeElement: document.body, 146 }, { 147 name: "Keyboard focus should be set on tree's conatiner on focus.", 148 action: () => els.tree.focus(), 149 activeElement: "tree", 150 }, { 151 name: "Focused node should be updated to next on ArrowDown.", 152 event: { type: "keyDown", el: "tree", options: { key: "ArrowDown" }}, 153 props: { focused: "M", active: null }, 154 }, { 155 name: "Focused item should be set as active on Enter. Keyboard focus should be " + 156 "set on the first focusable element inside the tree node, if available.", 157 event: { type: "keyDown", el: "tree", options: { key: "Enter" }}, 158 props: { focused: "M", active: "M" }, 159 activeElement: "anchor", 160 }, { 161 name: "Keyboard focus should be set to next tabbable element inside the active " + 162 "node on Tab.", 163 action() { 164 synthesizeKey("KEY_Tab"); 165 }, 166 props: { focused: "M", active: "M" }, 167 activeElement: "button", 168 }, { 169 name: "Keyboard focus should wrap inside the tree node when focused on last " + 170 "tabbable element.", 171 action() { 172 synthesizeKey("KEY_Tab"); 173 }, 174 props: { focused: "M", active: "M" }, 175 activeElement: "anchor", 176 }, { 177 name: "Keyboard focus should wrap inside the tree node when focused on first " + 178 "tabbable element.", 179 action() { 180 synthesizeKey("KEY_Tab", { shiftKey: true }); 181 }, 182 props: { focused: "M", active: "M" }, 183 activeElement: "button", 184 }, { 185 name: "Active tree node should be unset on Escape. Focus should move back to the " + 186 "tree container.", 187 event: { type: "keyDown", el: "tree", options: { key: "Escape" }}, 188 props: { focused: "M", active: null }, 189 activeElement: "tree", 190 }, { 191 name: "Focused node should be set as active on Space. Keyboard focus should be " + 192 "set on the first focusable element inside the tree node, if available.", 193 event: { type: "keyDown", el: "tree", options: { key: " " }}, 194 props: { focused: "M", active: "M" }, 195 activeElement: "anchor", 196 }, { 197 name: "Focused tree node should remain set even when the tree is blured. " + 198 "Keyboard focus should be set back to document body.", 199 action: () => document.activeElement.blur(), 200 props: { focused: "M", active: null, }, 201 activeElement: document.body, 202 }, { 203 name: "Keyboard focus should be set on tree's conatiner on focus.", 204 action: () => els.tree.focus(), 205 props: { focused: "M", active: null }, 206 activeElement: "tree", 207 }, { 208 name: "Focused tree node should be updated to previous on ArrowUp.", 209 event: { type: "keyDown", el: "tree", options: { key: "ArrowUp" }}, 210 props: { focused: "A", active: null }, 211 }, { 212 name: "Focused item should be set as active on Enter.", 213 event: { type: "keyDown", el: "tree", options: { key: "Enter" }}, 214 props: { focused: "A", active: "A" }, 215 activeElement: "tree", 216 }, { 217 name: "Keyboard focus should move to another focusable element outside of the " + 218 "tree when there's nothing to focus on inside the tree node.", 219 action() { 220 synthesizeKey("KEY_Tab", { shiftKey: true }); 221 }, 222 props: { focused: "A", active: null }, 223 activeElement: getExpectedActiveElementForFinalShiftTab(), 224 }]; 225 226 for (const test of tests) { 227 const { action, event, props, name } = test; 228 229 info(name); 230 if (event) { 231 const { type, options, el } = event; 232 const target = typeof el === "string" ? els[el] : el; 233 Simulate[type](target, options); 234 } else if (action) { 235 action(); 236 } 237 238 await forceRender(gTree); 239 240 if (test.activeElement) { 241 const expected = typeof test.activeElement === "string" ? 242 els[test.activeElement] : test.activeElement; 243 // eslint-disable-next-line no-debugger 244 if (document.activeElement!==expected) {debugger;} 245 is(document.activeElement, expected, "Focus is set correctly."); 246 } 247 248 for (const key in props) { 249 is(gTree.props[key], props[key], `${key} prop is correct.`); 250 } 251 } 252 } catch (e) { 253 ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); 254 } finally { 255 SimpleTest.finish(); 256 } 257 }; 258 </script> 259 </pre> 260 </body> 261 </html>