diff options
Diffstat (limited to 'devtools/client/debugger/test/mochitest/browser_dbg_variables-view-accessibility.js')
-rw-r--r-- | devtools/client/debugger/test/mochitest/browser_dbg_variables-view-accessibility.js | 557 |
1 files changed, 557 insertions, 0 deletions
diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_variables-view-accessibility.js b/devtools/client/debugger/test/mochitest/browser_dbg_variables-view-accessibility.js new file mode 100644 index 000000000..6acec5583 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg_variables-view-accessibility.js @@ -0,0 +1,557 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Make sure that the variables view is keyboard accessible. + */ + +var gTab, gPanel, gDebugger; +var gVariablesView; + +function test() { + initDebugger().then(([aTab,, aPanel]) => { + gTab = aTab; + gPanel = aPanel; + gDebugger = gPanel.panelWin; + gVariablesView = gDebugger.DebuggerView.Variables; + + performTest().then(null, aError => { + ok(false, "Got an error: " + aError.message + "\n" + aError.stack); + }); + }); +} + +function performTest() { + let arr = [ + 42, + true, + "nasu", + undefined, + null, + [0, 1, 2], + { prop1: 9, prop2: 8 } + ]; + + let obj = { + p0: 42, + p1: true, + p2: "nasu", + p3: undefined, + p4: null, + p5: [3, 4, 5], + p6: { prop1: 7, prop2: 6 }, + get p7() { return arr; }, + set p8(value) { arr[0] = value; } + }; + + let test = { + someProp0: 42, + someProp1: true, + someProp2: "nasu", + someProp3: undefined, + someProp4: null, + someProp5: arr, + someProp6: obj, + get someProp7() { return arr; }, + set someProp7(value) { arr[0] = value; } + }; + + gVariablesView.eval = function () {}; + gVariablesView.switch = function () {}; + gVariablesView.delete = function () {}; + gVariablesView.rawObject = test; + gVariablesView.scrollPageSize = 5; + + return Task.spawn(function* () { + yield waitForTick(); + + // Part 0: Test generic focus methods on the variables view. + + gVariablesView.focusFirstVisibleItem(); + is(gVariablesView.getFocusedItem().name, "someProp0", + "The 'someProp0' item should be focused."); + + gVariablesView.focusNextItem(); + is(gVariablesView.getFocusedItem().name, "someProp1", + "The 'someProp1' item should be focused."); + + gVariablesView.focusPrevItem(); + is(gVariablesView.getFocusedItem().name, "someProp0", + "The 'someProp0' item should be focused."); + + // Part 1: Make sure that UP/DOWN keys don't scroll the variables view. + + yield synthesizeKeyAndWaitForTick("VK_DOWN", {}); + is(gVariablesView._parent.scrollTop, 0, + "The 'variables' view shouldn't scroll when pressing the DOWN key."); + + yield synthesizeKeyAndWaitForTick("VK_UP", {}); + is(gVariablesView._parent.scrollTop, 0, + "The 'variables' view shouldn't scroll when pressing the UP key."); + + // Part 2: Make sure that RETURN/ESCAPE toggle input elements. + + yield synthesizeKeyAndWaitForElement("VK_RETURN", {}, ".element-value-input", true); + yield synthesizeKeyAndWaitForElement("VK_ESCAPE", {}, ".element-value-input", false); + yield synthesizeKeyAndWaitForElement("VK_RETURN", { shiftKey: true }, ".element-name-input", true); + yield synthesizeKeyAndWaitForElement("VK_ESCAPE", {}, ".element-name-input", false); + + // Part 3: Test simple navigation. + + EventUtils.sendKey("DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp1", + "The 'someProp1' item should be focused."); + + EventUtils.sendKey("UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp0", + "The 'someProp0' item should be focused."); + + EventUtils.sendKey("PAGE_DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp5", + "The 'someProp5' item should be focused."); + + EventUtils.sendKey("PAGE_UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp0", + "The 'someProp0' item should be focused."); + + EventUtils.sendKey("END", gDebugger); + is(gVariablesView.getFocusedItem().name, "__proto__", + "The '__proto__' item should be focused."); + + EventUtils.sendKey("HOME", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp0", + "The 'someProp0' item should be focused."); + + // Part 4: Test if pressing the same navigation key twice works as expected. + + EventUtils.sendKey("DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp1", + "The 'someProp1' item should be focused."); + + EventUtils.sendKey("DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp2", + "The 'someProp2' item should be focused."); + + EventUtils.sendKey("UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp1", + "The 'someProp1' item should be focused."); + + EventUtils.sendKey("UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp0", + "The 'someProp0' item should be focused."); + + EventUtils.sendKey("PAGE_DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp5", + "The 'someProp5' item should be focused."); + + EventUtils.sendKey("PAGE_DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "__proto__", + "The '__proto__' item should be focused."); + + EventUtils.sendKey("PAGE_UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp5", + "The 'someProp5' item should be focused."); + + EventUtils.sendKey("PAGE_UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp0", + "The 'someProp0' item should be focused."); + + // Part 5: Test that HOME/PAGE_UP/PAGE_DOWN are symmetrical. + + EventUtils.sendKey("HOME", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp0", + "The 'someProp0' item should be focused."); + + EventUtils.sendKey("HOME", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp0", + "The 'someProp0' item should be focused."); + + EventUtils.sendKey("PAGE_UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp0", + "The 'someProp0' item should be focused."); + + EventUtils.sendKey("HOME", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp0", + "The 'someProp0' item should be focused."); + + EventUtils.sendKey("END", gDebugger); + is(gVariablesView.getFocusedItem().name, "__proto__", + "The '__proto__' item should be focused."); + + EventUtils.sendKey("END", gDebugger); + is(gVariablesView.getFocusedItem().name, "__proto__", + "The '__proto__' item should be focused."); + + EventUtils.sendKey("PAGE_DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "__proto__", + "The '__proto__' item should be focused."); + + EventUtils.sendKey("END", gDebugger); + is(gVariablesView.getFocusedItem().name, "__proto__", + "The '__proto__' item should be focused."); + + // Part 6: Test that focus doesn't leave the variables view. + + EventUtils.sendKey("PAGE_UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp5", + "The 'someProp5' item should be focused."); + + EventUtils.sendKey("PAGE_UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp0", + "The 'someProp0' item should be focused."); + + EventUtils.sendKey("UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp0", + "The 'someProp0' item should be focused."); + + EventUtils.sendKey("UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp0", + "The 'someProp0' item should be focused."); + + EventUtils.sendKey("PAGE_DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp5", + "The 'someProp5' item should be focused."); + + EventUtils.sendKey("PAGE_DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "__proto__", + "The '__proto__' item should be focused."); + + EventUtils.sendKey("PAGE_DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "__proto__", + "The '__proto__' item should be focused."); + + EventUtils.sendKey("PAGE_DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "__proto__", + "The '__proto__' item should be focused."); + + // Part 7: Test that random offsets don't occur in tandem with HOME/END. + + EventUtils.sendKey("HOME", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp0", + "The 'someProp0' item should be focused."); + + EventUtils.sendKey("DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp1", + "The 'someProp1' item should be focused."); + + EventUtils.sendKey("END", gDebugger); + is(gVariablesView.getFocusedItem().name, "__proto__", + "The '__proto__' item should be focused."); + + EventUtils.sendKey("DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "__proto__", + "The '__proto__' item should be focused."); + + // Part 8: Test that the RIGHT key expands elements as intended. + + EventUtils.sendKey("PAGE_UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp5", + "The 'someProp5' item should be focused."); + is(gVariablesView.getFocusedItem().expanded, false, + "The 'someProp5' item should not be expanded yet."); + + yield synthesizeKeyAndWaitForTick("VK_RIGHT", {}); + is(gVariablesView.getFocusedItem().name, "someProp5", + "The 'someProp5' item should be focused."); + is(gVariablesView.getFocusedItem().expanded, true, + "The 'someProp5' item should now be expanded."); + is(gVariablesView.getFocusedItem()._store.size, 9, + "There should be 9 properties in the selected variable."); + is(gVariablesView.getFocusedItem()._enumItems.length, 7, + "There should be 7 enumerable properties in the selected variable."); + is(gVariablesView.getFocusedItem()._nonEnumItems.length, 2, + "There should be 2 non-enumerable properties in the selected variable."); + + yield waitForChildNodes(gVariablesView.getFocusedItem()._enum, 7); + yield waitForChildNodes(gVariablesView.getFocusedItem()._nonenum, 2); + + EventUtils.sendKey("RIGHT", gDebugger); + is(gVariablesView.getFocusedItem().name, "0", + "The '0' item should be focused."); + + EventUtils.sendKey("RIGHT", gDebugger); + is(gVariablesView.getFocusedItem().name, "0", + "The '0' item should still be focused."); + + EventUtils.sendKey("PAGE_DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "5", + "The '5' item should be focused."); + is(gVariablesView.getFocusedItem().expanded, false, + "The '5' item should not be expanded yet."); + + yield synthesizeKeyAndWaitForTick("VK_RIGHT", {}); + is(gVariablesView.getFocusedItem().name, "5", + "The '5' item should be focused."); + is(gVariablesView.getFocusedItem().expanded, true, + "The '5' item should now be expanded."); + is(gVariablesView.getFocusedItem()._store.size, 5, + "There should be 5 properties in the selected variable."); + is(gVariablesView.getFocusedItem()._enumItems.length, 3, + "There should be 3 enumerable properties in the selected variable."); + is(gVariablesView.getFocusedItem()._nonEnumItems.length, 2, + "There should be 2 non-enumerable properties in the selected variable."); + + yield waitForChildNodes(gVariablesView.getFocusedItem()._enum, 3); + yield waitForChildNodes(gVariablesView.getFocusedItem()._nonenum, 2); + + EventUtils.sendKey("RIGHT", gDebugger); + is(gVariablesView.getFocusedItem().name, "0", + "The '0' item should be focused."); + + EventUtils.sendKey("RIGHT", gDebugger); + is(gVariablesView.getFocusedItem().name, "0", + "The '0' item should still be focused."); + + EventUtils.sendKey("PAGE_DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "6", + "The '6' item should be focused."); + is(gVariablesView.getFocusedItem().expanded, false, + "The '6' item should not be expanded yet."); + + yield synthesizeKeyAndWaitForTick("VK_RIGHT", {}); + is(gVariablesView.getFocusedItem().name, "6", + "The '6' item should be focused."); + is(gVariablesView.getFocusedItem().expanded, true, + "The '6' item should now be expanded."); + is(gVariablesView.getFocusedItem()._store.size, 3, + "There should be 3 properties in the selected variable."); + is(gVariablesView.getFocusedItem()._enumItems.length, 2, + "There should be 2 enumerable properties in the selected variable."); + is(gVariablesView.getFocusedItem()._nonEnumItems.length, 1, + "There should be 1 non-enumerable properties in the selected variable."); + + yield waitForChildNodes(gVariablesView.getFocusedItem()._enum, 2); + yield waitForChildNodes(gVariablesView.getFocusedItem()._nonenum, 1); + + EventUtils.sendKey("RIGHT", gDebugger); + is(gVariablesView.getFocusedItem().name, "prop1", + "The 'prop1' item should be focused."); + + EventUtils.sendKey("RIGHT", gDebugger); + is(gVariablesView.getFocusedItem().name, "prop1", + "The 'prop1' item should still be focused."); + + EventUtils.sendKey("PAGE_DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp6", + "The 'someProp6' item should be focused."); + is(gVariablesView.getFocusedItem().expanded, false, + "The 'someProp6' item should not be expanded yet."); + + // Part 9: Test that the RIGHT key collapses elements as intended. + + EventUtils.sendKey("LEFT", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp6", + "The 'someProp6' item should be focused."); + + EventUtils.sendKey("UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "__proto__", + "The '__proto__' item should be focused."); + + EventUtils.sendKey("LEFT", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp5", + "The 'someProp5' item should be focused."); + is(gVariablesView.getFocusedItem().expanded, true, + "The '6' item should still be expanded."); + + EventUtils.sendKey("LEFT", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp5", + "The 'someProp5' item should still be focused."); + is(gVariablesView.getFocusedItem().expanded, false, + "The '6' item should still not be expanded anymore."); + + EventUtils.sendKey("LEFT", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp5", + "The 'someProp5' item should still be focused."); + + // Part 9: Test continuous navigation. + + EventUtils.sendKey("UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp4", + "The 'someProp4' item should be focused."); + + EventUtils.sendKey("UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp3", + "The 'someProp3' item should be focused."); + + EventUtils.sendKey("UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp2", + "The 'someProp2' item should be focused."); + + EventUtils.sendKey("UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp1", + "The 'someProp1' item should be focused."); + + EventUtils.sendKey("UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp0", + "The 'someProp0' item should be focused."); + + EventUtils.sendKey("PAGE_UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp0", + "The 'someProp0' item should be focused."); + + EventUtils.sendKey("PAGE_DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp5", + "The 'someProp5' item should be focused."); + + EventUtils.sendKey("DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp6", + "The 'someProp6' item should be focused."); + + EventUtils.sendKey("DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp7", + "The 'someProp7' item should be focused."); + + EventUtils.sendKey("DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "get", + "The 'get' item should be focused."); + + EventUtils.sendKey("DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "set", + "The 'set' item should be focused."); + + EventUtils.sendKey("DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "__proto__", + "The '__proto__' item should be focused."); + + // Part 10: Test that BACKSPACE deletes items in the variables view. + + EventUtils.sendKey("BACK_SPACE", gDebugger); + is(gVariablesView.getFocusedItem().name, "__proto__", + "The '__proto__' variable should still be focused."); + is(gVariablesView.getFocusedItem().value, "[object Object]", + "The '__proto__' variable should not have an empty value."); + is(gVariablesView.getFocusedItem().visible, false, + "The '__proto__' variable should be hidden."); + + EventUtils.sendKey("UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "set", + "The 'set' item should be focused."); + is(gVariablesView.getFocusedItem().value, "[object Object]", + "The 'set' item should not have an empty value."); + is(gVariablesView.getFocusedItem().visible, true, + "The 'set' item should be visible."); + + EventUtils.sendKey("BACK_SPACE", gDebugger); + is(gVariablesView.getFocusedItem().name, "set", + "The 'set' item should still be focused."); + is(gVariablesView.getFocusedItem().value, "[object Object]", + "The 'set' item should not have an empty value."); + is(gVariablesView.getFocusedItem().visible, true, + "The 'set' item should be visible."); + is(gVariablesView.getFocusedItem().twisty, false, + "The 'set' item should be disabled and have a hidden twisty."); + + EventUtils.sendKey("UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "get", + "The 'get' item should be focused."); + is(gVariablesView.getFocusedItem().value, "[object Object]", + "The 'get' item should not have an empty value."); + is(gVariablesView.getFocusedItem().visible, true, + "The 'get' item should be visible."); + + EventUtils.sendKey("BACK_SPACE", gDebugger); + is(gVariablesView.getFocusedItem().name, "get", + "The 'get' item should still be focused."); + is(gVariablesView.getFocusedItem().value, "[object Object]", + "The 'get' item should not have an empty value."); + is(gVariablesView.getFocusedItem().visible, true, + "The 'get' item should be visible."); + is(gVariablesView.getFocusedItem().twisty, false, + "The 'get' item should be disabled and have a hidden twisty."); + + EventUtils.sendKey("UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp7", + "The 'someProp7' item should be focused."); + is(gVariablesView.getFocusedItem().value, undefined, + "The 'someProp7' variable should have an empty value."); + is(gVariablesView.getFocusedItem().visible, true, + "The 'someProp7' variable should be visible."); + + EventUtils.sendKey("BACK_SPACE", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp7", + "The 'someProp7' variable should still be focused."); + is(gVariablesView.getFocusedItem().value, undefined, + "The 'someProp7' variable should have an empty value."); + is(gVariablesView.getFocusedItem().visible, false, + "The 'someProp7' variable should be hidden."); + + // Part 11: Test that Ctrl-C copies the current item to the system clipboard + + gVariablesView.focusFirstVisibleItem(); + let copied = promise.defer(); + let expectedValue = gVariablesView.getFocusedItem().name + + gVariablesView.getFocusedItem().separatorStr + + gVariablesView.getFocusedItem().value; + + waitForClipboard(expectedValue, function setup() { + EventUtils.synthesizeKey("C", { metaKey: true }, gDebugger); + }, copied.resolve, copied.reject + ); + + try { + yield copied.promise; + ok(true, + "Ctrl-C copied the selected item to the clipboard."); + } catch (e) { + ok(false, + "Ctrl-C didn't copy the selected item to the clipboard."); + } + + yield closeDebuggerAndFinish(gPanel); + }); +} + +registerCleanupFunction(function () { + gTab = null; + gPanel = null; + gDebugger = null; + gVariablesView = null; +}); + +function synthesizeKeyAndWaitForElement(aKey, aModifiers, aSelector, aExistence) { + EventUtils.synthesizeKey(aKey, aModifiers, gDebugger); + return waitForElement(aSelector, aExistence); +} + +function synthesizeKeyAndWaitForTick(aKey, aModifiers) { + EventUtils.synthesizeKey(aKey, aModifiers, gDebugger); + return waitForTick(); +} + +function waitForElement(aSelector, aExistence) { + return waitForPredicate(() => { + return !!gVariablesView._list.querySelector(aSelector) == aExistence; + }); +} + +function waitForChildNodes(aTarget, aCount) { + return waitForPredicate(() => { + return aTarget.childNodes.length == aCount; + }); +} + +function waitForPredicate(aPredicate, aInterval = 10) { + let deferred = promise.defer(); + + // Poll every few milliseconds until the element is retrieved. + let count = 0; + let intervalID = window.setInterval(() => { + // Make sure we don't wait for too long. + if (++count > 1000) { + deferred.reject("Timed out while polling for the element."); + window.clearInterval(intervalID); + return; + } + // Check if the predicate condition is fulfilled. + if (!aPredicate()) { + return; + } + // We got the element, it's safe to callback. + window.clearInterval(intervalID); + deferred.resolve(); + }, aInterval); + + return deferred.promise; +} |