blob: 2f52851f54ac717c706548d73a9a7786d55cc788 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
GEN_INCLUDE(['switch_access_e2e_test_base.js']);
/** Test fixture for the navigation manager. */
SwitchAccessNavigationManagerTest = class extends SwitchAccessE2ETest {
/** @override */
setUp() {
this.navigator = NavigationManager.instance;
BackButtonNode
.locationForTesting = {top: 10, left: 10, width: 20, height: 20};
}
moveToPageContents(pageContents) {
const cache = new SACache();
if (!SwitchAccessPredicate.isGroup(pageContents, null, cache)) {
pageContents =
new AutomationTreeWalker(pageContents, constants.Dir.FORWARD, {
visit: (node) => SwitchAccessPredicate.isGroup(node, null, cache)
})
.next()
.node;
}
assertNotNullNorUndefined(
pageContents, 'Could not find group corresponding to page contents');
this.navigator.moveTo_(pageContents);
NavigationManager.enterGroup();
}
};
function currentNode() {
return NavigationManager.instance.node_;
}
TEST_F('SwitchAccessNavigationManagerTest', 'MoveTo', function() {
const website = `<div id="outerGroup">
<div id="group">
<input type="text">
<input type="range">
</div>
<button></button>
</div>`;
this.runWithLoadedTree(website, (root) => {
const desktop = root.parent.root;
const textFields =
desktop.findAll({role: chrome.automation.RoleType.TEXT_FIELD});
assertEquals(2, textFields.length, 'Should be exactly 2 text fields.');
const omnibar = textFields[0];
const textInput = textFields[1];
const sliders = desktop.findAll({role: chrome.automation.RoleType.SLIDER});
assertEquals(1, sliders.length, 'Should be exactly 1 slider.');
const slider = sliders[0];
const group = this.findNodeById('group');
const outerGroup = this.findNodeById('outerGroup');
this.navigator.moveTo_(omnibar);
assertEquals(
chrome.automation.RoleType.TEXT_FIELD, this.navigator.node_.role,
'Did not successfully move to the omnibar');
assertFalse(
this.navigator.group_.isEquivalentTo(group),
'Omnibar is in the group in page contents somehow');
assertFalse(
this.navigator.group_.isEquivalentTo(outerGroup),
'Omnibar is in the outer group in page contents somehow');
const grandGroup = this.navigator.history_.peek().group;
assertFalse(
grandGroup.isEquivalentTo(group),
'Group stack contains the group from page contents');
assertFalse(
grandGroup.isEquivalentTo(outerGroup),
'Group stack contains the outer group from page contents');
this.navigator.moveTo_(textInput);
assertEquals(
chrome.automation.RoleType.TEXT_FIELD, this.navigator.node_.role,
'Did not successfully move to the text input');
assertTrue(
this.navigator.group_.isEquivalentTo(group),
'Group node was not successfully populated');
assertTrue(
this.navigator.history_.peek().group.isEquivalentTo(outerGroup),
'History was not built properly');
this.navigator.moveTo_(slider);
assertEquals(
chrome.automation.RoleType.SLIDER, this.navigator.node_.role,
'Did not successfully move to the slider');
this.navigator.moveTo_(group);
assertTrue(this.navigator.node_.isGroup(), 'Current node is not a group');
assertTrue(
this.navigator.node_.isEquivalentTo(group),
'Did not find the right group');
});
});
TEST_F('SwitchAccessNavigationManagerTest', 'JumpTo', function() {
const website = `<div id="group1">
<input type="text">
<button></button>
</div>
<div id="group2">
<button></button>
<button></button>
</div>`;
this.runWithLoadedTree(website, (root) => {
const desktop = root.parent.root;
const textInput =
desktop.findAll({role: chrome.automation.RoleType.TEXT_FIELD})[1];
assertNotNullNorUndefined(textInput, 'Text field is undefined');
const group1 = this.findNodeById('group1');
const group2 = this.findNodeById('group2');
this.navigator.moveTo_(textInput);
const textInputNode = this.navigator.node_;
assertEquals(
chrome.automation.RoleType.TEXT_FIELD, textInputNode.role,
'Did not successfully move to the text input');
assertTrue(
this.navigator.group_.isEquivalentTo(group1),
'Group is initialized in an unexpected manner');
this.navigator.jumpTo_(BasicRootNode.buildTree(group2));
assertFalse(
this.navigator.group_.isEquivalentTo(group1), 'Jump did nothing');
assertTrue(
this.navigator.group_.isEquivalentTo(group2),
'Jumped to the wrong group');
this.navigator.exitGroup_();
assertTrue(
this.navigator.group_.isEquivalentTo(group1),
'Did not jump back to the right group.');
});
});
TEST_F('SwitchAccessNavigationManagerTest', 'SelectButton', function() {
const website = `<button id="test" aria-pressed=false>First Button</button>
<button>Second Button</button>
<script>
let state = false;
let button = document.getElementById("test");
button.onclick = () => {
state = !state;
button.setAttribute("aria-pressed", state);
};
</script>`;
this.runWithLoadedTree(website, function(pageContents) {
this.moveToPageContents(pageContents);
const node = currentNode().automationNode;
assertNotNullNorUndefined(node, 'Node is invalid');
assertEquals(node.name, 'First Button', 'Did not find the right node');
node.addEventListener(
chrome.automation.EventType.CHECKED_STATE_CHANGED,
this.newCallback((event) => {
assertEquals(
node.name, event.target.name,
'Checked state changed on unexpected node');
}));
NavigationManager.instance.node_.performAction('select');
});
});
TEST_F('SwitchAccessNavigationManagerTest', 'EnterGroup', function() {
const website = `<div id="group">
<button></button>
<button></button>
</div>
<input type="range">`;
this.runWithLoadedTree(website, (root) => {
const targetGroup = this.findNodeById('group');
this.navigator.moveTo_(targetGroup);
const originalGroup = this.navigator.group_;
assertEquals(
this.navigator.node_.automationNode.htmlAttributes.id, 'group',
'Did not move to group properly');
NavigationManager.enterGroup();
assertEquals(
chrome.automation.RoleType.BUTTON, this.navigator.node_.role,
'Current node is not a button');
assertTrue(
this.navigator.group_.isEquivalentTo(targetGroup),
'Target group was not entered');
this.navigator.exitGroup_();
assertTrue(
originalGroup.equals(this.navigator.group_),
'Did not move back to the original group');
});
});
TEST_F('SwitchAccessNavigationManagerTest', 'MoveForward', function() {
const website = `<div>
<button id="button1"></button>
<button id="button2"></button>
<button id="button3"></button>
</div>`;
this.runWithLoadedTree(website, (root) => {
this.navigator.moveTo_(this.findNodeById('button1'));
const button1 = this.navigator.node_;
assertFalse(
button1 instanceof BackButtonNode,
'button1 should not be a BackButtonNode');
assertEquals(
'button1', button1.automationNode.htmlAttributes.id,
'Current node is not button1');
NavigationManager.moveForward();
assertFalse(
button1.equals(this.navigator.node_),
'Still on button1 after moveForward()');
const button2 = this.navigator.node_;
assertFalse(
button2 instanceof BackButtonNode,
'button2 should not be a BackButtonNode');
assertEquals(
'button2', button2.automationNode.htmlAttributes.id,
'Current node is not button2');
NavigationManager.moveForward();
assertFalse(
button1.equals(this.navigator.node_),
'Unexpected navigation to button1');
assertFalse(
button2.equals(this.navigator.node_),
'Still on button2 after moveForward()');
const button3 = this.navigator.node_;
assertFalse(
button3 instanceof BackButtonNode,
'button3 should not be a BackButtonNode');
assertEquals(
'button3', button3.automationNode.htmlAttributes.id,
'Current node is not button3');
NavigationManager.moveForward();
assertTrue(
this.navigator.node_ instanceof BackButtonNode,
'BackButtonNode should come after button3');
NavigationManager.moveForward();
assertTrue(
button1.equals(this.navigator.node_),
'button1 should come after the BackButtonNode');
});
});
TEST_F('SwitchAccessNavigationManagerTest', 'MoveBackward', function() {
const website = `<div>
<button id="button1"></button>
<button id="button2"></button>
<button id="button3"></button>
</div>`;
this.runWithLoadedTree(website, (root) => {
this.navigator.moveTo_(this.findNodeById('button1'));
const button1 = this.navigator.node_;
assertFalse(
button1 instanceof BackButtonNode,
'button1 should not be a BackButtonNode');
assertEquals(
'button1', button1.automationNode.htmlAttributes.id,
'Current node is not button1');
NavigationManager.moveBackward();
assertTrue(
this.navigator.node_ instanceof BackButtonNode,
'BackButtonNode should come before button1');
NavigationManager.moveBackward();
assertFalse(
button1.equals(this.navigator.node_),
'Unexpected navigation to button1');
const button3 = this.navigator.node_;
assertFalse(
button3 instanceof BackButtonNode,
'button3 should not be a BackButtonNode');
assertEquals(
'button3', button3.automationNode.htmlAttributes.id,
'Current node is not button3');
NavigationManager.moveBackward();
assertFalse(
button3.equals(this.navigator.node_),
'Still on button3 after moveBackward()');
assertFalse(button1.equals(this.navigator.node_), 'Skipped button2');
const button2 = this.navigator.node_;
assertFalse(
button2 instanceof BackButtonNode,
'button2 should not be a BackButtonNode');
assertEquals(
'button2', button2.automationNode.htmlAttributes.id,
'Current node is not button2');
NavigationManager.moveBackward();
assertTrue(
button1.equals(this.navigator.node_),
'button1 should come before button2');
});
});
TEST_F(
'SwitchAccessNavigationManagerTest', 'NodeUndefinedBeforeTreeChangeRemoved',
function() {
const website = `<div>
<button id="button1"></button>
</div>`;
this.runWithLoadedTree(website, (root) => {
this.navigator.moveTo_(this.findNodeById('button1'));
const button1 = this.navigator.node_;
assertFalse(
button1 instanceof BackButtonNode,
'button1 should not be a BackButtonNode');
assertEquals(
'button1', button1.automationNode.htmlAttributes.id,
'Current node is not button1');
// Simulate the underlying node's deletion. Note that this is different
// than an orphaned node (which can have a valid AutomationNode
// instance, but no backing C++ object, so attributes returned like role
// are undefined).
NavigationManager.instance.node_.baseNode_ = undefined;
// Tree change removed gets sent by C++ after the tree has already
// applied changes (so this comes after the above clearing).
NavigationManager.instance.onTreeChange_(
{type: chrome.automation.TreeChangeType.NODE_REMOVED});
});
});