blob: a39b5cd92b3187067407fa2d9a6a86a2a8e32967 [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 Switch Access predicates. */
SwitchAccessPredicateTest = class extends SwitchAccessE2ETest {};
function fakeLoc(x) {
return {left: x, top: x, width: x, height: x};
}
// This page has a 1:1 correlation between DOM nodes and accessibility nodes.
function testWebsite() {
return `<div aria-label="upper1">
<div aria-label="lower1">
<button>leaf1</button>
<p aria-label="leaf2">leaf2</p>
<button>leaf3</button>
</div>
<div aria-label="lower2">
<p aria-label="leaf4">leaf4</p>
<button>leaf5</button>
</div>
</div>
<div aria-label="upper2" role="button">
<div aria-label="lower3" >
<p aria-label="leaf6">leaf6</p>
<p aria-label="leaf7">leaf7</p>`;
}
function getTree(loadedPage) {
const root = loadedPage;
assertTrue(
root.role === chrome.automation.RoleType.ROOT_WEB_AREA,
'We should receive the root web area');
assertTrue(
root.firstChild && root.firstChild.name === 'upper1',
'First child should be upper1');
const upper1 = root.firstChild;
assertTrue(upper1 && upper1.name === 'upper1', 'Upper1 not found');
const upper2 = upper1.nextSibling;
assertTrue(upper2 && upper2.name === 'upper2', 'Upper2 not found');
const lower1 = upper1.firstChild;
assertTrue(lower1 && lower1.name === 'lower1', 'Lower1 not found');
const lower2 = lower1.nextSibling;
assertTrue(lower2 && lower2.name === 'lower2', 'Lower2 not found');
const lower3 = upper2.firstChild;
assertTrue(lower3 && lower3.name === 'lower3', 'Lower3 not found');
const leaf1 = lower1.firstChild;
assertTrue(leaf1 && leaf1.name === 'leaf1', 'Leaf1 not found');
const leaf2 = leaf1.nextSibling;
assertTrue(leaf2 && leaf2.name === 'leaf2', 'Leaf2 not found');
const leaf3 = leaf2.nextSibling;
assertTrue(leaf3 && leaf3.name === 'leaf3', 'Leaf3 not found');
const leaf4 = lower2.firstChild;
assertTrue(leaf4 && leaf4.name === 'leaf4', 'Leaf4 not found');
const leaf5 = leaf4.nextSibling;
assertTrue(leaf5 && leaf5.name === 'leaf5', 'Leaf5 not found');
const leaf6 = lower3.firstChild;
assertTrue(leaf6 && leaf6.name === 'leaf6', 'Leaf6 not found');
const leaf7 = leaf6.nextSibling;
assertTrue(leaf7 && leaf7.name === 'leaf7', 'Leaf7 not found');
return {
root,
upper1,
upper2,
lower1,
lower2,
lower3,
leaf1,
leaf2,
leaf3,
leaf4,
leaf5,
leaf6,
leaf7
};
}
TEST_F('SwitchAccessPredicateTest', 'IsInteresting', function() {
this.runWithLoadedTree(testWebsite(), (loadedPage) => {
const t = getTree(loadedPage);
const cache = new SACache();
// The scope is only used to verify the locations are not the same, and
// since the buildTree function depends on isInteresting, pass in null
// for the scope.
assertTrue(
SwitchAccessPredicate.isInteresting(t.root, null, cache),
'Root should be interesting');
assertTrue(
SwitchAccessPredicate.isInteresting(t.upper1, null, cache),
'Upper1 should be interesting');
assertTrue(
SwitchAccessPredicate.isInteresting(t.upper2, null, cache),
'Upper2 should be interesting');
assertTrue(
SwitchAccessPredicate.isInteresting(t.lower1, null, cache),
'Lower1 should be interesting');
assertFalse(
SwitchAccessPredicate.isInteresting(t.lower2, null, cache),
'Lower2 should not be interesting');
assertFalse(
SwitchAccessPredicate.isInteresting(t.lower3, null, cache),
'Lower3 should not be interesting');
assertTrue(
SwitchAccessPredicate.isInteresting(t.leaf1, null, cache),
'Leaf1 should be interesting');
assertFalse(
SwitchAccessPredicate.isInteresting(t.leaf2, null, cache),
'Leaf2 should not be interesting');
assertTrue(
SwitchAccessPredicate.isInteresting(t.leaf3, null, cache),
'Leaf3 should be interesting');
assertFalse(
SwitchAccessPredicate.isInteresting(t.leaf4, null, cache),
'Leaf4 should not be interesting');
assertTrue(
SwitchAccessPredicate.isInteresting(t.leaf5, null, cache),
'Leaf5 should be interesting');
assertFalse(
SwitchAccessPredicate.isInteresting(t.leaf6, null, cache),
'Leaf6 should not be interesting');
assertFalse(
SwitchAccessPredicate.isInteresting(t.leaf7, null, cache),
'Leaf7 should not be interesting');
});
});
TEST_F('SwitchAccessPredicateTest', 'IsGroup', function() {
this.runWithLoadedTree(testWebsite(), (loadedPage) => {
const t = getTree(loadedPage);
const cache = new SACache();
// The scope is only used to verify the locations are not the same, and
// since the buildTree function depends on isGroup, pass in null for
// the scope.
assertTrue(
SwitchAccessPredicate.isGroup(t.root, null, cache),
'Root should be a group');
assertTrue(
SwitchAccessPredicate.isGroup(t.upper1, null, cache),
'Upper1 should be a group');
assertFalse(
SwitchAccessPredicate.isGroup(t.upper2, null, cache),
'Upper2 should not be a group');
assertTrue(
SwitchAccessPredicate.isGroup(t.lower1, null, cache),
'Lower1 should be a group');
assertFalse(
SwitchAccessPredicate.isGroup(t.lower2, null, cache),
'Lower2 should not be a group');
assertFalse(
SwitchAccessPredicate.isGroup(t.lower3, null, cache),
'Lower3 should not be a group');
assertFalse(
SwitchAccessPredicate.isGroup(t.leaf1, null, cache),
'Leaf1 should not be a group');
assertFalse(
SwitchAccessPredicate.isGroup(t.leaf2, null, cache),
'Leaf2 should not be a group');
assertFalse(
SwitchAccessPredicate.isGroup(t.leaf3, null, cache),
'Leaf3 should not be a group');
assertFalse(
SwitchAccessPredicate.isGroup(t.leaf4, null, cache),
'Leaf4 should not be a group');
assertFalse(
SwitchAccessPredicate.isGroup(t.leaf5, null, cache),
'Leaf5 should not be a group');
assertFalse(
SwitchAccessPredicate.isGroup(t.leaf6, null, cache),
'Leaf6 should not be a group');
assertFalse(
SwitchAccessPredicate.isGroup(t.leaf7, null, cache),
'Leaf7 should not be a group');
});
});
TEST_F('SwitchAccessPredicateTest', 'IsInterestingSubtree', function() {
this.runWithLoadedTree(testWebsite(), (loadedPage) => {
const t = getTree(loadedPage);
const cache = new SACache();
assertTrue(
SwitchAccessPredicate.isInterestingSubtree(t.root, cache),
'Root should be an interesting subtree');
assertTrue(
SwitchAccessPredicate.isInterestingSubtree(t.upper1, cache),
'Upper1 should be an interesting subtree');
assertTrue(
SwitchAccessPredicate.isInterestingSubtree(t.upper2, cache),
'Upper2 should be an interesting subtree');
assertTrue(
SwitchAccessPredicate.isInterestingSubtree(t.lower1, cache),
'Lower1 should be an interesting subtree');
assertTrue(
SwitchAccessPredicate.isInterestingSubtree(t.lower2, cache),
'Lower2 should be an interesting subtree');
assertFalse(
SwitchAccessPredicate.isInterestingSubtree(t.lower3, cache),
'Lower3 should not be an interesting subtree');
assertTrue(
SwitchAccessPredicate.isInterestingSubtree(t.leaf1, cache),
'Leaf1 should be an interesting subtree');
assertFalse(
SwitchAccessPredicate.isInterestingSubtree(t.leaf2, cache),
'Leaf2 should not be an interesting subtree');
assertTrue(
SwitchAccessPredicate.isInterestingSubtree(t.leaf3, cache),
'Leaf3 should be an interesting subtree');
assertFalse(
SwitchAccessPredicate.isInterestingSubtree(t.leaf4, cache),
'Leaf4 should not be an interesting subtree');
assertTrue(
SwitchAccessPredicate.isInterestingSubtree(t.leaf5, cache),
'Leaf5 should be an interesting subtree');
assertFalse(
SwitchAccessPredicate.isInterestingSubtree(t.leaf6, cache),
'Leaf6 should not be an interesting subtree');
assertFalse(
SwitchAccessPredicate.isInterestingSubtree(t.leaf7, cache),
'Leaf7 should not be an interesting subtree');
});
});
TEST_F('SwitchAccessPredicateTest', 'IsActionable', function() {
const treeString =
`<button style="position:absolute; top:-100px;">offscreen</button>
<button disabled>disabled</button>
<a href="https://www.google.com/" aria-label="link1">link1</a>
<input type="text" aria-label="input1">input1</input>
<button>button3</button>
<input type="range" aria-label="slider" value=5 min=0 max=10>
<div id="clickable" role="listitem" onclick="2+2"></div>
<div aria-label="div1"><p>p1</p></div>`;
this.runWithLoadedTree(treeString, (loadedPage) => {
const cache = new SACache();
const offscreenButton = this.findNodeByNameAndRole('offscreen', 'button');
assertFalse(
SwitchAccessPredicate.isActionable(offscreenButton, cache),
'Offscreen objects should not be actionable');
const disabledButton = this.findNodeByNameAndRole('disabled', 'button');
assertFalse(
SwitchAccessPredicate.isActionable(disabledButton, cache),
'Disabled objects should not be actionable');
assertFalse(
SwitchAccessPredicate.isActionable(loadedPage, cache),
'Root web area should not be directly actionable');
const link1 = this.findNodeByNameAndRole('link1', 'link');
assertTrue(
SwitchAccessPredicate.isActionable(link1, cache),
'Links should be actionable');
const input1 = this.findNodeByNameAndRole('input1', 'textField');
assertTrue(
SwitchAccessPredicate.isActionable(input1, cache),
'Inputs should be actionable');
const button3 = this.findNodeByNameAndRole('button3', 'button');
assertTrue(
SwitchAccessPredicate.isActionable(button3, cache),
'Buttons should be actionable');
const slider = this.findNodeByNameAndRole('slider', 'slider');
assertTrue(
SwitchAccessPredicate.isActionable(slider, cache),
'Sliders should be actionable');
const clickable = this.findNodeById('clickable');
assertTrue(
SwitchAccessPredicate.isActionable(clickable, cache),
'Clickable list items should be actionable');
const div1 = this.findNodeByNameAndRole('div1', 'genericContainer');
assertFalse(
SwitchAccessPredicate.isActionable(div1, cache),
'Divs should not generally be actionable');
const p1 = this.findNodeByNameAndRole('p1', 'staticText');
assertFalse(
SwitchAccessPredicate.isActionable(p1, cache),
'Static text should not generally be actionable');
});
});
TEST_F('SwitchAccessPredicateTest', 'IsActionableFocusableElements', function() {
const treeString = `<div id="noChildren" tabindex=0></div>
<div id="oneInterestingChild" tabindex=0>
<div>
<div>
<div>
<button>button1</button>
</div>
</div>
</div>
</div>
<div id="oneUninterestingChild" tabindex=0>
<p>p1</p>
</div>
<div id="interestingChildren" tabindex=0>
<button>button2</button>
<button>button3</button>
</div>
<div id="uninterestingChildren" tabindex=0>
<p>p2</p>
<p>p3</p>
</div>`;
this.runWithLoadedTree(treeString, (loadedPage) => {
const cache = new SACache();
const noChildren = this.findNodeById('noChildren');
assertTrue(
SwitchAccessPredicate.isActionable(noChildren, cache),
'Focusable element with no children should be actionable');
const oneInterestingChild = this.findNodeById('oneInterestingChild');
assertFalse(
SwitchAccessPredicate.isActionable(oneInterestingChild, cache),
'Focusable element with an interesting child should not be actionable');
const interestingChildren = this.findNodeById('interestingChildren');
assertFalse(
SwitchAccessPredicate.isActionable(interestingChildren, cache),
'Focusable element with interesting children should not be actionable');
const oneUninterestingChild = this.findNodeById('oneUninterestingChild');
assertTrue(
SwitchAccessPredicate.isActionable(oneUninterestingChild, cache),
'Focusable element with one uninteresting child should be actionable');
const uninterestingChildren = this.findNodeById('uninterestingChildren');
assertTrue(
SwitchAccessPredicate.isActionable(uninterestingChildren, cache),
'Focusable element with uninteresting children should be actionable');
});
});
TEST_F('SwitchAccessPredicateTest', 'LeafPredicate', function() {
this.runWithLoadedTree(testWebsite(), (loadedPage) => {
const t = getTree(loadedPage);
const cache = new SACache();
// Start with root as scope
let leaf = SwitchAccessPredicate.leaf(t.root, cache);
assertFalse(leaf(t.root), 'Root should not be a leaf node');
assertTrue(leaf(t.upper1), 'Upper1 should be a leaf node for root tree');
assertTrue(leaf(t.upper2), 'Upper2 should be a leaf node for root tree');
// Set upper1 as scope
leaf = SwitchAccessPredicate.leaf(t.upper1, cache);
assertFalse(leaf(t.upper1), 'Upper1 should not be a leaf for upper1 tree');
assertTrue(leaf(t.lower1), 'Lower1 should be a leaf for upper1 tree');
assertTrue(leaf(t.leaf4), 'leaf4 should be a leaf for upper1 tree');
assertTrue(leaf(t.leaf5), 'leaf5 should be a leaf for upper1 tree');
// Set lower1 as scope
leaf = SwitchAccessPredicate.leaf(t.lower1, cache);
assertFalse(leaf(t.lower1), 'Lower1 should not be a leaf for lower1 tree');
assertTrue(leaf(t.leaf1), 'Leaf1 should be a leaf for lower1 tree');
assertTrue(leaf(t.leaf2), 'Leaf2 should be a leaf for lower1 tree');
assertTrue(leaf(t.leaf3), 'Leaf3 should be a leaf for lower1 tree');
});
});
TEST_F('SwitchAccessPredicateTest', 'RootPredicate', function() {
this.runWithLoadedTree(testWebsite(), (loadedPage) => {
const t = getTree(loadedPage);
// Start with root as scope
let root = SwitchAccessPredicate.root(t.root);
assertTrue(root(t.root), 'Root should be a root of the root tree');
assertFalse(root(t.upper1), 'Upper1 should not be a root of the root tree');
assertFalse(root(t.upper2), 'Upper2 should not be a root of the root tree');
// Set upper1 as scope
root = SwitchAccessPredicate.root(t.upper1);
assertTrue(root(t.upper1), 'Upper1 should be a root of the upper1 tree');
assertFalse(
root(t.lower1), 'Lower1 should not be a root of the upper1 tree');
assertFalse(
root(t.lower2), 'Lower2 should not be a root of the upper1 tree');
// Set lower1 as scope
root = SwitchAccessPredicate.root(t.lower1);
assertTrue(root(t.lower1), 'Lower1 should be a root of the lower1 tree');
assertFalse(root(t.leaf1), 'Leaf1 should not be a root of the lower1 tree');
assertFalse(root(t.leaf2), 'Leaf2 should not be a root of the lower1 tree');
assertFalse(root(t.leaf3), 'Leaf3 should not be a root of the lower1 tree');
});
});
TEST_F('SwitchAccessPredicateTest', 'VisitPredicate', function() {
this.runWithLoadedTree(testWebsite(), (loadedPage) => {
const t = getTree(loadedPage);
const cache = new SACache();
// Start with root as scope
let visit = SwitchAccessPredicate.visit(t.root, cache);
assertTrue(visit(t.root), 'Root should be visited in root tree');
assertTrue(visit(t.upper1), 'Upper1 should be visited in root tree');
assertTrue(visit(t.upper2), 'Upper2 should be visited in root tree');
// Set upper1 as scope
visit = SwitchAccessPredicate.visit(t.upper1, cache);
assertTrue(visit(t.upper1), 'Upper1 should be visited in upper1 tree');
assertTrue(visit(t.lower1), 'Lower1 should be visited in upper1 tree');
assertFalse(visit(t.lower2), 'Lower2 should not be visited in upper1 tree');
assertFalse(visit(t.leaf4), 'Leaf4 should not be visited in upper1 tree');
assertTrue(visit(t.leaf5), 'Leaf5 should be visited in upper1 tree');
// Set lower1 as scope
visit = SwitchAccessPredicate.visit(t.lower1, cache);
assertTrue(visit(t.lower1), 'Lower1 should be visited in lower1 tree');
assertTrue(visit(t.leaf1), 'Leaf1 should be visited in lower1 tree');
assertFalse(visit(t.leaf2), 'Leaf2 should not be visited in lower1 tree');
assertTrue(visit(t.leaf3), 'Leaf3 should be visited in lower1 tree');
// An uninteresting subtree should return false, regardless of scope
assertFalse(visit(t.lower3), 'Lower3 should not be visited in lower1 tree');
assertFalse(visit(t.leaf6), 'Leaf6 should not be visited in lower1 tree');
assertFalse(visit(t.leaf7), 'Leaf7 should not be visited in lower1 tree');
});
});
TEST_F('SwitchAccessPredicateTest', 'Cache', function() {
this.runWithLoadedTree(testWebsite(), (loadedPage) => {
const t = getTree(loadedPage);
const cache = new SACache();
let locationAccessCount = 0;
class TestRoot extends SARootNode {
/** @override */
get location() {
locationAccessCount++;
return null;
}
}
const group = new TestRoot(t.root);
assertTrue(
SwitchAccessPredicate.isGroup(t.root, group, cache),
'Root should be a group');
assertEquals(
locationAccessCount, 1,
'Location should have been accessed to calculate isGroup');
assertTrue(
SwitchAccessPredicate.isGroup(t.root, group, cache),
'isGroup value should not change');
assertEquals(
locationAccessCount, 1,
'Cache should have been used, avoiding second location access');
locationAccessCount = 0;
assertFalse(
SwitchAccessPredicate.isGroup(t.leaf1, group, cache),
'Leaf should not be a group');
assertEquals(
locationAccessCount, 1,
'Location should have been accessed to calculate isGroup');
assertFalse(
SwitchAccessPredicate.isGroup(t.leaf1, group, cache),
'isGroup value should not change');
assertEquals(
locationAccessCount, 1,
'Cache should have been used, avoiding second location access');
});
});