Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Errors
- This test failed 6 times in the preceding 30 days. quicksearch this test
- Manifest: accessible/tests/browser/text/browser.toml
/* 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/states.js */
loadScripts({ name: "states.js", dir: MOCHITESTS_DIR });
async function testEditable(browser, acc, aBefore = "", aAfter = "") {
async function resetInput() {
if (acc.childCount <= 1) {
return;
}
let emptyInputEvent = waitForEvent(EVENT_TEXT_VALUE_CHANGE, "input");
await invokeContentTask(browser, [], async () => {
content.document.getElementById("input").innerHTML = "";
});
await emptyInputEvent;
}
// ////////////////////////////////////////////////////////////////////////
// insertText
await testInsertText(acc, "hello", 0, aBefore.length);
await isFinalValueCorrect(browser, acc, [aBefore, "hello", aAfter]);
await testInsertText(acc, "ma ", 0, aBefore.length);
await isFinalValueCorrect(browser, acc, [aBefore, "ma hello", aAfter]);
await testInsertText(acc, "ma", 2, aBefore.length);
await isFinalValueCorrect(browser, acc, [aBefore, "mama hello", aAfter]);
await testInsertText(acc, " hello", 10, aBefore.length);
await isFinalValueCorrect(browser, acc, [
aBefore,
"mama hello hello",
aAfter,
]);
// ////////////////////////////////////////////////////////////////////////
// deleteText
await testDeleteText(acc, 0, 5, aBefore.length);
await isFinalValueCorrect(browser, acc, [aBefore, "hello hello", aAfter]);
await testDeleteText(acc, 5, 6, aBefore.length);
await isFinalValueCorrect(browser, acc, [aBefore, "hellohello", aAfter]);
await testDeleteText(acc, 5, 10, aBefore.length);
await isFinalValueCorrect(browser, acc, [aBefore, "hello", aAfter]);
await testDeleteText(acc, 0, 5, aBefore.length);
// When a contentEditable inside a document is cleared, it contains a single
// line feed. See dom::HTMLBrElement::IsPaddingForEmptyEditor.
const clearedText =
acc.role == ROLE_DOCUMENT ||
acc.attributes.getStringProperty("tag") == "input"
? ""
: "\n";
await isFinalValueCorrect(browser, acc, [aBefore, clearedText, aAfter]);
// XXX: clipboard operation tests don't work well with editable documents.
if (acc.role == ROLE_DOCUMENT) {
return;
}
await resetInput();
// copyText and pasteText
await testInsertText(acc, "hello", 0, aBefore.length);
await isFinalValueCorrect(browser, acc, [aBefore, "hello", aAfter]);
await testCopyText(acc, 0, 1, aBefore.length, browser, "h");
await testPasteText(acc, 1, aBefore.length);
await isFinalValueCorrect(browser, acc, [aBefore, "hhello", aAfter]);
await testCopyText(acc, 5, 6, aBefore.length, browser, "o");
await testPasteText(acc, 6, aBefore.length);
await isFinalValueCorrect(browser, acc, [aBefore, "hhelloo", aAfter]);
await testCopyText(acc, 2, 3, aBefore.length, browser, "e");
await testPasteText(acc, 1, aBefore.length);
await isFinalValueCorrect(browser, acc, [aBefore, "hehelloo", aAfter]);
// cut & paste
await testCutText(acc, 0, 1, aBefore.length);
await isFinalValueCorrect(browser, acc, [aBefore, "ehelloo", aAfter]);
await testPasteText(acc, 2, aBefore.length);
await isFinalValueCorrect(browser, acc, [aBefore, "ehhelloo", aAfter]);
await testCutText(acc, 3, 4, aBefore.length);
await isFinalValueCorrect(browser, acc, [aBefore, "ehhlloo", aAfter]);
await testPasteText(acc, 6, aBefore.length);
await isFinalValueCorrect(browser, acc, [aBefore, "ehhlloeo", aAfter]);
await testCutText(acc, 0, 8, aBefore.length);
await isFinalValueCorrect(browser, acc, [aBefore, clearedText, aAfter]);
await resetInput();
// ////////////////////////////////////////////////////////////////////////
// setTextContents
await testSetTextContents(acc, "hello", aBefore.length, [
EVENT_TEXT_INSERTED,
]);
await isFinalValueCorrect(browser, acc, [aBefore, "hello", aAfter]);
await testSetTextContents(acc, "katze", aBefore.length, [
EVENT_TEXT_REMOVED,
EVENT_TEXT_INSERTED,
]);
await isFinalValueCorrect(browser, acc, [aBefore, "katze", aAfter]);
}
addAccessibleTask(
`<input id="input"/>`,
async function (browser, docAcc) {
await testEditable(browser, findAccessibleChildByID(docAcc, "input"));
},
{ chrome: true, topLevel: true }
);
addAccessibleTask(
`<div id="input" contenteditable="true" role="textbox"></div>`,
async function (browser, docAcc) {
await testEditable(
browser,
findAccessibleChildByID(docAcc, "input"),
"",
""
);
},
{ chrome: true, topLevel: true }
);
addAccessibleTask(
`<style>
#input::after {
content: "pseudo element";
}
</style>
<div id="input" contenteditable="true" role="textbox"></div>`,
async function (browser, docAcc) {
await testEditable(
browser,
findAccessibleChildByID(docAcc, "input"),
"",
"pseudo element"
);
},
{ chrome: true, topLevel: true }
);
addAccessibleTask(
`<style>
#input::before {
content: "pseudo element";
}
</style>
<div id="input" contenteditable="true" role="textbox"></div>`,
async function (browser, docAcc) {
await testEditable(
browser,
findAccessibleChildByID(docAcc, "input"),
"pseudo element"
);
},
{ chrome: true, topLevel: true }
);
addAccessibleTask(
`<style>
#input::before {
content: "before";
}
#input::after {
content: "after";
}
</style>
<div id="input" contenteditable="true" role="textbox"></div>`,
async function (browser, docAcc) {
await testEditable(
browser,
findAccessibleChildByID(docAcc, "input"),
"before",
"after"
);
},
{ chrome: true, topLevel: true }
);
addAccessibleTask(
`<style>
br {
position: fixed;
}
</style>
<div id="input" contenteditable="true" role="textbox"></div>`,
async function (browser, docAcc) {
document.execCommand("insertText", false, "a");
document.execCommand("delete");
await testEditable(browser, findAccessibleChildByID(docAcc, "input"));
},
{ chrome: true, topLevel: true }
);
addAccessibleTask(
`<style>
#input {
white-space: pre;
}
#input::before {
content: "before";
}
#input::after {
content: "after";
}
</style>
<div id="input" contenteditable="plaintext-only" role="textbox"></div>`,
async function (browser, docAcc) {
await testEditable(
browser,
findAccessibleChildByID(docAcc, "input"),
"before",
"after"
);
},
{ chrome: true, topLevel: true }
);
addAccessibleTask(
``,
async function (browser, docAcc) {
await testEditable(browser, docAcc);
},
{
chrome: true,
topLevel: true,
contentDocBodyAttrs: { contentEditable: "true" },
}
);
/**
* Test PasteText replacement of selected text.
*/
addAccessibleTask(
`<input id="input" value="abcdef">`,
async function testPasteTextReplace(browser, docAcc) {
const input = findAccessibleChildByID(docAcc, "input");
let focused = waitForEvent(EVENT_FOCUS, input);
info("Focusing input");
input.takeFocus();
await focused;
info("Copying ef");
input.QueryInterface(nsIAccessibleEditableText);
let selected = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, input);
input.copyText(4, 6);
await selected;
info("Selecting bc");
selected = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, input);
await invokeContentTask(browser, [], () => {
const inputDom = content.document.getElementById("input");
inputDom.selectionStart = 1;
inputDom.selectionEnd = 3;
});
await selected;
info("Pasting at caret");
let changed = waitForEvents([
[EVENT_TEXT_REMOVED, input],
[EVENT_TEXT_INSERTED, input],
[EVENT_TEXT_VALUE_CHANGE, input],
]);
input.pasteText(nsIAccessibleText.TEXT_OFFSET_CARET);
await changed;
is(input.value, "aefdef", "input value correct after pasting");
}
);
addAccessibleTask(
`<div id="editable" contenteditable="true"><p id="p">one</p></div>`,
async function testNoRoleEditable(browser, docAcc) {
const editable = findAccessibleChildByID(docAcc, "editable");
is(editable.value, "one", "initial value correct");
ok(true, "Set initial text");
await invokeContentTask(browser, [], () => {
content.document.getElementById("p").firstChild.data = "two";
});
await untilCacheIs(() => editable.value, "two", "value changed correctly");
function isMultiline() {
let extState = {};
editable.getState({}, extState);
return (
!!(extState.value & EXT_STATE_MULTI_LINE) &&
!(extState.value & EXT_STATE_SINGLE_LINE)
);
}
ok(isMultiline(), "Editable is in multiline state");
await invokeSetAttribute(browser, "editable", "aria-multiline", "false");
await untilCacheOk(() => !isMultiline(), "editable is in singleline state");
await invokeSetAttribute(browser, "editable", "aria-multiline");
await untilCacheOk(() => isMultiline(), "editable is in multi-line again");
await invokeSetAttribute(browser, "editable", "contenteditable");
await untilCacheOk(() => {
let extState = {};
editable.getState({}, extState);
return (
!(extState.value & EXT_STATE_MULTI_LINE) &&
!(extState.value & EXT_STATE_SINGLE_LINE)
);
}, "editable should have neither multi-line nor single-line state");
},
{
chrome: true,
topLevel: true,
}
);