fune/accessible/tests/browser/mac/browser_text_basics.js
James Teh 81d78e645f Bug 1827557 part 2: CachedTextMarker: Differentiate between LeftLine and Line. r=eeejay
Line should return the current line when the start of the line is queried.
Otherwise, VoiceOver reports the previous line if you're on the first character of a line and you move by line with down or up arrow.

LegacyTextMarker behaves inconsistently when you use Line/LeftLine depending on whether you fetched the marker via index or from a selection range.
browser_text_basics.js treated the index behaviour as correct, but it isn't.
The tests have been updated accordingly with expected failures for non-cached.

Differential Revision: https://phabricator.services.mozilla.com/D176608
2023-04-27 08:27:24 +00:00

403 lines
10 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
function testRangeAtMarker(macDoc, marker, attribute, expected, msg) {
let range = macDoc.getParameterizedAttributeValue(attribute, marker);
is(stringForRange(macDoc, range), expected, msg);
}
function testUIElement(
macDoc,
marker,
msg,
expectedRole,
expectedValue,
expectedRange
) {
let elem = macDoc.getParameterizedAttributeValue(
"AXUIElementForTextMarker",
marker
);
is(
elem.getAttributeValue("AXRole"),
expectedRole,
`${msg}: element role matches`
);
is(elem.getAttributeValue("AXValue"), expectedValue, `${msg}: element value`);
let elemRange = macDoc.getParameterizedAttributeValue(
"AXTextMarkerRangeForUIElement",
elem
);
is(
stringForRange(macDoc, elemRange),
expectedRange,
`${msg}: element range matches element value`
);
}
function testStyleRun(macDoc, marker, msg, expectedStyleRun) {
testRangeAtMarker(
macDoc,
marker,
"AXStyleTextMarkerRangeForTextMarker",
expectedStyleRun,
`${msg}: style run matches`
);
}
function testParagraph(macDoc, marker, msg, expectedParagraph) {
testRangeAtMarker(
macDoc,
marker,
"AXParagraphTextMarkerRangeForTextMarker",
expectedParagraph,
`${msg}: paragraph matches`
);
}
function testWords(macDoc, marker, msg, expectedLeft, expectedRight) {
testRangeAtMarker(
macDoc,
marker,
"AXLeftWordTextMarkerRangeForTextMarker",
expectedLeft,
`${msg}: left word matches`
);
testRangeAtMarker(
macDoc,
marker,
"AXRightWordTextMarkerRangeForTextMarker",
expectedRight,
`${msg}: right word matches`
);
}
function testLines(
macDoc,
marker,
msg,
expectedLine,
expectedLeft,
expectedRight
) {
if (!isCacheEnabled && expectedLine != expectedLeft) {
todo(false, `${msg}: line broken at start with cache disabled`);
} else {
testRangeAtMarker(
macDoc,
marker,
"AXLineTextMarkerRangeForTextMarker",
expectedLine,
`${msg}: line matches`
);
}
testRangeAtMarker(
macDoc,
marker,
"AXLeftLineTextMarkerRangeForTextMarker",
expectedLeft,
`${msg}: left line matches`
);
testRangeAtMarker(
macDoc,
marker,
"AXRightLineTextMarkerRangeForTextMarker",
expectedRight,
`${msg}: right line matches`
);
}
function* markerIterator(macDoc, reverse = false) {
let m = macDoc.getAttributeValue(
reverse ? "AXEndTextMarker" : "AXStartTextMarker"
);
let c = 0;
while (m) {
yield [m, c++];
m = macDoc.getParameterizedAttributeValue(
reverse
? "AXPreviousTextMarkerForTextMarker"
: "AXNextTextMarkerForTextMarker",
m
);
}
}
// Tests consistency in text markers between:
// 1. "Linked list" forward navagation
// 2. Getting markers by index
// 3. "Linked list" reverse navagation
// For each iteration method check that the returned index is consistent
function testMarkerIntegrity(accDoc, expectedMarkerValues) {
let macDoc = accDoc.nativeInterface.QueryInterface(
Ci.nsIAccessibleMacInterface
);
// Iterate forward with "AXNextTextMarkerForTextMarker"
let prevMarker;
let count = 0;
for (let [marker, index] of markerIterator(macDoc)) {
count++;
let markerIndex = macDoc.getParameterizedAttributeValue(
"AXIndexForTextMarker",
marker
);
is(
markerIndex,
index,
`Correct index in "AXNextTextMarkerForTextMarker": ${index}`
);
if (prevMarker) {
let range = macDoc.getParameterizedAttributeValue(
"AXTextMarkerRangeForUnorderedTextMarkers",
[prevMarker, marker]
);
is(
macDoc.getParameterizedAttributeValue(
"AXLengthForTextMarkerRange",
range
),
1,
`[${index}] marker moved one character`
);
}
prevMarker = marker;
testWords(
macDoc,
marker,
`At index ${index}`,
...expectedMarkerValues[index].words
);
testLines(
macDoc,
marker,
`At index ${index}`,
...expectedMarkerValues[index].lines
);
testUIElement(
macDoc,
marker,
`At index ${index}`,
...expectedMarkerValues[index].element
);
testParagraph(
macDoc,
marker,
`At index ${index}`,
expectedMarkerValues[index].paragraph
);
testStyleRun(
macDoc,
marker,
`At index ${index}`,
expectedMarkerValues[index].style
);
}
is(expectedMarkerValues.length, count, `Correct marker count: ${count}`);
// Use "AXTextMarkerForIndex" to retrieve all text markers
for (let i = 0; i < count; i++) {
let marker = macDoc.getParameterizedAttributeValue(
"AXTextMarkerForIndex",
i
);
let index = macDoc.getParameterizedAttributeValue(
"AXIndexForTextMarker",
marker
);
is(index, i, `Correct index in "AXTextMarkerForIndex": ${i}`);
if (i == count - 1) {
ok(
!macDoc.getParameterizedAttributeValue(
"AXNextTextMarkerForTextMarker",
marker
),
"Iterated through all markers"
);
}
}
count = expectedMarkerValues.length;
// Iterate backward with "AXPreviousTextMarkerForTextMarker"
for (let [marker] of markerIterator(macDoc, true)) {
if (count <= 0) {
ok(false, "Exceeding marker count");
break;
}
count--;
let index = macDoc.getParameterizedAttributeValue(
"AXIndexForTextMarker",
marker
);
is(
index,
count,
`Correct index in "AXPreviousTextMarkerForTextMarker": ${count}`
);
}
is(count, 0, "Iterated backward through all text markers");
}
addAccessibleTask("mac/doc_textmarker_test.html", async (browser, accDoc) => {
const expectedValues = await SpecialPowers.spawn(browser, [], async () => {
return content.wrappedJSObject.EXPECTED;
});
if (isCacheEnabled) {
// Some changes in expected results when cache is enabled
// These are more correct return values (or at least not more incorrect)
expectedValues[231].words[1] = "Skip'";
expectedValues[248].lines[1] = "These ";
expectedValues[252].lines[1] = "are ";
expectedValues[255].lines[1] = "my ";
expectedValues[261].words[0] = expectedValues[261].words[1] = "awards,";
expectedValues[263].lines[1] = "awards, ";
expectedValues[269].words[0] = expectedValues[269].words[1] = "Mother.";
expectedValues[271].lines[1] = "Mother. ";
expectedValues[276].lines[1] = "From ";
expectedValues[269].paragraph = "These are my awards, Mother. From Army.";
expectedValues[283].words[0] = "deceived";
expectedValues[295].paragraph = "I deceived you, mom.";
expectedValues[295].words[0] = "";
expectedValues[297].words[0] = " ";
}
testMarkerIntegrity(accDoc, expectedValues);
});
// Test text marker lesser-than operator
addAccessibleTask(
`<p id="p">hello <a id="a" href="#">goodbye</a> world</p>`,
async (browser, accDoc) => {
let macDoc = accDoc.nativeInterface.QueryInterface(
Ci.nsIAccessibleMacInterface
);
let start = macDoc.getParameterizedAttributeValue(
"AXTextMarkerForIndex",
1
);
let end = macDoc.getParameterizedAttributeValue("AXTextMarkerForIndex", 10);
let range = macDoc.getParameterizedAttributeValue(
"AXTextMarkerRangeForUnorderedTextMarkers",
[end, start]
);
is(stringForRange(macDoc, range), "ello good");
}
);
addAccessibleTask(
`<input id="input" value=""><a href="#">goodbye</a>`,
async (browser, accDoc) => {
let macDoc = accDoc.nativeInterface.QueryInterface(
Ci.nsIAccessibleMacInterface
);
let input = getNativeInterface(accDoc, "input");
let range = macDoc.getParameterizedAttributeValue(
"AXTextMarkerRangeForUIElement",
input
);
is(stringForRange(macDoc, range), "", "string value is correct");
}
);
addAccessibleTask(
`<div role="listbox" id="box">
<input type="radio" name="test" role="option" title="First item"/>
<input type="radio" name="test" role="option" title="Second item"/>
</div>`,
async (browser, accDoc) => {
let box = getNativeInterface(accDoc, "box");
const children = box.getAttributeValue("AXChildren");
is(children.length, 2, "Listbox contains two items");
is(children[0].getAttributeValue("AXValue"), "First item");
is(children[1].getAttributeValue("AXValue"), "Second item");
}
);
addAccessibleTask(
`<div id="t">
A link <b>should</b> explain <em>clearly</em> what information the <i>reader</i> will get by clicking on that link.
</div>`,
async (browser, accDoc) => {
let t = getNativeInterface(accDoc, "t");
const children = t.getAttributeValue("AXChildren");
const expectedTitles = [
"A link ",
"should",
" explain ",
"clearly",
" what information the ",
"reader",
" will get by clicking on that link. ",
];
is(children.length, 7, "container has seven children");
children.forEach((child, index) => {
is(child.getAttributeValue("AXValue"), expectedTitles[index]);
});
}
);
addAccessibleTask(
`<a href="#">link</a> <input id="input" value="hello">`,
async (browser, accDoc) => {
let macDoc = accDoc.nativeInterface.QueryInterface(
Ci.nsIAccessibleMacInterface
);
let input = getNativeInterface(accDoc, "input");
let range = macDoc.getParameterizedAttributeValue(
"AXTextMarkerRangeForUIElement",
input
);
let firstMarkerInInput = macDoc.getParameterizedAttributeValue(
"AXStartTextMarkerForTextMarkerRange",
range
);
let leftWordRange = macDoc.getParameterizedAttributeValue(
"AXLeftWordTextMarkerRangeForTextMarker",
firstMarkerInInput
);
let str = macDoc.getParameterizedAttributeValue(
"AXStringForTextMarkerRange",
leftWordRange
);
is(str, "hello", "Left word at start of input should be right word");
}
);
addAccessibleTask(`<p id="p">hello world</p>`, async (browser, accDoc) => {
let macDoc = accDoc.nativeInterface.QueryInterface(
Ci.nsIAccessibleMacInterface
);
let p = getNativeInterface(accDoc, "p");
let range = macDoc.getParameterizedAttributeValue(
"AXTextMarkerRangeForUIElement",
p
);
let bounds = macDoc.getParameterizedAttributeValue(
"AXBoundsForTextMarkerRange",
range
);
ok(bounds.origin && bounds.size, "Returned valid bounds");
});