diff options
Diffstat (limited to 'accessible/tests/browser/text/browser_text.js')
-rw-r--r-- | accessible/tests/browser/text/browser_text.js | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/accessible/tests/browser/text/browser_text.js b/accessible/tests/browser/text/browser_text.js new file mode 100644 index 0000000000..79909ee412 --- /dev/null +++ b/accessible/tests/browser/text/browser_text.js @@ -0,0 +1,326 @@ +/* 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/attributes.js */ +/* import-globals-from ../../mochitest/text.js */ +loadScripts({ name: "attributes.js", dir: MOCHITESTS_DIR }); + +/** + * Test line and word offsets for various cases for both local and remote + * Accessibles. There is more extensive coverage in ../../mochitest/text. These + * tests don't need to duplicate all of that, since much of the underlying code + * is unified. They should ensure that the cache works as expected and that + * there is consistency between local and remote. + */ +addAccessibleTask( + ` +<p id="br">ab cd<br>ef gh</p> +<pre id="pre">ab cd +ef gh</pre> +<p id="linksStartEnd"><a href="https://example.com/">a</a>b<a href="https://example.com/">c</a></p> +<p id="linksBreaking">a<a href="https://example.com/">b<br>c</a>d</p> +<p id="p">a<br role="presentation">b</p> +<p id="leafThenWrap" style="font-family: monospace; width: 2ch; word-break: break-word;"><span>a</span>bc</p> + `, + async function (browser, docAcc) { + for (const id of ["br", "pre"]) { + const acc = findAccessibleChildByID(docAcc, id); + testCharacterCount([acc], 11); + testTextAtOffset(acc, BOUNDARY_LINE_START, [ + [0, 5, "ab cd\n", 0, 6], + [6, 11, "ef gh", 6, 11], + ]); + testTextBeforeOffset(acc, BOUNDARY_LINE_START, [ + [0, 5, "", 0, 0], + [6, 11, "ab cd\n", 0, 6], + ]); + testTextAfterOffset(acc, BOUNDARY_LINE_START, [ + [0, 5, "ef gh", 6, 11], + [6, 11, "", 11, 11], + ]); + testTextAtOffset(acc, BOUNDARY_LINE_END, [ + [0, 5, "ab cd", 0, 5], + [6, 11, "\nef gh", 5, 11], + ]); + testTextBeforeOffset(acc, BOUNDARY_LINE_END, [ + [0, 5, "", 0, 0], + [6, 11, "ab cd", 0, 5], + ]); + testTextAfterOffset(acc, BOUNDARY_LINE_END, [ + [0, 5, "\nef gh", 5, 11], + [6, 11, "", 11, 11], + ]); + testTextAtOffset(acc, BOUNDARY_WORD_START, [ + [0, 2, "ab ", 0, 3], + [3, 5, "cd\n", 3, 6], + [6, 8, "ef ", 6, 9], + [9, 11, "gh", 9, 11], + ]); + testTextBeforeOffset(acc, BOUNDARY_WORD_START, [ + [0, 2, "", 0, 0], + [3, 5, "ab ", 0, 3], + [6, 8, "cd\n", 3, 6], + [9, 11, "ef ", 6, 9], + ]); + testTextAfterOffset(acc, BOUNDARY_WORD_START, [ + [0, 2, "cd\n", 3, 6], + [3, 5, "ef ", 6, 9], + [6, 8, "gh", 9, 11], + [9, 11, "", 11, 11], + ]); + testTextAtOffset(acc, BOUNDARY_WORD_END, [ + [0, 1, "ab", 0, 2], + [2, 4, " cd", 2, 5], + [5, 7, "\nef", 5, 8], + [8, 11, " gh", 8, 11], + ]); + testTextBeforeOffset(acc, BOUNDARY_WORD_END, [ + [0, 2, "", 0, 0], + [3, 5, "ab", 0, 2], + // See below for offset 6. + [7, 8, " cd", 2, 5], + [9, 11, "\nef", 5, 8], + ]); + testTextBeforeOffset(acc, BOUNDARY_WORD_END, [[6, 6, " cd", 2, 5]]); + testTextAfterOffset(acc, BOUNDARY_WORD_END, [ + [0, 2, " cd", 2, 5], + [3, 5, "\nef", 5, 8], + [6, 8, " gh", 8, 11], + [9, 11, "", 11, 11], + ]); + testTextAtOffset(acc, BOUNDARY_PARAGRAPH, [ + [0, 5, "ab cd\n", 0, 6], + [6, 11, "ef gh", 6, 11], + ]); + } + const linksStartEnd = findAccessibleChildByID(docAcc, "linksStartEnd"); + testTextAtOffset(linksStartEnd, BOUNDARY_LINE_START, [ + [0, 3, `${kEmbedChar}b${kEmbedChar}`, 0, 3], + ]); + testTextAtOffset(linksStartEnd, BOUNDARY_WORD_START, [ + [0, 3, `${kEmbedChar}b${kEmbedChar}`, 0, 3], + ]); + const linksBreaking = findAccessibleChildByID(docAcc, "linksBreaking"); + testTextAtOffset(linksBreaking, BOUNDARY_LINE_START, [ + [0, 0, `a${kEmbedChar}`, 0, 2], + [1, 1, `a${kEmbedChar}d`, 0, 3], + [2, 3, `${kEmbedChar}d`, 1, 3], + ]); + const p = findAccessibleChildByID(docAcc, "p"); + testTextAtOffset(p, BOUNDARY_LINE_START, [ + [0, 0, "a", 0, 1], + [1, 2, "b", 1, 2], + ]); + testTextAtOffset(p, BOUNDARY_PARAGRAPH, [[0, 2, "ab", 0, 2]]); + const leafThenWrap = findAccessibleChildByID(docAcc, "leafThenWrap"); + testTextAtOffset(leafThenWrap, BOUNDARY_LINE_START, [ + [0, 1, "ab", 0, 2], + [2, 3, "c", 2, 3], + ]); + }, + { chrome: true, topLevel: true, iframe: true, remoteIframe: true } +); + +/** + * Test line offsets after text mutation. + */ +addAccessibleTask( + ` +<p id="initBr"><br></p> +<p id="rewrap" style="font-family: monospace; width: 2ch; word-break: break-word;"><span id="rewrap1">ac</span>def</p> + `, + async function (browser, docAcc) { + const initBr = findAccessibleChildByID(docAcc, "initBr"); + testTextAtOffset(initBr, BOUNDARY_LINE_START, [ + [0, 0, "\n", 0, 1], + [1, 1, "", 1, 1], + ]); + info("initBr: Inserting text before br"); + let reordered = waitForEvent(EVENT_REORDER, initBr); + await invokeContentTask(browser, [], () => { + const initBrNode = content.document.getElementById("initBr"); + initBrNode.insertBefore( + content.document.createTextNode("a"), + initBrNode.firstElementChild + ); + }); + await reordered; + testTextAtOffset(initBr, BOUNDARY_LINE_START, [ + [0, 1, "a\n", 0, 2], + [2, 2, "", 2, 2], + ]); + + const rewrap = findAccessibleChildByID(docAcc, "rewrap"); + testTextAtOffset(rewrap, BOUNDARY_LINE_START, [ + [0, 1, "ac", 0, 2], + [2, 3, "de", 2, 4], + [4, 5, "f", 4, 5], + ]); + info("rewrap: Changing ac to abc"); + reordered = waitForEvent(EVENT_REORDER, rewrap); + await invokeContentTask(browser, [], () => { + const rewrap1 = content.document.getElementById("rewrap1"); + rewrap1.textContent = "abc"; + }); + await reordered; + testTextAtOffset(rewrap, BOUNDARY_LINE_START, [ + [0, 1, "ab", 0, 2], + [2, 3, "cd", 2, 4], + [4, 6, "ef", 4, 6], + ]); + }, + { chrome: true, topLevel: true, iframe: true, remoteIframe: true } +); + +/** + * Test retrieval of text offsets when an invalid offset is given. + */ +addAccessibleTask( + `<p id="p">test</p>`, + async function (browser, docAcc) { + const p = findAccessibleChildByID(docAcc, "p"); + testTextAtOffset(p, BOUNDARY_LINE_START, [[5, 5, "", 0, 0]]); + testTextBeforeOffset(p, BOUNDARY_LINE_START, [[5, 5, "", 0, 0]]); + testTextAfterOffset(p, BOUNDARY_LINE_START, [[5, 5, "", 0, 0]]); + }, + { + // The old HyperTextAccessible implementation doesn't crash, but it returns + // different offsets. This doesn't matter because they're invalid either + // way. Since the new HyperTextAccessibleBase implementation is all we will + // have soon, just test that. + chrome: true, + topLevel: true, + iframe: true, + remoteIframe: true, + } +); + +/** + * Test HyperText embedded object methods. + */ +addAccessibleTask( + `<div id="container">a<a id="link" href="https://example.com/">b</a>c</div>`, + async function (browser, docAcc) { + const container = findAccessibleChildByID(docAcc, "container", [ + nsIAccessibleHyperText, + ]); + is(container.linkCount, 1, "container linkCount is 1"); + let link = container.getLinkAt(0); + queryInterfaces(link, [nsIAccessible, nsIAccessibleHyperText]); + is(getAccessibleDOMNodeID(link), "link", "LinkAt 0 is the link"); + is(container.getLinkIndex(link), 0, "getLinkIndex for link is 0"); + is(link.startIndex, 1, "link's startIndex is 1"); + is(link.endIndex, 2, "link's endIndex is 2"); + is(container.getLinkIndexAtOffset(1), 0, "getLinkIndexAtOffset(1) is 0"); + is(container.getLinkIndexAtOffset(0), -1, "getLinkIndexAtOffset(0) is -1"); + is(link.linkCount, 0, "link linkCount is 0"); + }, + { + chrome: true, + topLevel: true, + iframe: true, + remoteIframe: true, + } +); + +/** + * Test HyperText embedded object methods near a list bullet. + */ +addAccessibleTask( + `<ul><li id="li"><a id="link" href="https://example.com/">a</a></li></ul>`, + async function (browser, docAcc) { + const li = findAccessibleChildByID(docAcc, "li", [nsIAccessibleHyperText]); + let link = li.getLinkAt(0); + queryInterfaces(link, [nsIAccessible]); + is(getAccessibleDOMNodeID(link), "link", "LinkAt 0 is the link"); + is(li.getLinkIndex(link), 0, "getLinkIndex for link is 0"); + is(link.startIndex, 2, "link's startIndex is 2"); + is(li.getLinkIndexAtOffset(2), 0, "getLinkIndexAtOffset(2) is 0"); + is(li.getLinkIndexAtOffset(0), -1, "getLinkIndexAtOffset(0) is -1"); + }, + { + chrome: true, + topLevel: true, + iframe: true, + remoteIframe: true, + } +); + +const boldAttrs = { "font-weight": "700" }; + +/** + * Test text attribute methods. + */ +addAccessibleTask( + ` +<p id="plain">ab</p> +<p id="bold" style="font-weight: bold;">ab</p> +<p id="partialBold">ab<b>cd</b>ef</p> +<p id="consecutiveBold">ab<b>cd</b><b>ef</b>gh</p> +<p id="embeddedObjs">ab<a href="https://example.com/">cd</a><a href="https://example.com/">ef</a><a href="https://example.com/">gh</a>ij</p> +<p id="empty"></p> +<p id="fontFamilies" style="font-family: sans-serif;">ab<span style="font-family: monospace;">cd</span><span style="font-family: monospace;">ef</span>gh</p> + `, + async function (browser, docAcc) { + let defAttrs = { + "text-position": "baseline", + "font-style": "normal", + "font-weight": "400", + }; + + const plain = findAccessibleChildByID(docAcc, "plain"); + testDefaultTextAttrs(plain, defAttrs, true); + for (let offset = 0; offset <= 2; ++offset) { + testTextAttrs(plain, offset, {}, defAttrs, 0, 2, true); + } + + const bold = findAccessibleChildByID(docAcc, "bold"); + defAttrs["font-weight"] = "700"; + testDefaultTextAttrs(bold, defAttrs, true); + testTextAttrs(bold, 0, {}, defAttrs, 0, 2, true); + + const partialBold = findAccessibleChildByID(docAcc, "partialBold"); + defAttrs["font-weight"] = "400"; + testDefaultTextAttrs(partialBold, defAttrs, true); + testTextAttrs(partialBold, 0, {}, defAttrs, 0, 2, true); + testTextAttrs(partialBold, 2, boldAttrs, defAttrs, 2, 4, true); + testTextAttrs(partialBold, 4, {}, defAttrs, 4, 6, true); + + const consecutiveBold = findAccessibleChildByID(docAcc, "consecutiveBold"); + testDefaultTextAttrs(consecutiveBold, defAttrs, true); + testTextAttrs(consecutiveBold, 0, {}, defAttrs, 0, 2, true); + testTextAttrs(consecutiveBold, 2, boldAttrs, defAttrs, 2, 6, true); + testTextAttrs(consecutiveBold, 6, {}, defAttrs, 6, 8, true); + + const embeddedObjs = findAccessibleChildByID(docAcc, "embeddedObjs"); + testDefaultTextAttrs(embeddedObjs, defAttrs, true); + testTextAttrs(embeddedObjs, 0, {}, defAttrs, 0, 2, true); + for (let offset = 2; offset <= 4; ++offset) { + // attrs and defAttrs should be completely empty, so we pass + // false for aSkipUnexpectedAttrs. + testTextAttrs(embeddedObjs, offset, {}, {}, 2, 5, false); + } + testTextAttrs(embeddedObjs, 5, {}, defAttrs, 5, 7, true); + + const empty = findAccessibleChildByID(docAcc, "empty"); + testDefaultTextAttrs(empty, defAttrs, true); + testTextAttrs(empty, 0, {}, defAttrs, 0, 0, true); + + const fontFamilies = findAccessibleChildByID(docAcc, "fontFamilies", [ + nsIAccessibleHyperText, + ]); + testDefaultTextAttrs(fontFamilies, defAttrs, true); + testTextAttrs(fontFamilies, 0, {}, defAttrs, 0, 2, true); + testTextAttrs(fontFamilies, 2, {}, defAttrs, 2, 6, true); + testTextAttrs(fontFamilies, 6, {}, defAttrs, 6, 8, true); + }, + { + chrome: true, + topLevel: true, + iframe: true, + remoteIframe: true, + } +); |