firefox-desktop/accessible/tests/browser/windows/uia/browser_textPatterns.js
2025-03-05 19:56:11 +01:00

2661 lines
86 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";
/* import-globals-from ../../../mochitest/text.js */
loadScripts({ name: "text.js", dir: MOCHITESTS_DIR });
/* eslint-disable camelcase */
const SupportedTextSelection_None = 0;
const SupportedTextSelection_Multiple = 2;
/* eslint-enable camelcase */
/**
* Test the Text pattern's DocumentRange property. This also tests where the
* Text pattern is exposed.
*/
addUiaTask(
`
<div><input id="input" value="input"></div>
<textarea id="textarea">textarea</textarea>
<div id="contentEditable" contenteditable><p>content</p><p>editable</p></div>
<p id="p">p</p>
<a id="link" href="#">link</a>
`,
async function testTextDocumentRange() {
await definePyVar("doc", `getDocUia()`);
await definePyVar("pattern", `getUiaPattern(doc, "Text")`);
ok(await runPython(`bool(pattern)`), "doc has Text pattern");
// The IA2 -> UIA proxy adds spaces between elements that don't exist.
if (gIsUiaEnabled) {
is(
await runPython(`pattern.DocumentRange.GetText(-1)`),
"inputtextareacontenteditableplink",
"document DocumentRange Text correct"
);
}
await assignPyVarToUiaWithId("input");
await definePyVar("pattern", `getUiaPattern(input, "Text")`);
ok(await runPython(`bool(pattern)`), "input has Text pattern");
is(
await runPython(`pattern.DocumentRange.GetText(-1)`),
"input",
"input DocumentRange Text correct"
);
await assignPyVarToUiaWithId("textarea");
await definePyVar("pattern", `getUiaPattern(textarea, "Text")`);
ok(await runPython(`bool(pattern)`), "textarea has Text pattern");
is(
await runPython(`pattern.DocumentRange.GetText(-1)`),
"textarea",
"textarea DocumentRange Text correct"
);
// The IA2 -> UIA proxy doesn't expose the Text pattern on contentEditables
// without role="textbox".
if (gIsUiaEnabled) {
await assignPyVarToUiaWithId("contentEditable");
await definePyVar("pattern", `getUiaPattern(contentEditable, "Text")`);
ok(await runPython(`bool(pattern)`), "contentEditable has Text pattern");
is(
await runPython(`pattern.DocumentRange.GetText(-1)`),
"contenteditable",
"contentEditable DocumentRange Text correct"
);
}
await testPatternAbsent("p", "Text");
// The IA2 -> UIA proxy doesn't expose the Text pattern on this text leaf.
if (gIsUiaEnabled) {
await runPython(`
global pLeaf
p = findUiaByDomId(doc, "p")
pLeaf = uiaClient.RawViewWalker.GetFirstChildElement(p)
`);
await definePyVar("pattern", `getUiaPattern(pLeaf, "Text")`);
ok(await runPython(`bool(pattern)`), "pLeaf has Text pattern");
is(
await runPython(`pattern.DocumentRange.GetText(-1)`),
"p",
"pLeaf DocumentRange Text correct"
);
}
await testPatternAbsent("link", "Text");
// The IA2 -> UIA proxy doesn't expose this text leaf at all.
if (gIsUiaEnabled) {
await runPython(`
global linkLeaf
link = findUiaByDomId(doc, "link")
linkLeaf = uiaClient.RawViewWalker.GetFirstChildElement(link)
`);
await definePyVar("pattern", `getUiaPattern(linkLeaf, "Text")`);
ok(await runPython(`bool(pattern)`), "linkLeaf has Text pattern");
is(
await runPython(`pattern.DocumentRange.GetText(-1)`),
"link",
"linkLeaf DocumentRange Text correct"
);
}
}
);
/**
* Test the TextRange pattern's GetText method.
*/
addUiaTask(
`<div id="editable" contenteditable role="textbox">a <span>b</span>`,
async function testTextRangeGetText() {
await runPython(`
doc = getDocUia()
editable = findUiaByDomId(doc, "editable")
text = getUiaPattern(editable, "Text")
global range
range = text.DocumentRange
`);
is(await runPython(`range.GetText(-1)`), "a b", "GetText(-1) correct");
is(await runPython(`range.GetText(0)`), "", "GetText(0) correct");
is(await runPython(`range.GetText(1)`), "a", "GetText(1) correct");
is(await runPython(`range.GetText(2)`), "a ", "GetText(2) correct");
is(await runPython(`range.GetText(3)`), "a b", "GetText(3) correct");
is(await runPython(`range.GetText(4)`), "a b", "GetText(4) correct");
}
);
/**
* Test the TextRange pattern's Clone method.
*/
addUiaTask(
`<input id="input" type="text" value="testing">`,
async function testTextRangeClone() {
await runPython(`
doc = getDocUia()
input = findUiaByDomId(doc, "input")
text = getUiaPattern(input, "Text")
global origRange
origRange = text.DocumentRange
`);
is(
await runPython(`origRange.GetText(-1)`),
"testing",
"origRange text correct"
);
await runPython(`
global clonedRange
clonedRange = origRange.Clone()
`);
is(
await runPython(`clonedRange.GetText(-1)`),
"testing",
"clonedRange text correct"
);
// Test that modifying clonedRange doesn't impact origRange.
info("Collapsing clonedRange to start");
await runPython(
`clonedRange.MoveEndpointByRange(TextPatternRangeEndpoint_End, clonedRange, TextPatternRangeEndpoint_Start)`
);
is(
await runPython(`clonedRange.GetText(-1)`),
"",
"clonedRange text correct"
);
is(
await runPython(`origRange.GetText(-1)`),
"testing",
"origRange text correct"
);
}
);
/**
* Test the TextRange pattern's Compare method.
*/
addUiaTask(
`<input id="input" type="text" value="testing">`,
async function testTextRangeCompare() {
await runPython(`
doc = getDocUia()
input = findUiaByDomId(doc, "input")
text = getUiaPattern(input, "Text")
global range1, range2
range1 = text.DocumentRange
range2 = text.DocumentRange
`);
ok(
await runPython(`range1.Compare(range2)`),
"range1 Compare range2 correct"
);
ok(
await runPython(`range2.Compare(range1)`),
"range2 Compare range1 correct"
);
info("Collapsing range2 to start");
await runPython(
`range2.MoveEndpointByRange(TextPatternRangeEndpoint_End, range2, TextPatternRangeEndpoint_Start)`
);
ok(
!(await runPython(`range1.Compare(range2)`)),
"range1 Compare range2 correct"
);
ok(
!(await runPython(`range2.Compare(range1)`)),
"range2 Compare range1 correct"
);
}
);
/**
* Test the TextRange pattern's CompareEndpoints method.
*/
addUiaTask(
`
<p>before</p>
<div><input id="input" type="text" value="input"></div>
<p>after</p>
`,
async function testTextRangeCompareEndpoints() {
await runPython(`
global doc, range1, range2
doc = getDocUia()
input = findUiaByDomId(doc, "input")
text = getUiaPattern(input, "Text")
range1 = text.DocumentRange
range2 = text.DocumentRange
`);
is(
await runPython(
`range1.CompareEndpoints(TextPatternRangeEndpoint_Start, range1, TextPatternRangeEndpoint_Start)`
),
0,
"Compare range1 start to range1 start correct"
);
is(
await runPython(
`range1.CompareEndpoints(TextPatternRangeEndpoint_End, range1, TextPatternRangeEndpoint_End)`
),
0,
"Compare range1 end to range1 end correct"
);
is(
await runPython(
`range1.CompareEndpoints(TextPatternRangeEndpoint_Start, range1, TextPatternRangeEndpoint_End)`
),
-1,
"Compare range1 start to range1 end correct"
);
is(
await runPython(
`range1.CompareEndpoints(TextPatternRangeEndpoint_End, range1, TextPatternRangeEndpoint_Start)`
),
1,
"Compare range1 end to range1 start correct"
);
// Compare different ranges.
is(
await runPython(
`range1.CompareEndpoints(TextPatternRangeEndpoint_Start, range2, TextPatternRangeEndpoint_Start)`
),
0,
"Compare range1 start to range2 start correct"
);
is(
await runPython(
`range1.CompareEndpoints(TextPatternRangeEndpoint_End, range2, TextPatternRangeEndpoint_End)`
),
0,
"Compare range1 end to range2 end correct"
);
is(
await runPython(
`range1.CompareEndpoints(TextPatternRangeEndpoint_Start, range2, TextPatternRangeEndpoint_End)`
),
-1,
"Compare range1 start to range2 end correct"
);
is(
await runPython(
`range1.CompareEndpoints(TextPatternRangeEndpoint_End, range2, TextPatternRangeEndpoint_Start)`
),
1,
"Compare range1 end to range2 start correct"
);
// Compare ranges created using different elements.
await definePyVar("range3", `getUiaPattern(doc, "Text").DocumentRange`);
is(
await runPython(
`range1.CompareEndpoints(TextPatternRangeEndpoint_Start, range3, TextPatternRangeEndpoint_Start)`
),
1,
"Compare range1 start to range3 start correct"
);
is(
await runPython(
`range1.CompareEndpoints(TextPatternRangeEndpoint_End, range3, TextPatternRangeEndpoint_End)`
),
-1,
"Compare range1 end to range3 end correct"
);
}
);
/**
* Test the TextRange pattern's ExpandToEnclosingUnit method.
*/
addUiaTask(
`
<p>before</p>
<div><textarea id="textarea" cols="5">ab cd ef gh</textarea></div>
<div>after <input id="input" value="input"></div>
`,
async function testTextRangeExpandToEnclosingUnit() {
info("Getting DocumentRange from textarea");
await runPython(`
global doc, range
doc = getDocUia()
textarea = findUiaByDomId(doc, "textarea")
text = getUiaPattern(textarea, "Text")
range = text.DocumentRange
`);
is(
await runPython(`range.GetText(-1)`),
"ab cd ef gh",
"range text correct"
);
// Expand should shrink the range because it's too big.
info("Expanding to character");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
is(await runPython(`range.GetText(-1)`), "a", "range text correct");
info("Collapsing to end");
await runPython(
`range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)`
);
is(await runPython(`range.GetText(-1)`), "", "range text correct");
// range is now collapsed at "b".
info("Expanding to character");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
is(await runPython(`range.GetText(-1)`), "b", "range text correct");
info("Expanding to word");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Word)`);
is(await runPython(`range.GetText(-1)`), "ab ", "range text correct");
info("Collapsing to end");
await runPython(
`range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)`
);
// range is now collapsed at "c".
info("Expanding to word");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Word)`);
is(await runPython(`range.GetText(-1)`), "cd ", "range text correct");
info("Expanding to line");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Line)`);
is(await runPython(`range.GetText(-1)`), "ab cd ", "range text correct");
info("Collapsing to end");
await runPython(
`range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)`
);
// range is now collapsed at "e".
info("Expanding to line");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Line)`);
// The IA2 -> UIA proxy gets most things below this wrong.
if (!gIsUiaEnabled) {
return;
}
is(await runPython(`range.GetText(-1)`), "ef gh", "range text correct");
info("Expanding to document");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Document)`);
is(
await runPython(`range.GetText(-1)`),
"beforeab cd ef ghafter input",
"range text correct"
);
// Test expanding to a line which crosses elements.
info("Getting DocumentRange from input");
await runPython(`
input = findUiaByDomId(doc, "input")
text = getUiaPattern(input, "Text")
global range
range = text.DocumentRange
`);
info("Expanding to line");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Line)`);
is(
await runPython(`range.GetText(-1)`),
"after input",
"range text correct"
);
info("Collapsing to end");
await runPython(
`range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)`
);
// range is now collapsed at the end of the document.
info("Expanding to line");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Line)`);
is(
await runPython(`range.GetText(-1)`),
"after input",
"range text correct"
);
}
);
/**
* Test the Format TextUnit. Exercises ExpandToEnclosingUnit, Move, and
* MoveEndpointByUnit. Tested here separately since the setup and implementation
* is somewhat different from other TextUnits.
*/
addUiaTask(
`
<div id="bold-container">a <b>bcd</b> ef</div>
<div id="container-container">a <span tabindex="0">bcd</span> ef</div>
<textarea id="textarea" spellcheck="true">test tset test</textarea>
`,
async function testTextRangeMove(browser, docAcc) {
info("Constructing range on bold text run");
await runPython(`
global doc, docText, range
doc = getDocUia()
docText = getUiaPattern(doc, "Text")
boldContainerAcc = findUiaByDomId(doc, "bold-container")
range = docText.RangeFromChild(boldContainerAcc)
`);
is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
info("Moving to bold text run");
is(
await runPython(`range.Move(TextUnit_Format, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
// Testing ExpandToEnclosingUnit (on formatting boundaries)
info("Expanding to character (shrinking the range)");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
is(await runPython(`range.GetText(-1)`), "b", "range text correct");
info("Expanding to Format");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Format)`);
is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
info("Making range larger than the Format unit");
is(
await runPython(
`range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, 1)`
),
1,
"MoveEndpointByUnit return correct"
);
is(await runPython(`range.GetText(-1)`), "bcd ", "range text correct");
info("Expanding to Format (shrinking the range)");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Format)`);
is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
// Testing Move (on formatting boundaries)
info("Moving 1 Format unit");
is(
await runPython(`range.Move(TextUnit_Format, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), " ef", "range text correct");
info("Moving -3 Format units (but only -2 are left)");
is(
await runPython(`range.Move(TextUnit_Format, -3)`),
-2,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "a ", "range text correct");
// Testing MoveEndpointByUnit (on formatting boundaries)
info("Moving end 1 Format unit");
is(
await runPython(
`range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Format, 1)`
),
1,
"MoveEndpointByUnit return correct"
);
is(await runPython(`range.GetText(-1)`), "a bcd", "range text correct");
info("Moving start 1 Format unit");
is(
await runPython(
`range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Format, 1)`
),
1,
"MoveEndpointByUnit return correct"
);
is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
// Testing above three methods on text runs defined by container boundaries
info("Constructing range on text run defined by container boundaries");
await runPython(`
global doc, docText, range
containerContainer = findUiaByDomId(doc, "container-container")
range = docText.RangeFromChild(containerContainer)
`);
is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
info("Expanding to Format");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Format)`);
is(await runPython(`range.GetText(-1)`), "a ", "range text correct");
info("Moving 1 Format unit");
is(
await runPython(`range.Move(TextUnit_Format, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
info("Moving start -1 Format unit");
is(
await runPython(
`range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Format, -1)`
),
-1,
"MoveEndpointByUnit return correct"
);
is(await runPython(`range.GetText(-1)`), "a bcd", "range text correct");
// Trigger spelling errors so we can test text offset attributes
const textarea = findAccessibleChildByID(docAcc, "textarea");
textarea.takeFocus();
await waitForEvent(EVENT_TEXT_ATTRIBUTE_CHANGED);
// Testing above three methods on text offset attributes
info("Constructing range on italic text run");
await runPython(`
global doc, docText, range
textarea = findUiaByDomId(doc, "textarea")
range = docText.RangeFromChild(textarea)
`);
is(
await runPython(`range.GetText(-1)`),
"test tset test",
"range text correct"
);
info("Expanding to Format");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Format)`);
is(await runPython(`range.GetText(-1)`), "test ", "range text correct");
info("Moving 1 Format unit");
is(
await runPython(`range.Move(TextUnit_Format, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "tset", "range text correct");
info("Moving start -1 Format unit");
is(
await runPython(
`range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Format, -1)`
),
-1,
"MoveEndpointByUnit return correct"
);
is(await runPython(`range.GetText(-1)`), "test tset", "range text correct");
}
);
/**
* Test the GetAttributeValue method. Verify the behavior of various UIA
* Attribute IDs.
*/
addUiaTask(
`
<div id="font-weight-container">a <span tabindex="0"><b>bcd</b></span><b> ef</b></div>
<div id="font-size-container">a <span style="font-size:20px">bcd</span> ef</div>
<div id="font-family-container">a <span style="font-family:Arial">bcd</span> ef</div>
<div id="italic-container">a <span style="font-style:italic">bcd</span> ef</div>
<div id="subscript-container">a <sub>bcd</sub> ef</div>
<div id="superscript-container">a <sup>bcd</sup> ef</div>
<div id="not-hidden-container">a bcd ef</div>
<div id="readonly-container">a <span contenteditable="true">bcd</span> ef</div>
<input id="text-input"/>
<div id="spelling-error-container">a <span aria-invalid="spelling">bcd</span> ef</div>
<div id="grammar-error-container">a <span aria-invalid="grammar">bcd</span> ef</div>
<div id="data-validation-error-container">a <span aria-invalid="true">bcd</span> ef</div>
<div id="highlight-container">a highlighted phrase ef</div>
<div id="heading-container">ab<h3>h3</h3>cd</div>
<div id="blockquote-container">ab<blockquote>quote</blockquote>cd</div>
<div id="emphasis-container">ab<em>emph</em>cd</div>
`,
async function testTextRangeGetAttributeValue() {
// ================== UIA_FontWeightAttributeId ==================
info("Constructing range on bold text run");
await runPython(`
global doc, docText, range
doc = getDocUia()
docText = getUiaPattern(doc, "Text")
fontWeightContainerAcc = findUiaByDomId(doc, "font-weight-container")
range = docText.RangeFromChild(fontWeightContainerAcc)
`);
is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
info("checking mixed font weights");
ok(
await runPython(`
val = range.GetAttributeValue(UIA_FontWeightAttributeId)
return val == uiaClient.ReservedMixedAttributeValue
`),
"FontWeight correct (mixed)"
);
info("Moving to bold text run");
is(
await runPython(`range.Move(TextUnit_Format, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
info("checking FontWeight");
is(
await runPython(`range.GetAttributeValue(UIA_FontWeightAttributeId)`),
700,
"FontWeight correct"
);
info("Moving end 1 Format unit");
is(
await runPython(
`range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Format, 1)`
),
1,
"MoveEndpointByUnit return correct"
);
is(await runPython(`range.GetText(-1)`), "bcd ef", "range text correct");
info(
"checking font weight (across equivalent container-separated Format runs)"
);
is(
await runPython(`range.GetAttributeValue(UIA_FontWeightAttributeId)`),
700,
"FontWeight correct"
);
// ================== UIA_FontSizeAttributeId ==================
await runPython(`
global range
fontSizeContainerAcc = findUiaByDomId(doc, "font-size-container")
range = docText.RangeFromChild(fontSizeContainerAcc)
`);
is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
info("checking mixed font weights");
ok(
await runPython(`
val = range.GetAttributeValue(UIA_FontSizeAttributeId)
return val == uiaClient.ReservedMixedAttributeValue
`),
"FontSize correct (mixed)"
);
info("Moving to increased font-size text run");
is(
await runPython(`range.Move(TextUnit_Format, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
info("checking FontSize");
is(
await runPython(`range.GetAttributeValue(UIA_FontSizeAttributeId)`),
15,
"FontSize correct"
);
// ================== UIA_FontNameAttributeId ==================
await runPython(`
global range
fontFamilyContainerAcc = findUiaByDomId(doc, "font-family-container")
range = docText.RangeFromChild(fontFamilyContainerAcc)
`);
is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
info("checking mixed font families");
ok(
await runPython(`
val = range.GetAttributeValue(UIA_FontNameAttributeId)
return val == uiaClient.ReservedMixedAttributeValue
`),
"FontName correct (mixed)"
);
info("Moving to sans-serif font-family text run");
is(
await runPython(`range.Move(TextUnit_Format, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
info("checking FontName");
is(
await runPython(`range.GetAttributeValue(UIA_FontNameAttributeId)`),
"Arial",
"FontName correct"
);
// ================== UIA_IsItalicAttributeId ==================
await runPython(`
global range
italicContainerAcc = findUiaByDomId(doc, "italic-container")
range = docText.RangeFromChild(italicContainerAcc)
`);
is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
info("checking mixed IsItalic properties");
ok(
await runPython(`
val = range.GetAttributeValue(UIA_IsItalicAttributeId)
return val == uiaClient.ReservedMixedAttributeValue
`),
"IsItalic correct (mixed)"
);
info("Moving to italic text run");
is(
await runPython(`range.Move(TextUnit_Format, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
info("checking IsItalic");
is(
await runPython(`range.GetAttributeValue(UIA_IsItalicAttributeId)`),
true,
"IsItalic correct"
);
// ================== UIA_IsSubscriptAttributeId ==================
await runPython(`
global range
subscriptContainerAcc = findUiaByDomId(doc, "subscript-container")
range = docText.RangeFromChild(subscriptContainerAcc)
`);
is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
info("checking mixed IsSubscript properties");
ok(
await runPython(`
val = range.GetAttributeValue(UIA_IsSubscriptAttributeId)
return val == uiaClient.ReservedMixedAttributeValue
`),
"IsSubscript correct (mixed)"
);
info("Moving to subscript text run");
is(
await runPython(`range.Move(TextUnit_Format, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
info("checking IsSubscript");
is(
await runPython(`range.GetAttributeValue(UIA_IsSubscriptAttributeId)`),
true,
"IsSubscript correct"
);
// ================== UIA_IsSuperscriptAttributeId ==================
await runPython(`
global range
superscriptContainerAcc = findUiaByDomId(doc, "superscript-container")
range = docText.RangeFromChild(superscriptContainerAcc)
`);
is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
info("checking mixed IsSuperscript properties");
ok(
await runPython(`
val = range.GetAttributeValue(UIA_IsSuperscriptAttributeId)
return val == uiaClient.ReservedMixedAttributeValue
`),
"IsSuperscript correct (mixed)"
);
info("Moving to superscript text run");
is(
await runPython(`range.Move(TextUnit_Format, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
info("checking IsSuperscript");
is(
await runPython(`range.GetAttributeValue(UIA_IsSuperscriptAttributeId)`),
true,
"IsSuperscript correct"
);
// ================== UIA_IsHiddenAttributeId ==================
// Testing the "true" case is not really possible since these Accessible
// nodes are not present in the tree. Verify the "false" case.
await runPython(`
global range
notHiddenContainerAcc = findUiaByDomId(doc, "not-hidden-container")
range = docText.RangeFromChild(notHiddenContainerAcc)
`);
is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
info("checking mixed IsHidden properties");
ok(
await runPython(`
val = range.GetAttributeValue(UIA_IsHiddenAttributeId)
return val != uiaClient.ReservedMixedAttributeValue
`),
"IsHidden correct (not mixed)"
);
// ================== UIA_IsReadOnlyAttributeId ==================
await runPython(`
global range
readonlyContainerAcc = findUiaByDomId(doc, "readonly-container")
range = docText.RangeFromChild(readonlyContainerAcc)
`);
is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
info("checking mixed ReadOnly properties");
ok(
await runPython(`
val = range.GetAttributeValue(UIA_IsReadOnlyAttributeId)
return val == uiaClient.ReservedMixedAttributeValue
`),
"ReadOnly correct (mixed)"
);
info("Moving to editable text run");
is(
await runPython(`range.Move(TextUnit_Format, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
info("checking IsReadOnly");
is(
await runPython(`range.GetAttributeValue(UIA_IsReadOnlyAttributeId)`),
false,
"IsReadOnly correct"
);
// Verify that text inputs are not read-only by default.
await runPython(`
global range
textInputAcc = findUiaByDomId(doc, "text-input")
range = docText.RangeFromChild(textInputAcc)
`);
info("checking IsReadOnly");
is(
await runPython(`range.GetAttributeValue(UIA_IsReadOnlyAttributeId)`),
false,
"IsReadOnly correct for text input"
);
// ================== UIA_AnnotationTypesAttributeId - AnnotationType_SpellingError ==================
await runPython(`
global range
spellingErrorContainerAcc = findUiaByDomId(doc, "spelling-error-container")
range = docText.RangeFromChild(spellingErrorContainerAcc)
`);
is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
info("checking mixed SpellingError properties");
ok(
await runPython(`
val = range.GetAttributeValue(UIA_AnnotationTypesAttributeId)
return val == uiaClient.ReservedMixedAttributeValue
`),
"SpellingError correct (mixed)"
);
info('Moving to aria-invalid="spelling" text run');
is(
await runPython(`range.Move(TextUnit_Format, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
info("checking SpellingError");
ok(
await runPython(`
annotations = range.GetAttributeValue(UIA_AnnotationTypesAttributeId)
return annotations == (AnnotationType_SpellingError,)
`),
"SpellingError correct"
);
// ================== UIA_AnnotationTypesAttributeId - AnnotationType_GrammarError ==================
await runPython(`
global range
grammarErrorContainerAcc = findUiaByDomId(doc, "grammar-error-container")
range = docText.RangeFromChild(grammarErrorContainerAcc)
`);
is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
info("checking mixed GrammarError properties");
ok(
await runPython(`
val = range.GetAttributeValue(UIA_AnnotationTypesAttributeId)
return val == uiaClient.ReservedMixedAttributeValue
`),
"GrammarError correct (mixed)"
);
info('Moving to aria-invalid="grammar" text run');
is(
await runPython(`range.Move(TextUnit_Format, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
info("checking GrammarError");
ok(
await runPython(`
annotations = range.GetAttributeValue(UIA_AnnotationTypesAttributeId)
return annotations == (AnnotationType_GrammarError,)
`),
"GrammarError correct"
);
// ================== UIA_AnnotationTypesAttributeId - AnnotationType_DataValidationError ==================
// The IA2 -> UIA bridge does not work for aria-invalid=true or highlights.
if (gIsUiaEnabled) {
await runPython(`
global range
dataValidationErrorContainerAcc = findUiaByDomId(doc, "data-validation-error-container")
range = docText.RangeFromChild(dataValidationErrorContainerAcc)
`);
is(
await runPython(`range.GetText(-1)`),
"a bcd ef",
"range text correct"
);
info("checking mixed DataValidationError properties");
ok(
await runPython(`
val = range.GetAttributeValue(UIA_AnnotationTypesAttributeId)
return val == uiaClient.ReservedMixedAttributeValue
`),
"DataValidationError correct (mixed)"
);
info('Moving to aria-invalid="true" text run');
is(
await runPython(`range.Move(TextUnit_Format, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
info("checking DataValidationError");
ok(
await runPython(`
annotations = range.GetAttributeValue(UIA_AnnotationTypesAttributeId)
return annotations == (AnnotationType_DataValidationError,)
`),
"DataValidationError correct"
);
// ================== UIA_AnnotationTypesAttributeId - AnnotationType_Highlighted ==================
await runPython(`
global range
highlightContainerAcc = findUiaByDomId(doc, "highlight-container")
range = docText.RangeFromChild(highlightContainerAcc)
`);
is(
await runPython(`range.GetText(-1)`),
"a highlighted phrase ef",
"range text correct"
);
info("checking mixed Highlighted properties");
ok(
await runPython(`
val = range.GetAttributeValue(UIA_AnnotationTypesAttributeId)
return val == uiaClient.ReservedMixedAttributeValue
`),
"Highlighted correct (mixed)"
);
info("Moving to highlighted text run");
is(
await runPython(`range.Move(TextUnit_Format, 1)`),
1,
"Move return correct"
);
is(
await runPython(`range.GetText(-1)`),
"highlighted phrase",
"range text correct"
);
info("checking Highlighted");
ok(
await runPython(`
annotations = range.GetAttributeValue(UIA_AnnotationTypesAttributeId)
return annotations == (AnnotationType_Highlighted,)
`),
"Highlighted correct"
);
}
// The IA2 -> UIA bridge does not work correctly here.
if (gIsUiaEnabled) {
// ================== UIA_StyleIdAttributeId - StyleId_Heading* ==================
await runPython(`
global range
headingContainerAcc = findUiaByDomId(doc, "heading-container")
range = docText.RangeFromChild(headingContainerAcc)
`);
is(await runPython(`range.GetText(-1)`), "abh3cd", "range text correct");
info("checking mixed StyleId properties");
ok(
await runPython(`
val = range.GetAttributeValue(UIA_StyleIdAttributeId)
return val == uiaClient.ReservedMixedAttributeValue
`),
"StyleId correct (mixed)"
);
info("Moving to h3 text run");
is(
await runPython(`range.Move(TextUnit_Format, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "h3", "range text correct");
info("checking StyleId");
ok(
await runPython(`
styleId = range.GetAttributeValue(UIA_StyleIdAttributeId)
return styleId == StyleId_Heading3
`),
"StyleId correct"
);
// ================== UIA_StyleIdAttributeId - StyleId_Quote ==================
await runPython(`
global range
blockquoteContainerAcc = findUiaByDomId(doc, "blockquote-container")
range = docText.RangeFromChild(blockquoteContainerAcc)
`);
is(
await runPython(`range.GetText(-1)`),
"abquotecd",
"range text correct"
);
info("checking mixed StyleId properties");
ok(
await runPython(`
val = range.GetAttributeValue(UIA_StyleIdAttributeId)
return val == uiaClient.ReservedMixedAttributeValue
`),
"StyleId correct (mixed)"
);
info("Moving to blockquote text run");
is(
await runPython(`range.Move(TextUnit_Format, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "quote", "range text correct");
info("checking StyleId");
ok(
await runPython(`
styleId = range.GetAttributeValue(UIA_StyleIdAttributeId)
return styleId == StyleId_Quote
`),
"StyleId correct"
);
// ================== UIA_StyleIdAttributeId - StyleId_Emphasis ==================
await runPython(`
global range
emphasisContainerAcc = findUiaByDomId(doc, "emphasis-container")
range = docText.RangeFromChild(emphasisContainerAcc)
`);
is(
await runPython(`range.GetText(-1)`),
"abemphcd",
"range text correct"
);
info("checking mixed StyleId properties");
ok(
await runPython(`
val = range.GetAttributeValue(UIA_StyleIdAttributeId)
return val == uiaClient.ReservedMixedAttributeValue
`),
"StyleId correct (mixed)"
);
info("Moving to emphasized text run");
is(
await runPython(`range.Move(TextUnit_Format, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "emph", "range text correct");
info("checking StyleId");
ok(
await runPython(`
styleId = range.GetAttributeValue(UIA_StyleIdAttributeId)
return styleId == StyleId_Emphasis
`),
"StyleId correct"
);
}
},
{ urlSuffix: "#:~:text=highlighted%20phrase" }
);
/**
* Test the TextRange pattern's Move method.
*/
addUiaTask(
`
<p>ab</p>
<textarea id="textarea">cd ef gh</textarea>
<p>ij</p>
`,
async function testTextRangeMove() {
await runPython(`
doc = getDocUia()
textarea = findUiaByDomId(doc, "textarea")
text = getUiaPattern(textarea, "Text")
global range
range = text.DocumentRange
`);
is(await runPython(`range.GetText(-1)`), "cd ef gh", "range text correct");
info("Moving 1 word");
is(
await runPython(`range.Move(TextUnit_Word, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "ef ", "range text correct");
info("Moving 3 words");
// There are only 2 words after.
is(
await runPython(`range.Move(TextUnit_Word, 3)`),
2,
"Move return correct"
);
// The IA2 -> UIA proxy gets most things below this wrong.
if (!gIsUiaEnabled) {
return;
}
is(await runPython(`range.GetText(-1)`), "ij", "range text correct");
info("Moving -5 words");
// There are only 4 words before.
is(
await runPython(`range.Move(TextUnit_Word, -5)`),
-4,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "ab", "range text correct");
info("Moving 1 word");
is(
await runPython(`range.Move(TextUnit_Word, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "cd ", "range text correct");
info("Moving 1 character");
is(
await runPython(`range.Move(TextUnit_Character, 1)`),
1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "d", "range text correct");
// When the range is not collapsed, Move moves backward to the start of the
// unit before moving to the requested unit.
info("Moving -1 word");
is(
await runPython(`range.Move(TextUnit_Word, -1)`),
-1,
"Move return correct"
);
is(await runPython(`range.GetText(-1)`), "ab", "range text correct");
info("Collapsing to start");
await runPython(
`range.MoveEndpointByRange(TextPatternRangeEndpoint_End, range, TextPatternRangeEndpoint_Start)`
);
is(await runPython(`range.GetText(-1)`), "", "range text correct");
// range is now collapsed at "a".
info("Moving 1 word");
is(
await runPython(`range.Move(TextUnit_Word, 1)`),
1,
"Move return correct"
);
// range is now collapsed at "c".
is(await runPython(`range.GetText(-1)`), "", "range text correct");
info("Expanding to character");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
is(await runPython(`range.GetText(-1)`), "c", "range text correct");
info("Collapsing to end");
await runPython(
`range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)`
);
// range is now collapsed at "d".
// When the range is collapsed, Move does *not* first move back to the start
// of the unit.
info("Moving -1 word");
is(
await runPython(`range.Move(TextUnit_Word, -1)`),
-1,
"Move return correct"
);
// range is collapsed at "c".
is(await runPython(`range.GetText(-1)`), "", "range text correct");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Word)`);
is(await runPython(`range.GetText(-1)`), "cd ", "range text correct");
}
);
/**
* Test the TextRange pattern's MoveEndpointByRange method.
*/
addUiaTask(
`
<p>ab</p>
<div><textarea id="textarea">cd ef gh</textarea></div>
<p>ij</p>
`,
async function testTextRangeMoveEndpointByRange() {
await runPython(`
global doc, taRange, range
doc = getDocUia()
textarea = findUiaByDomId(doc, "textarea")
text = getUiaPattern(textarea, "Text")
taRange = text.DocumentRange
range = text.DocumentRange
`);
is(await runPython(`range.GetText(-1)`), "cd ef gh", "range text correct");
info("Expanding to character");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
is(await runPython(`range.GetText(-1)`), "c", "range text correct");
is(
await runPython(
`range.CompareEndpoints(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)`
),
-1,
"start < end"
);
info("Moving end to start");
await runPython(
`range.MoveEndpointByRange(TextPatternRangeEndpoint_End, range, TextPatternRangeEndpoint_Start)`
);
is(
await runPython(
`range.CompareEndpoints(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)`
),
0,
"start == end"
);
info("Moving range end to textarea end");
await runPython(
`range.MoveEndpointByRange(TextPatternRangeEndpoint_End, taRange, TextPatternRangeEndpoint_End)`
);
is(await runPython(`range.GetText(-1)`), "cd ef gh", "range text correct");
info("Expanding to character");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
is(await runPython(`range.GetText(-1)`), "c", "range text correct");
info("Moving range start to textarea end");
await runPython(
`range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, taRange, TextPatternRangeEndpoint_End)`
);
is(
await runPython(
`range.CompareEndpoints(TextPatternRangeEndpoint_Start, taRange, TextPatternRangeEndpoint_End)`
),
0,
"range start == textarea end"
);
is(
await runPython(
`range.CompareEndpoints(TextPatternRangeEndpoint_End, taRange, TextPatternRangeEndpoint_End)`
),
0,
"range end == textarea end"
);
info("Moving range end to textarea start");
await runPython(
`range.MoveEndpointByRange(TextPatternRangeEndpoint_End, taRange, TextPatternRangeEndpoint_Start)`
);
is(
await runPython(
`range.CompareEndpoints(TextPatternRangeEndpoint_Start, taRange, TextPatternRangeEndpoint_Start)`
),
0,
"range start == textarea start"
);
is(
await runPython(
`range.CompareEndpoints(TextPatternRangeEndpoint_End, taRange, TextPatternRangeEndpoint_Start)`
),
0,
"range end == textarea start"
);
await definePyVar("docRange", `getUiaPattern(doc, "Text").DocumentRange`);
info("Moving range start to document start");
await runPython(
`range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, docRange, TextPatternRangeEndpoint_Start)`
);
info("Moving range end to document end");
await runPython(
`range.MoveEndpointByRange(TextPatternRangeEndpoint_End, docRange, TextPatternRangeEndpoint_End)`
);
is(
await runPython(
`range.CompareEndpoints(TextPatternRangeEndpoint_Start, docRange, TextPatternRangeEndpoint_Start)`
),
0,
"range start == document start"
);
is(
await runPython(
`range.CompareEndpoints(TextPatternRangeEndpoint_End, docRange, TextPatternRangeEndpoint_End)`
),
0,
"range end == document end"
);
}
);
/**
* Test the TextRange pattern's MoveEndpointByUnit method.
*/
addUiaTask(
`
<p>ab</p>
<textarea id="textarea">cd ef gh</textarea>
<p>ij</p>
`,
async function testTextRangeMoveEndpointByUnit() {
await runPython(`
doc = getDocUia()
textarea = findUiaByDomId(doc, "textarea")
text = getUiaPattern(textarea, "Text")
global range
range = text.DocumentRange
`);
is(await runPython(`range.GetText(-1)`), "cd ef gh", "range text correct");
info("Moving end -1 word");
is(
await runPython(
`range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Word, -1)`
),
-1,
"MoveEndpointByUnit return correct"
);
is(await runPython(`range.GetText(-1)`), "cd ef ", "range text correct");
info("Moving end -4 words");
// There are only 3 words before.
is(
await runPython(
`range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Word, -4)`
),
-3,
"MoveEndpointByUnit return correct"
);
is(await runPython(`range.GetText(-1)`), "", "range text correct");
info("Moving start 1 word");
is(
await runPython(
`range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Word, 1)`
),
1,
"MoveEndpointByUnit return correct"
);
is(await runPython(`range.GetText(-1)`), "", "range text correct");
info("Moving end 1 character");
is(
await runPython(
`range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, 1)`
),
1,
"MoveEndpointByUnit return correct"
);
is(await runPython(`range.GetText(-1)`), "c", "range text correct");
info("Moving start 5 words");
// There are only 4 word boundaries after.
is(
await runPython(
`range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Word, 5)`
),
4,
"MoveEndpointByUnit return correct"
);
is(await runPython(`range.GetText(-1)`), "", "range text correct");
info("Moving end -1 word");
is(
await runPython(
`range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Word, -1)`
),
-1,
"MoveEndpointByUnit return correct"
);
is(await runPython(`range.GetText(-1)`), "", "range text correct");
info("Moving end 1 character");
is(
await runPython(
`range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, 1)`
),
1,
"MoveEndpointByUnit return correct"
);
is(await runPython(`range.GetText(-1)`), "i", "range text correct");
}
);
/**
* Test the Text pattern's SupportedTextSelection property.
*/
addUiaTask(
`
<style>
body {
user-select: none;
}
</style>
<input id="input">
<p id="p">p</p>
`,
async function testTextSupportedTextSelection() {
let result = await runPython(`
global doc
doc = getDocUia()
input = findUiaByDomId(doc, "input")
text = getUiaPattern(input, "Text")
return text.SupportedTextSelection
`);
is(
result,
SupportedTextSelection_Multiple,
"input SupportedTextSelection correct"
);
if (gIsUiaEnabled) {
// The IA2 -> UIA proxy doesn't expose the Text pattern on this text leaf.
is(
await runPython(`
p = findUiaByDomId(doc, "p")
pLeaf = uiaClient.RawViewWalker.GetFirstChildElement(p)
text = getUiaPattern(pLeaf, "Text")
return text.SupportedTextSelection
`),
SupportedTextSelection_None,
"pLeaf SupportedTextSelection correct"
);
// The IA2 -> UIA proxy doesn't understand that text isn't selectable in
// this document.
is(
await runPython(`getUiaPattern(doc, "Text").SupportedTextSelection`),
SupportedTextSelection_None,
"doc SupportedTextSelection correct"
);
}
}
);
/**
* Test the Text pattern's SupportedTextSelection property on a document with a
* selectable body.
*/
addUiaTask(
`<p id="p">p</p>`,
async function testTextSupportedTextSelectionSelectableBody() {
is(
await runPython(`
global doc
doc = getDocUia()
text = getUiaPattern(doc, "Text")
return text.SupportedTextSelection
`),
SupportedTextSelection_Multiple,
"doc SupportedTextSelection correct"
);
// The IA2 -> UIA proxy doesn't expose the Text pattern on this text leaf.
if (gIsUiaEnabled) {
is(
await runPython(`
p = findUiaByDomId(doc, "p")
pLeaf = uiaClient.RawViewWalker.GetFirstChildElement(p)
text = getUiaPattern(pLeaf, "Text")
return text.SupportedTextSelection
`),
SupportedTextSelection_Multiple,
"pLeaf SupportedTextSelection correct"
);
}
}
);
/**
* Test the Text pattern's GetSelection method with the caret.
*/
addUiaTask(
`<textarea id="textarea" cols="2">ab cd</textarea>`,
async function testTextGetSelectionCaret(browser, docAcc) {
await runPython(`
doc = getDocUia()
textarea = findUiaByDomId(doc, "textarea")
global text
text = getUiaPattern(textarea, "Text")
`);
is(await runPython(`text.GetSelection().Length`), 0, "No selection");
info("Focusing textarea");
const textarea = findAccessibleChildByID(docAcc, "textarea");
let moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
textarea.takeFocus();
await moved;
is(await runPython(`text.GetSelection().Length`), 1, "1 selection");
await definePyVar("range", `text.GetSelection().GetElement(0)`);
ok(await runPython(`bool(range)`), "Got selection range 0");
info("Expanding to character");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
is(await runPython(`range.GetText(-1)`), "a", "range text correct");
info("Pressing ArrowRight");
moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
EventUtils.synthesizeKey("KEY_ArrowRight");
await moved;
is(await runPython(`text.GetSelection().Length`), 1, "1 selection");
await definePyVar("range", `text.GetSelection().GetElement(0)`);
ok(await runPython(`bool(range)`), "Got selection range 0");
info("Expanding to character");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
is(await runPython(`range.GetText(-1)`), "b", "range text correct");
// The IA2 -> UIA proxy doesn't handle the insertion point at the end of a
// line correctly.
if (!gIsUiaEnabled) {
return;
}
// Test the insertion point at the end of a wrapped line.
info("Pressing End");
moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
EventUtils.synthesizeKey("KEY_End");
await moved;
is(await runPython(`text.GetSelection().Length`), 1, "1 selection");
await definePyVar("range", `text.GetSelection().GetElement(0)`);
ok(await runPython(`bool(range)`), "Got selection range 0");
info("Expanding to character");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
is(await runPython(`range.GetText(-1)`), "", "range text correct");
info("Moving end 1 character");
await runPython(
`range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, 1)`
);
is(await runPython(`range.GetText(-1)`), "c", "range text correct");
info("Expanding to line at caret");
await definePyVar("range", `text.GetSelection().GetElement(0)`);
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Line)`);
is(await runPython(`range.GetText(-1)`), "ab ", "range text correct");
// Test the insertion point at the end of the textarea.
info("Pressing Ctrl+End");
moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
EventUtils.synthesizeKey("KEY_End", { ctrlKey: true });
await moved;
is(await runPython(`text.GetSelection().Length`), 1, "1 selection");
await definePyVar("range", `text.GetSelection().GetElement(0)`);
ok(await runPython(`bool(range)`), "Got selection range 0");
info("Expanding to character");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
is(await runPython(`range.GetText(-1)`), "", "range text correct");
info("Expanding to line");
await definePyVar("range", `text.GetSelection().GetElement(0)`);
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Line)`);
is(await runPython(`range.GetText(-1)`), "cd", "range text correct");
}
);
/**
* Test the Text pattern's GetSelection method with selection.
*/
addUiaTask(
`<textarea id="textarea" cols="3">ab cd</textarea>`,
async function testTextGetSelectionSelection(browser, docAcc) {
await runPython(`
doc = getDocUia()
textarea = findUiaByDomId(doc, "textarea")
global text
text = getUiaPattern(textarea, "Text")
`);
is(await runPython(`text.GetSelection().Length`), 0, "No selection");
info("Focusing textarea");
const textarea = findAccessibleChildByID(docAcc, "textarea", [
nsIAccessibleText,
]);
let moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
textarea.takeFocus();
await moved;
is(await runPython(`text.GetSelection().Length`), 1, "1 selection");
await definePyVar("range", `text.GetSelection().GetElement(0)`);
ok(await runPython(`bool(range)`), "Got selection range 0");
is(await runPython(`range.GetText(-1)`), "", "range text correct");
info("Selecting ab");
moved = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, textarea);
textarea.addSelection(0, 2);
await moved;
is(await runPython(`text.GetSelection().Length`), 1, "1 selection");
await definePyVar("range", `text.GetSelection().GetElement(0)`);
ok(await runPython(`bool(range)`), "Got selection range 0");
is(await runPython(`range.GetText(-1)`), "ab", "range text correct");
info("Adding cd to selection");
moved = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, textarea);
textarea.addSelection(3, 5);
await moved;
is(await runPython(`text.GetSelection().Length`), 2, "2 selections");
await definePyVar("range", `text.GetSelection().GetElement(0)`);
ok(await runPython(`bool(range)`), "Got selection range 0");
is(await runPython(`range.GetText(-1)`), "ab", "range text correct");
await definePyVar("range", `text.GetSelection().GetElement(1)`);
ok(await runPython(`bool(range)`), "Got selection range 1");
is(await runPython(`range.GetText(-1)`), "cd", "range text correct");
}
);
/**
* Test the Text pattern's TextSelectionChanged event.
*/
addUiaTask(
`<input id="input" value="abc">`,
async function testTextTextSelectionChanged(browser) {
info("Focusing input");
await setUpWaitForUiaEvent("Text_TextSelectionChanged", "input");
await invokeContentTask(browser, [], () => {
content.document.getElementById("input").focus();
});
await waitForUiaEvent();
ok(true, "input got TextSelectionChanged event");
info("Moving caret to b");
await setUpWaitForUiaEvent("Text_TextSelectionChanged", "input");
await invokeContentTask(browser, [], () => {
content.document.getElementById("input").setSelectionRange(1, 1);
});
await waitForUiaEvent();
ok(true, "input got TextSelectionChanged event");
info("Selecting bc");
await setUpWaitForUiaEvent("Text_TextSelectionChanged", "input");
await invokeContentTask(browser, [], () => {
content.document.getElementById("input").setSelectionRange(1, 3);
});
await waitForUiaEvent();
ok(true, "input got TextSelectionChanged event");
}
);
/**
* Test the Text pattern's TextChanged event.
*/
addUiaTask(
`<input id="input" value="abc">`,
async function testTextTextChanged(browser) {
info("Focusing input");
let moved = waitForEvent(EVENT_TEXT_CARET_MOVED, "input");
await invokeContentTask(browser, [], () => {
content.document.getElementById("input").focus();
});
await moved;
info("Deleting a");
await setUpWaitForUiaEvent("Text_TextChanged", "input");
await invokeContentTask(browser, [], () => {
content.document.execCommand("forwardDelete");
});
await waitForUiaEvent();
ok(true, "input got TextChanged event");
info("Inserting a");
await setUpWaitForUiaEvent("Text_TextChanged", "input");
await invokeContentTask(browser, [], () => {
content.document.execCommand("insertText", false, "a");
});
await waitForUiaEvent();
ok(true, "input got TextChanged event");
}
);
/**
* Test the TextRange pattern's GetEnclosingElement method.
*/
addUiaTask(
`
<div id="editable" contenteditable role="textbox">
ab
<mark id="cdef"><span>cd</span> <a id="ef" href="/">ef</a></mark>
<a href="/"><img id="g" src="https://example.com/a11y/accessible/tests/mochitest/moz.png" alt="g"></a>
<p><button id="h">h</button></p>
</div>
`,
async function testTextRangeGetEnclosingElement() {
info("Getting editable DocumentRange");
await runPython(`
doc = getDocUia()
editable = findUiaByDomId(doc, "editable")
text = getUiaPattern(editable, "Text")
global range
range = text.DocumentRange
`);
is(
await runPython(`range.GetEnclosingElement().CurrentAutomationId`),
"editable",
"EnclosingElement is editable"
);
info("Expanding to word");
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Word)`);
// Range is now "ab ".
// The IA2 -> UIA proxy gets this wrong.
if (gIsUiaEnabled) {
is(
await runPython(`range.GetEnclosingElement().CurrentName`),
"ab ",
"EnclosingElement is ab text leaf"
);
}
info("Moving 1 word");
await runPython(`range.Move(TextUnit_Word, 1)`);
// Range is now "cd ".
// The "cd" text leaf doesn't include the space, so the enclosing element is
// its parent.
is(
await runPython(`range.GetEnclosingElement().CurrentAutomationId`),
"cdef",
"EnclosingElement is cdef"
);
info("Moving end -1 character");
await runPython(
`range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, -1)`
);
// Range is now "cd".
// The IA2 -> UIA proxy gets this wrong.
if (gIsUiaEnabled) {
is(
await runPython(`range.GetEnclosingElement().CurrentName`),
"cd",
"EnclosingElement is cd text leaf"
);
}
info("Moving 1 word");
await runPython(`range.Move(TextUnit_Word, 1)`);
// Range is now "ef ".
// Neither the "ef" text leaf/link nor the "cdef" mark include the trailing
// space, so the enclosing element is cdef's parent.
is(
await runPython(`range.GetEnclosingElement().CurrentAutomationId`),
"editable",
"EnclosingElement is editable"
);
info("Moving end -1 character");
await runPython(
`range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, -1)`
);
// Range is now "ef". The innermost element is the text leaf, but "ef" is a
// link and that's what Narrator wants.
is(
await runPython(`range.GetEnclosingElement().CurrentAutomationId`),
"ef",
"EnclosingElement is ef"
);
// The IA2 -> UIA proxy gets the rest of this wrong.
if (!gIsUiaEnabled) {
return;
}
info("Moving 1 word");
await runPython(`range.Move(TextUnit_Word, 1)`);
// Range is now the embedded object character for the img (g).
is(
await runPython(`range.GetEnclosingElement().CurrentAutomationId`),
"g",
"EnclosingElement is g"
);
info("Moving 1 word");
await runPython(`range.Move(TextUnit_Word, 1)`);
// Range is now "h". "h" is a button and buttons prune their children, so
// UIA doesn't see the text leaf.
is(
await runPython(`range.GetEnclosingElement().CurrentAutomationId`),
"h",
"EnclosingElement is h"
);
}
);
/**
* Test the TextRange pattern's GetChildren method.
*/
addUiaTask(
`<div id="editable" contenteditable role="textbox">ab <span id="cdef" role="button"><span>cd</span> <a id="ef" href="/">ef</a> </span><img id="g" src="https://example.com/a11y/accessible/tests/mochitest/moz.png" alt="g"></div>`,
async function testTextRangeGetChildren() {
info("Getting editable DocumentRange");
await runPython(`
doc = getDocUia()
editable = findUiaByDomId(doc, "editable")
text = getUiaPattern(editable, "Text")
global r
r = text.DocumentRange
`);
await isUiaElementArray(
`r.GetChildren()`,
["cdef", "g"],
"Children are correct"
);
info("Expanding to word");
await runPython(`r.ExpandToEnclosingUnit(TextUnit_Word)`);
// Range is now "ab ".
await isUiaElementArray(`r.GetChildren()`, [], "Children are correct");
info("Moving 1 word");
await runPython(`r.Move(TextUnit_Word, 1)`);
// Range is now "cd ".
await isUiaElementArray(`r.GetChildren()`, [], "Children are correct");
info("Moving 1 word");
await runPython(`r.Move(TextUnit_Word, 1)`);
// Range is now "ef ". The range includes the link but is not completely
// enclosed by the link.
await isUiaElementArray(`r.GetChildren()`, ["ef"], "Children are correct");
info("Moving end -1 character");
await runPython(
`r.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, -1)`
);
// Range is now "ef". The range encloses the link, so there are no children.
await isUiaElementArray(`r.GetChildren()`, [], "Children are correct");
info("Moving 1 word");
await runPython(`r.Move(TextUnit_Word, 1)`);
// Range is now the embedded object character for the img (g). The range is
// completely enclosed by the image.
// The IA2 -> UIA proxy gets this wrong.
if (gIsUiaEnabled) {
await isUiaElementArray(`r.GetChildren()`, [], "Children are correct");
}
}
);
/**
* Test the Text pattern's RangeFromChild method.
*/
addUiaTask(
`<div id="editable" contenteditable role="textbox">ab <mark id="cdef"><span>cd</span> <a id="ef" href="/">ef</a></mark> <img id="g" src="https://example.com/a11y/accessible/tests/mochitest/moz.png" alt="g"></div>`,
async function testTextRangeFromChild() {
await runPython(`
global doc, docText, editable, edText
doc = getDocUia()
docText = getUiaPattern(doc, "Text")
editable = findUiaByDomId(doc, "editable")
edText = getUiaPattern(editable, "Text")
`);
is(
await runPython(`docText.RangeFromChild(editable).GetText(-1)`),
`ab cd ef ${kEmbedChar}`,
"doc returned correct range for editable"
);
await testPythonRaises(
`edText.RangeFromChild(editable)`,
"editable correctly failed to return range for editable"
);
is(
await runPython(`docText.RangeFromChild(editable).GetText(-1)`),
`ab cd ef ${kEmbedChar}`,
"doc returned correct range for editable"
);
let text = await runPython(`
ab = uiaClient.RawViewWalker.GetFirstChildElement(editable)
range = docText.RangeFromChild(ab)
return range.GetText(-1)
`);
is(text, "ab ", "doc returned correct range for ab");
text = await runPython(`
global cdef
cdef = findUiaByDomId(doc, "cdef")
range = docText.RangeFromChild(cdef)
return range.GetText(-1)
`);
is(text, "cd ef", "doc returned correct range for cdef");
text = await runPython(`
cd = uiaClient.RawViewWalker.GetFirstChildElement(cdef)
range = docText.RangeFromChild(cd)
return range.GetText(-1)
`);
is(text, "cd", "doc returned correct range for cd");
text = await runPython(`
global efLink
efLink = findUiaByDomId(doc, "ef")
range = docText.RangeFromChild(efLink)
return range.GetText(-1)
`);
is(text, "ef", "doc returned correct range for ef link");
text = await runPython(`
efLeaf = uiaClient.RawViewWalker.GetFirstChildElement(efLink)
range = docText.RangeFromChild(efLeaf)
return range.GetText(-1)
`);
is(text, "ef", "doc returned correct range for ef leaf");
text = await runPython(`
g = findUiaByDomId(doc, "g")
range = docText.RangeFromChild(g)
return range.GetText(-1)
`);
is(text, kEmbedChar, "doc returned correct range for g");
},
// The IA2 -> UIA proxy has too many quirks/bugs here.
{ uiaEnabled: true, uiaDisabled: false }
);
/**
* Test the Text pattern's RangeFromPoint method.
*/
addUiaTask(
`<div id="test">a <span>b </span>c</div>`,
async function testTextRangeFromPoint(browser, docAcc) {
const acc = findAccessibleChildByID(docAcc, "test", [nsIAccessibleText]);
await runPython(`
global doc, docText
doc = getDocUia()
docText = getUiaPattern(doc, "Text")
`);
// Walk through every offset in the accessible and hit test each. Verify
// that the returned range is empty, and that it hit the right character.
for (let offset = 0; offset < acc.characterCount; ++offset) {
const x = {};
const y = {};
acc.getCharacterExtents(offset, x, y, {}, {}, COORDTYPE_SCREEN_RELATIVE);
await runPython(`
global range
range = docText.RangeFromPoint(POINT(${x.value}, ${y.value}))`);
is(
await runPython(`range.GetText(-1)`),
``,
"doc returned correct empty range"
);
await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
const charAtOffset = acc.getCharacterAtOffset(offset);
is(
await runPython(`range.GetText(-1)`),
`${charAtOffset}`,
"doc returned correct range"
);
}
// An arbitrary invalid point should cause an invalid argument error.
await testPythonRaises(
`docText.RangeFromPoint(POINT(9999999999, 9999999999))`,
"no text leaves at invalid point"
);
},
{ uiaEnabled: true, uiaDisabled: true }
);
/**
* Test the TextRange pattern's GetBoundingRectangles method.
*/
addUiaTask(
`
<div id="test"><p id="line1">abc</p><p id="line2">d</p><p id="line3"></p></div>
<div id="offscreen" style="position:absolute; left:200vw;">xyz</div>
`,
async function testTextRangeGetBoundingRectangles(browser, docAcc) {
const line1 = findAccessibleChildByID(docAcc, "line1", [nsIAccessibleText]);
const line2 = findAccessibleChildByID(docAcc, "line2", [nsIAccessibleText]);
const lineRects = await runPython(`
global doc, docText, testAcc, range
doc = getDocUia()
docText = getUiaPattern(doc, "Text")
testAcc = findUiaByDomId(doc, "test")
range = docText.RangeFromChild(testAcc)
return range.GetBoundingRectangles()
`);
is(lineRects.length, 8, "GetBoundingRectangles returned two rectangles");
const firstLineRect = [
lineRects[0],
lineRects[1],
lineRects[2],
lineRects[3],
];
const secondLineRect = [
lineRects[4],
lineRects[5],
lineRects[6],
lineRects[7],
];
testTextBounds(line1, 0, -1, firstLineRect, COORDTYPE_SCREEN_RELATIVE);
testTextBounds(line2, 0, -1, secondLineRect, COORDTYPE_SCREEN_RELATIVE);
// line3 has no rectangle - GetBoundingRectangles shouldn't return anything for empty lines.
// GetBoundingRectangles shouldn't return anything for offscreen lines.
const offscreenRects = await runPython(`
global offscreenAcc, range
offscreenAcc = findUiaByDomId(doc, "offscreen")
range = docText.RangeFromChild(offscreenAcc)
return range.GetBoundingRectangles()
`);
is(
offscreenRects.length,
0,
"GetBoundingRectangles returned no rectangles"
);
},
{ uiaEnabled: true, uiaDisabled: true, chrome: true }
);
/**
* Test the TextRange pattern's ScrollIntoView method.
*/
addUiaTask(
`
<style>
body {
margin: 0;
}
</style>
<p>p1</p>
<hr style="height: 200vh;">
<p id="p2">p2</p>
<hr style="height: 200vh;">
<p>p3</p>
`,
async function testTextRangeScrollIntoView(browser, docAcc) {
const [docLeft, docTop, , docBottom] = await runPython(`
global doc
doc = getDocUia()
rect = doc.CurrentBoundingRectangle
return (rect.left, rect.top, rect.right, rect.bottom)
`);
info("Scrolling p2 to top");
let scrolled = waitForEvent(EVENT_SCROLLING_END, docAcc);
await runPython(`
global docText, p2, range
docText = getUiaPattern(doc, "Text")
p2 = findUiaByDomId(doc, "p2")
range = docText.RangeFromChild(p2)
range.ScrollIntoView(True)
`);
await scrolled;
let [left, top, , height] = await runPython(
`range.GetBoundingRectangles()`
);
is(left, docLeft, "range is at left of document");
is(top, docTop, "range is at top of document");
info("Scrolling p2 to bottom");
scrolled = waitForEvent(EVENT_SCROLLING_END, docAcc);
await runPython(`
range.ScrollIntoView(False)
`);
await scrolled;
[left, top, , height] = await runPython(`range.GetBoundingRectangles()`);
is(left, docLeft, "range is at left of document");
is(top + height, docBottom, "range is at bottom of document");
}
);
/**
* Test the TextRange pattern's Select method.
*/
addUiaTask(
`
<input id="input" type="text" value="ab">
<div id="contenteditable" contenteditable role="textbox">ab</div>
`,
async function testTextRangeSelect(browser, docAcc) {
// <input> and contentEditable should behave the same.
for (const id of ["input", "contenteditable"]) {
info(`Focusing ${id}`);
const acc = findAccessibleChildByID(docAcc, id, [nsIAccessibleText]);
let moved = waitForEvents([
[EVENT_FOCUS, acc],
[EVENT_TEXT_CARET_MOVED, acc],
]);
acc.takeFocus();
await moved;
info("Selecting a");
moved = waitForEvents([
[EVENT_TEXT_SELECTION_CHANGED, acc],
[EVENT_TEXT_CARET_MOVED, acc],
]);
await runPython(`
doc = getDocUia()
acc = findUiaByDomId(doc, "${id}")
text = getUiaPattern(acc, "Text")
global range
range = text.DocumentRange
range.ExpandToEnclosingUnit(TextUnit_Character)
range.Select()
`);
await moved;
testTextSelectionCount(acc, 1);
testTextGetSelection(acc, 0, 1, 0);
info("Moving caret to b");
moved = waitForEvent(EVENT_TEXT_CARET_MOVED, acc);
await runPython(`
# Collapse to b.
range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Character, 1)
range.Select()
`);
await moved;
testTextSelectionCount(acc, 0);
is(acc.caretOffset, 1, "caret at 1");
}
}
);
/**
* Test the TextRange pattern's AddToSelection method.
*/
addUiaTask(
`
<input id="input" type="text" value="abc">
<div id="contenteditable" contenteditable role="textbox">abc</div>
`,
async function testTextRangeAddToSelection(browser, docAcc) {
// <input> and contentEditable should behave the same.
for (const id of ["input", "contenteditable"]) {
info(`Focusing ${id}`);
const acc = findAccessibleChildByID(docAcc, id, [nsIAccessibleText]);
let moved = waitForEvents([
[EVENT_FOCUS, acc],
[EVENT_TEXT_CARET_MOVED, acc],
]);
acc.takeFocus();
await moved;
info("Adding a to selection");
moved = waitForEvents([
[EVENT_TEXT_SELECTION_CHANGED, acc],
[EVENT_TEXT_CARET_MOVED, acc],
]);
await runPython(`
doc = getDocUia()
acc = findUiaByDomId(doc, "${id}")
text = getUiaPattern(acc, "Text")
global range
range = text.DocumentRange
range.ExpandToEnclosingUnit(TextUnit_Character)
range.AddToSelection()
`);
await moved;
testTextSelectionCount(acc, 1);
testTextGetSelection(acc, 0, 1, 0);
info("Adding c to selection");
moved = waitForEvent(EVENT_TEXT_CARET_MOVED, acc);
await runPython(`
# Move start to c.
range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Character, 2)
range.ExpandToEnclosingUnit(TextUnit_Character)
range.AddToSelection()
`);
await moved;
testTextSelectionCount(acc, 2);
testTextGetSelection(acc, 0, 1, 0);
testTextGetSelection(acc, 2, 3, 1);
}
}
);
/**
* Test the TextRange pattern's RemoveFromSelection method.
*/
addUiaTask(
`
<input id="input" type="text" value="abc">
<div id="contenteditable" contenteditable role="textbox">abc</div>
`,
async function testTextRangeRemoveFromSelection(browser, docAcc) {
// <input> and contentEditable should behave the same.
for (const id of ["input", "contenteditable"]) {
info(`Focusing ${id}`);
const acc = findAccessibleChildByID(docAcc, id, [nsIAccessibleText]);
let moved = waitForEvents([
[EVENT_FOCUS, acc],
[EVENT_TEXT_CARET_MOVED, acc],
]);
acc.takeFocus();
await moved;
info("Adding a to selection");
moved = waitForEvents([
[EVENT_TEXT_SELECTION_CHANGED, acc],
[EVENT_TEXT_CARET_MOVED, acc],
]);
acc.addSelection(0, 1);
await moved;
info("Adding c to selection");
moved = waitForEvents([
[EVENT_TEXT_SELECTION_CHANGED, acc],
[EVENT_TEXT_CARET_MOVED, acc],
]);
acc.addSelection(2, 3);
await moved;
info("Removing a from selection");
moved = waitForEvents([
[EVENT_TEXT_SELECTION_CHANGED, acc],
[EVENT_TEXT_CARET_MOVED, acc],
]);
await runPython(`
doc = getDocUia()
acc = findUiaByDomId(doc, "${id}")
text = getUiaPattern(acc, "Text")
global range
range = text.DocumentRange
range.ExpandToEnclosingUnit(TextUnit_Character)
range.RemoveFromSelection()
`);
await moved;
testTextSelectionCount(acc, 1);
testTextGetSelection(acc, 2, 3, 0);
info("Removing b from selection even though it isn't selected");
await runPython(`
# Move start to b.
range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Character, 1)
range.ExpandToEnclosingUnit(TextUnit_Character)
`);
await testPythonRaises(
`range.RemoveFromSelection()`,
"RemoveFromSelection failed"
);
info("Removing c from selection");
moved = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, acc);
await runPython(`
# Move start to c.
range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Character, 1)
range.ExpandToEnclosingUnit(TextUnit_Character)
range.RemoveFromSelection()
`);
await moved;
testTextSelectionCount(acc, 0);
}
},
// The IA2 -> UIA proxy doesn't support RemoveFromSelection correctly.
{ uiaEnabled: true, uiaDisabled: false }
);
/**
* Test the TextRange pattern's FindAttribute method.
*/
addUiaTask(
`
<div id="font-weight-container">a <span tabindex="0"><b>bcd</b></span><b> ef</b> ghi</div>
`,
async function testTextRangeFindAttribute(_browser, _docAcc) {
info("Constructing range on bold text run");
await runPython(`
global doc, docText, range, fontWeightContainerAcc
doc = getDocUia()
docText = getUiaPattern(doc, "Text")
fontWeightContainerAcc = findUiaByDomId(doc, "font-weight-container")
range = docText.RangeFromChild(fontWeightContainerAcc)
`);
is(
await runPython(`range.GetText(-1)`),
"a bcd ef ghi",
"range text correct"
);
info("Finding first font-weight 400 text range");
await runPython(`
global subrange
subrange = range.FindAttribute(UIA_FontWeightAttributeId, 400, False)
`);
is(await runPython(`subrange.GetText(-1)`), "a ", "range text correct");
info("Finding first font-weight 700 text range");
await runPython(`
global subrange
subrange = range.FindAttribute(UIA_FontWeightAttributeId, 700, False)
`);
is(await runPython(`subrange.GetText(-1)`), "bcd ef", "range text correct");
info("Finding last font-weight 700 text range");
await runPython(`
global subrange
subrange = range.FindAttribute(UIA_FontWeightAttributeId, 700, True)
`);
is(await runPython(`subrange.GetText(-1)`), "bcd ef", "range text correct");
info("Finding last font-weight 400 text range");
await runPython(`
global subrange
subrange = range.FindAttribute(UIA_FontWeightAttributeId, 400, True)
`);
is(await runPython(`subrange.GetText(-1)`), " ghi", "range text correct");
// The IA2 -> UIA proxy gets things below this wrong.
if (!gIsUiaEnabled) {
return;
}
info("Moving range to the middle of a text attribute run");
is(
await runPython(
`range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Character, 4)`
),
4,
"MoveEndpointByUnit return correct"
);
is(await runPython(`range.GetText(-1)`), "cd ef ghi", "range text correct");
info(
"Finding first font-weight 700 text range (range starts in middle of text attribute run)"
);
await runPython(`
global subrange
subrange = range.FindAttribute(UIA_FontWeightAttributeId, 700, False)
`);
is(await runPython(`subrange.GetText(-1)`), "cd ef", "range text correct");
await runPython(`
global range
range = docText.RangeFromChild(fontWeightContainerAcc)
`);
is(
await runPython(`range.GetText(-1)`),
"a bcd ef ghi",
"range text correct"
);
is(
await runPython(
`range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, -5)`
),
-5,
"MoveEndpointByUnit return correct"
);
is(await runPython(`range.GetText(-1)`), "a bcd e", "range text correct");
info(
"Finding last font-weight 700 text range (range ends in middle of text attribute run)"
);
await runPython(`
global subrange
subrange = range.FindAttribute(UIA_FontWeightAttributeId, 700, True)
`);
is(await runPython(`subrange.GetText(-1)`), "bcd e", "range text correct");
info("Collapsing range at start");
await runPython(`
global subrange
subrange = range.Clone()
subrange.MoveEndpointByRange(TextPatternRangeEndpoint_End, subrange, TextPatternRangeEndpoint_Start)
`);
is(await runPython(`subrange.GetText(-1)`), "", "subrange text correct");
info("Finding last font-weight 400 text range on collapsed range");
await runPython(`
global subrange
subrange = subrange.FindAttribute(UIA_FontWeightAttributeId, 400, True)
`);
is(await runPython(`subrange.GetText(-1)`), "", "subrange text correct");
},
{ uiaEnabled: true, uiaDisabled: true }
);
/**
* Test the Text pattern's GetVisibleRanges method.
*/
addUiaTask(
`
<div id="div">
<p>line1</p>
<p>line2</p>
<p style="position: absolute; left: -10000px; width: 1px;">line3</p>
<p>line4</p>
</div>
<!-- We use 0.5lh so the second line is definitely fully scrolled out.
With 1lh, it could be partially visible and thus included. -->
<textarea id="textarea" style="height: 0.5lh;">line5
line6
line7</textarea>
<hr aria-hidden="true" style="height: 100vh;">
<p>line8</p>
`,
async function testTextGetVisibleRanges() {
await runPython(`
global doc, docText, ranges
doc = getDocUia()
docText = getUiaPattern(doc, "Text")
ranges = docText.GetVisibleRanges()
`);
// XXX This should be 4 once we fix the scrolling case below.
is(
await runPython(`ranges.Length`),
6,
"doc has correct number of visible ranges"
);
is(
await runPython(`ranges.GetElement(0).GetText(-1)`),
"line1",
"range 0 text correct"
);
is(
await runPython(`ranges.GetElement(1).GetText(-1)`),
"line2",
"range 1 text correct"
);
// line3 is off-screen and thus not visible.
is(
await runPython(`ranges.GetElement(2).GetText(-1)`),
"line4",
"range 2 text correct"
);
is(
await runPython(`ranges.GetElement(3).GetText(-1)`),
"line5\n",
"range 3 text correct"
);
// XXX line6 and line7 are scrolled off screen by the textarea, but we
// incorrectly return them for now (ranges 4 and 5).
// line8 is scrolled off screen by the document.
await runPython(`
textarea = findUiaByDomId(doc, "textarea")
textareaText = getUiaPattern(textarea, "Text")
global ranges
ranges = textareaText.GetVisibleRanges()
`);
is(
await runPython(`ranges.Length`),
1,
"textarea has correct number of visible ranges"
);
is(
await runPython(`ranges.GetElement(0).GetText(-1)`),
"line5\n",
"range 0 text correct"
);
// line6 and line7 are scrolled off screen by the textarea.
},
// The IA2 -> UIA proxy doesn't support GetVisibleRanges.
{ uiaEnabled: true, uiaDisabled: false }
);
/**
* Test the TextRange pattern's FindText method.
*/
addUiaTask(
`<div id="container"><b>abc</b>TEST<div id="inner">def</div>TEST<p>ghi</p></div>`,
async function testTextRangeFromChild() {
await runPython(`
global doc, docText, container, range
doc = getDocUia()
docText = getUiaPattern(doc, "Text")
container = findUiaByDomId(doc, "container")
range = docText.RangeFromChild(container)
`);
// The IA2 -> UIA bridge inserts a space at the end of the text.
if (gIsUiaEnabled) {
is(
await runPython(`range.GetText(-1)`),
`abcTESTdefTESTghi`,
"doc returned correct range for container"
);
}
info("Finding 'abc', searching from the start");
await runPython(`
global subrange
subrange = range.FindText("abc", False, False)
`);
is(await runPython(`subrange.GetText(-1)`), "abc", "range text correct");
info("Finding 'abc', searching from the end");
await runPython(`
global subrange
subrange = range.FindText("abc", True, False)
`);
is(await runPython(`subrange.GetText(-1)`), "abc", "range text correct");
info("Finding 'ghi', searching from the start");
await runPython(`
global subrange
subrange = range.FindText("ghi", False, False)
`);
is(await runPython(`subrange.GetText(-1)`), "ghi", "range text correct");
info("Finding 'ghi', searching from the end");
await runPython(`
global subrange
subrange = range.FindText("ghi", True, False)
`);
is(await runPython(`subrange.GetText(-1)`), "ghi", "range text correct");
info("Finding 'TEST', searching from the start");
await runPython(`
global subrange
subrange = range.FindText("TEST", False, False)
`);
is(await runPython(`subrange.GetText(-1)`), "TEST", "range text correct");
info("Finding 'TEST', searching from the end");
await runPython(`
global subrange2
subrange2 = range.FindText("TEST", True, False)
`);
is(await runPython(`subrange2.GetText(-1)`), "TEST", "range text correct");
ok(
!(await runPython(`subrange.compare(subrange2)`)),
"ranges are not equal"
);
info("Finding 'test', searching from the start, case-sensitive");
await runPython(`
global subrange
subrange = range.FindText("test", False, False)
`);
ok(await runPython(`not subrange`), "range not found");
info("Finding 'test', searching from the start, case-insensitive");
await runPython(`
global subrange
subrange = range.FindText("test", False, True)
`);
is(await runPython(`subrange.GetText(-1)`), "TEST", "range text correct");
},
{ uiaEnabled: true, uiaDisabled: true }
);
const textChildSnippet = `
<p id="p">p</p>
<a id="a" href="/">a</a>
<img id="img" src="https://example.com/a11y/accessible/tests/mochitest/moz.png" alt="img">
<div id="textbox" contenteditable role="textbox">textboxLeaf
<p id="textboxP">textboxP</p>
</div>
`;
/**
* Test the TextChild pattern's TextContainer property.
*/
addUiaTask(textChildSnippet, async function testTextChildTextContainer() {
ok(
await runPython(`
global doc, p
doc = getDocUia()
p = findUiaByDomId(doc, "p")
tc = getUiaPattern(p, "TextChild")
return uiaClient.CompareElements(tc.TextContainer, doc)
`),
"p TextContainer is doc"
);
// The IA2 -> UIA proxy doesn't support the TextChild pattern on text
// leaves.
if (gIsUiaEnabled) {
ok(
await runPython(`
pLeaf = uiaClient.RawViewWalker.GetFirstChildElement(p)
tc = getUiaPattern(pLeaf, "TextChild")
return uiaClient.CompareElements(tc.TextContainer, doc)
`),
"p leaf TextContainer is doc"
);
}
ok(
await runPython(`
a = findUiaByDomId(doc, "a")
tc = getUiaPattern(a, "TextChild")
return uiaClient.CompareElements(tc.TextContainer, doc)
`),
"a TextContainer is doc"
);
ok(
await runPython(`
img = findUiaByDomId(doc, "img")
tc = getUiaPattern(img, "TextChild")
return uiaClient.CompareElements(tc.TextContainer, doc)
`),
"img TextContainer is doc"
);
ok(
await runPython(`
global textbox
textbox = findUiaByDomId(doc, "textbox")
tc = getUiaPattern(textbox, "TextChild")
return uiaClient.CompareElements(tc.TextContainer, doc)
`),
"textbox TextContainer is doc"
);
// The IA2 -> UIA proxy doesn't support the TextChild pattern on text
// leaves.
if (gIsUiaEnabled) {
ok(
await runPython(`
textboxLeaf = uiaClient.RawViewWalker.GetFirstChildElement(textbox)
tc = getUiaPattern(textboxLeaf, "TextChild")
return uiaClient.CompareElements(tc.TextContainer, textbox)
`),
"textbox leaf TextContainer is textbox"
);
}
ok(
await runPython(`
textboxP = findUiaByDomId(doc, "textboxP")
tc = getUiaPattern(textboxP, "TextChild")
return uiaClient.CompareElements(tc.TextContainer, textbox)
`),
"textboxP TextContainer is textbox"
);
});
/**
* Test the TextChild pattern's TextRange property.
*/
addUiaTask(textChildSnippet, async function testTextChildTextRange() {
is(
await runPython(`
global doc, p
doc = getDocUia()
p = findUiaByDomId(doc, "p")
tc = getUiaPattern(p, "TextChild")
return tc.TextRange.GetText(-1)
`),
"p",
"p text correct"
);
// The IA2 -> UIA proxy doesn't support the TextChild pattern on text
// leaves.
if (gIsUiaEnabled) {
is(
await runPython(`
pLeaf = uiaClient.RawViewWalker.GetFirstChildElement(p)
tc = getUiaPattern(pLeaf, "TextChild")
return tc.TextRange.GetText(-1)
`),
"p",
"p leaf text correct"
);
}
is(
await runPython(`
a = findUiaByDomId(doc, "a")
tc = getUiaPattern(a, "TextChild")
return tc.TextRange.GetText(-1)
`),
"a",
"a text correct"
);
if (gIsUiaEnabled) {
// The IA2 -> UIA proxy doesn't expose an embedded object character for
// images.
is(
await runPython(`
img = findUiaByDomId(doc, "img")
tc = getUiaPattern(img, "TextChild")
return tc.TextRange.GetText(-1)
`),
kEmbedChar,
"img text correct"
);
// The IA2 -> UIA proxy adds spaces between elements that don't exist.
is(
await runPython(`
global textbox
textbox = findUiaByDomId(doc, "textbox")
tc = getUiaPattern(textbox, "TextChild")
return tc.TextRange.GetText(-1)
`),
"textboxLeaf textboxP",
"textbox text correct"
);
// The IA2 -> UIA proxy doesn't support the TextChild pattern on text
// leaves.
is(
await runPython(`
textboxLeaf = uiaClient.RawViewWalker.GetFirstChildElement(textbox)
tc = getUiaPattern(textboxLeaf, "TextChild")
return tc.TextRange.GetText(-1)
`),
"textboxLeaf ",
"textbox leaf text correct"
);
}
is(
await runPython(`
textboxP = findUiaByDomId(doc, "textboxP")
tc = getUiaPattern(textboxP, "TextChild")
return tc.TextRange.GetText(-1)
`),
"textboxP",
"textboxP text correct"
);
});