`,
expected: [
"test2 test3",
"test1",
"This is a paragraph This is a link \u2022 This is a list",
"test5",
],
},
{
id: "gc",
ruleset: "HTMLCell",
markup: `
test2test3
`,
expected: [
"test2 test3",
"test1",
"This is a paragraph This is a link \u2022 Listitem1 \u2022 Listitem2",
"This is a paragraph This is a link This is a list",
],
},
{
id: "t",
ruleset: "HTMLTable",
markup: `
lby_tst6_1lby_tst6_2
`,
expected: ["do not press me", "press me"],
},
{
// TODO: uncomment when Bug-1256382 is resoved.
// id: 'li',
// ruleset: 'CSSContent',
// markup: `
//
//
//
Listitem
//
`,
// expected: ['1. Listitem', `${String.fromCharCode(0x2022)} Listitem`]
// }, {
id: "a",
ruleset: "HTMLLink",
markup: `
test2test3test5`,
expected: ["test2 test3", "test1", "test5", "test4"],
},
{
id: "a-img",
ruleset: "HTMLLinkImage",
markup: `
test2test3`,
expected: ["test2 test3", "test1", "test5", "test4"],
},
];
/**
* Test accessible name that is calculated from an attribute, remove the
* attribute before proceeding to the next name test. If attribute removal
* results in a reorder or text inserted event - wait for it. If accessible
* becomes defunct, update its reference using the one that is attached to one
* of the above events.
*
* @param {object} browser current "tabbrowser" element
* @param {object} target { acc, id } structure that contains an
* accessible and its content element
* id.
* @param {object} rule current attr rule for name calculation
* @param {[type]} expected expected name value
*/
async function testAttrRule(browser, target, rule, expected) {
let { id, acc } = target;
let { attr } = rule;
testName(acc, expected);
let nameChange = waitForEvent(EVENT_NAME_CHANGE, id);
await invokeContentTask(browser, [id, attr], (contentId, contentAttr) => {
content.document.getElementById(contentId).removeAttribute(contentAttr);
});
let event = await nameChange;
// Update accessible just in case it is now defunct.
target.acc = findAccessibleChildByID(event.accessible, id);
}
/**
* Test accessible name that is calculated from an element name, remove the
* element before proceeding to the next name test. If element removal results
* in a reorder event - wait for it. If accessible becomes defunct, update its
* reference using the one that is attached to a possible reorder event.
*
* @param {object} browser current "tabbrowser" element
* @param {object} target { acc, id } structure that contains an
* accessible and its content element
* id.
* @param {object} rule current elm rule for name calculation
* @param {[type]} expected expected name value
*/
async function testElmRule(browser, target, rule, expected) {
let { id, acc } = target;
let { elm } = rule;
testName(acc, expected);
let nameChange = waitForEvent(EVENT_NAME_CHANGE, id);
await invokeContentTask(browser, [elm], contentElm => {
content.document.querySelector(`${contentElm}`).remove();
});
let event = await nameChange;
// Update accessible just in case it is now defunct.
target.acc = findAccessibleChildByID(event.accessible, id);
}
/**
* Test accessible name that is calculated from its subtree, remove the subtree
* and wait for a reorder event before proceeding to the next name test. If
* accessible becomes defunct, update its reference using the one that is
* attached to a reorder event.
*
* @param {object} browser current "tabbrowser" element
* @param {object} target { acc, id } structure that contains an
* accessible and its content element
* id.
* @param {object} rule current subtree rule for name calculation
* @param {[type]} expected expected name value
*/
async function testSubtreeRule(browser, target, rule, expected) {
let { id, acc } = target;
testName(acc, expected);
let nameChange = waitForEvent(EVENT_NAME_CHANGE, id);
await invokeContentTask(browser, [id], contentId => {
let elm = content.document.getElementById(contentId);
while (elm.firstChild) {
elm.firstChild.remove();
}
});
let event = await nameChange;
// Update accessible just in case it is now defunct.
target.acc = findAccessibleChildByID(event.accessible, id);
}
/**
* Iterate over a list of rules and test accessible names for each one of the
* rules.
*
* @param {object} browser current "tabbrowser" element
* @param {object} target { acc, id } structure that contains an
* accessible and its content element
* id.
* @param {Array} ruleset A list of rules to test a target with
* @param {Array} expected A list of expected name value for each rule
*/
async function testNameRule(browser, target, ruleset, expected) {
for (let i = 0; i < ruleset.length; ++i) {
let rule = ruleset[i];
let testFn;
if (rule.attr) {
testFn = testAttrRule;
} else if (rule.elm) {
testFn = testElmRule;
} else if (rule.fromsubtree) {
testFn = testSubtreeRule;
}
await testFn(browser, target, rule, expected[i]);
}
}
markupTests.forEach(({ id, ruleset, markup, expected }) =>
addAccessibleTask(
markup,
async function (browser, accDoc) {
const observer = {
observe(subject) {
const event = subject.QueryInterface(nsIAccessibleEvent);
console.log(eventToString(event));
},
};
Services.obs.addObserver(observer, "accessible-event");
// Find a target accessible from an accessible subtree.
let acc = findAccessibleChildByID(accDoc, id);
let target = { id, acc };
await testNameRule(browser, target, rules[ruleset], expected);
Services.obs.removeObserver(observer, "accessible-event");
},
{ iframe: true, remoteIframe: true }
)
);
/**
* Generic test cases ported from mochitest/name/test_general.html
*/
addAccessibleTask(
`
text text1text2 nomore text
nomore
hidden
text2
hidden2 lala more hidden text
text
more text
textTEXTtext
text text text
Choose country from.
Country
Error
14
i am visiblei am hidden
menuitem 1
menuitem 2
This is a paragraph inside the article.
heading
aria_heading
15
Image:
Text:
x2 +
y2 + z2
subtree
a
b
a
b
a
b
a
b
label
label
root
sub
label
This content should not be included in the grouping's label.
abc
a
b
c
ab
shadowButtonVisibleText
shadowButtonHiddenText
content
before
codesupsub
ins
del
after
`,
async function testGeneric(browser, docAcc) {
// aria-label
function testName_(id, expectedName) {
const acc = findAccessibleChildByID(docAcc, id);
testName(acc, expectedName);
}
// Simple label provided via ARIA
testName_("btn_simple_aria_label", "I am a button");
// aria-label and aria-labelledby, expect aria-labelledby
testName_("btn_both_aria_labels", "text I am a button, two");
// ////////////////////////////////////////////////////////////////////////
// aria-labelledby
// Single relation. The value of 'aria-labelledby' contains the ID of
// an element. Gets the name from text node of that element.
testName_("btn_labelledby_text", "text");
// Multiple relations. The value of 'aria-labelledby' contains the IDs
// of elements. Gets the name from text nodes of those elements.
testName_("btn_labelledby_texts", "text1 text2");
// ////////////////////////////////////////////////////////////////////////
// Name from named accessible
testName_("input_labelledby_namedacc", "Data");
// ////////////////////////////////////////////////////////////////////////
// Name from subtree (single relation labelled_by).
// Gets the name from text nodes contained by nested elements
testName_("btn_labelledby_mixed", "nomore text");
// Gets the name from text nodes contained by nested elements, ignores
// hidden elements (bug 443081).
testName_("btn_labelledby_mixed_hidden_child", "nomore text2");
// Gets the name from hidden text nodes contained by nested elements,
// (label element is hidden entirely), (bug 443081).
testName_("btn_labelledby_mixed_hidden", "lala more hidden text");
// Gets the name from text nodes contained by nested elements having block
// representation (every text node value in the name should be divided by
// spaces)
testName_("btn_labelledby_mixed_block", "text more text");
// Gets the name from text nodes contained by html:td. The text nodes
// should not be divided by spaces.
testName_("btn_labelledby_mixed_table", "textTEXTtext");
// Gets the name from image accessible.
testName_("btn_labelledby_mixed_img", "text image");
// Gets the name from input accessibles
// Note: if input have label elements then the name isn't calculated
// from them.
testName_(
"btn_labelledby_mixed_input",
"input button Submit Query Reset Submit Query"
);
// Gets the name from the title of object element.
testName_("btn_labelledby_mixed_object", "object");
// Gets the name from text nodes. Element br adds space between them.
testName_("btn_labelledby_mixed_br", "text text");
// Gets the name from label content which allows name from subtree,
// ignore @title attribute on label
testName_("from_label_ignoretitle", "Country:");
// Gets the name from html:p content, which doesn't allow name from
// subtree, ignore @title attribute on label
testName_("from_p_ignoretitle", "Choose country from.");
// Gets the name from html:input value, ignore @title attribute on input
testName_("from_input_ignoretitle", "Custom country");
// Insert spaces around the control's value to not jam sibling text nodes
testName_("insert_spaces_around_control", "start value end");
// Gets the name from @title, ignore whitespace content
testName_("from_label_ignore_ws_subtree", "about");
// role="alert" doesn't get name from subtree...
testName_("alert", null);
// but the subtree is used if referenced by aria-labelledby.
testName_("inputLabelledByAlert", "Error");
// ////////////////////////////////////////////////////////////////////////
// label element
// The label element contains the button. The name of the button is
// calculated from the content of the label.
// Note: the name does not contain the content of the button.
testName_("btn_label_inside", "texttext");
// The label element and the button are placed in the same form. Gets
// the name from the label subtree.
testName_("btn_label_inform", "in form");
// The label element is placed outside of form where the button is.
// Take into account the label.
testName_("btn_label_outform", "out form");
// The label element and the button are in the same document. Gets the
// name from the label subtree.
testName_("btn_label_indocument", "in document");
// Multiple label elements for single button
testName_("btn_label_multi", "label1 label2");
// Multiple controls inside a label element
testName_("ctrl_in_label_1", "Enable a button control");
testName_("ctrl_in_label_2", "button");
// ////////////////////////////////////////////////////////////////////////
// name from children
// ARIA role button is presented allowing the name calculation from
// children.
testName_("btn_children", "14");
// html:button, no name from content
testName_("btn_nonamefromcontent", null);
// ARIA role option is presented allowing the name calculation from
// visible children (bug 443081).
testName_("lb_opt1_children_hidden", "i am visible");
// Get the name from subtree of menuitem crossing role nothing to get
// the name from its children.
testName_("tablemenuitem", "menuitem 1");
// Get the name from child acronym title attribute rather than from
// acronym content.
testName_("label_with_acronym", "O A T F World Wide Web");
testName_("testArticle", "Test article");
testName_("h1", "heading");
testName_("aria_heading", "aria_heading");
// ////////////////////////////////////////////////////////////////////////
// title attribute
// If nothing is left. Let's try title attribute.
testName_("btn_title", "title");
// ////////////////////////////////////////////////////////////////////////
// textarea name
// textarea's name should not have the value, which initially is specified
// by a text child.
testName_("textareawithchild", "Story is ended.");
// new textarea name should not reflect the value change.
let valueChanged = waitForEvent(
EVENT_TEXT_VALUE_CHANGE,
"textareawithchild"
);
await invokeContentTask(browser, [], () => {
content.document.getElementById("textareawithchild").value = "Bar";
});
await valueChanged;
testName_("textareawithchild", "Story is ended.");
// ////////////////////////////////////////////////////////////////////////
// controls having a value used as a part of computed name
testName_("ctrlvalue_progressbar:input", "foo 5 baz");
testName_("ctrlvalue_scrollbar:input", "foo 5 baz");
testName_("ctrlvalue_slider:input", "foo 5 baz");
testName_("ctrlvalue_spinbutton:input", "foo 5 baz");
testName_("ctrlvalue_combobox:input", "foo 5 baz");
testName_("ctrlvalue_meter:input", "foo 5 baz");
// ///////////////////////////////////////////////////////////////////////
// label with nested combobox (test for 'f' item of name computation guide)
testName_("comboinstart", "One day(s).");
testName_("combo3", "day(s).");
testName_("textboxinstart", "Two days.");
testName_("textbox1", "days.");
testName_("comboinmiddle", "Subscribe to ATOM feed.");
testName_("combo4", "Subscribe to feed.");
testName_(
"comboinmiddle2",
"Play the Haliluya sound when new mail arrives"
);
testName_("combo5", null); // label isn't used as a name for control
testName_("checkbox", "Play the Haliluya sound when new mail arrives");
testName_(
"comboinmiddle3",
"Play the Haliluya sound when new mail arrives"
);
testName_("combo6", "Play the sound when new mail arrives");
testName_("comboinend", "This day was sunny");
testName_("combo7", "This day was");
testName_("textboxinend", "This day was sunny");
testName_("textbox2", "This day was");
// placeholder
testName_("ph_password", "a placeholder");
testName_("ph_text", "a placeholder");
testName_("ph_textarea", "a placeholder");
testName_("ph_text2", "a label");
testName_("ph_textarea2", "a label");
testName_("ph_text3", "a label");
// Test equation image
testName_("img_eq", "x^2 + y^2 + z^2");
testName_("input_img_eq", "x^2 + y^2 + z^2");
testName_("txt_eq", "x^2 + y^2 + z^2");
// //////////////////////////////////////////////////////////////////////
// tests for duplicate announcement of content
testName_("test_note", null);
// //////////////////////////////////////////////////////////////////////
// Tests for name from sub tree of tr element.
// By default, we want no name.
testName_("NoNameForTR", null);
testName_("NoNameForNonStandardTR", null);
// But we want it if the tr has an ARIA role.
testName_("NameForTRBecauseStrongARIA", "a b");
// But not if it is a weak (landmark) ARIA role
testName_("NoNameForTRBecauseWeakARIA", null);
// Name from sub tree of grouping if requested by other accessible.
testName_("grouping", null);
testName_("requested_name_from_grouping", "label");
testName_("listitem_containing_block_tbody", "label");
// Groupings shouldn't be included when calculating from the subtree of
// a treeitem.
testName_("treeitem_containing_grouping", "root");
// Name from subtree of grouping labelled by an ancestor.
testName_("grouping_labelledby_ancestor", "label");
// Name from subtree of a container containing text nodes and inline
// elements. There should be no spaces because everything is inline.
testName_("container_text_inline", "abc");
// Name from subtree of a container containing text nodes and block
// elements. There should be a space on both sides of the block.
testName_("container_text_block", "a b c");
// Name from subtree of a container containing text nodes and empty
// block elements. There should be space on either side of the blocks, but
// not a double space.
testName_("container_text_emptyblock", "a b");
let reordered = waitForEvent(EVENT_REORDER, "shadowHost");
await invokeContentTask(browser, [], () => {
const shadowHost = content.document.getElementById("shadowHost");
const shadowRoot = shadowHost.attachShadow({ mode: "open" });
shadowRoot.append(
content.document
.getElementById("shadowTemplate")
.content.cloneNode(true)
);
});
await reordered;
const shadowRoot = findAccessibleChildByID(docAcc, "shadowHost");
testName(shadowRoot.firstChild, "shadowButtonVisibleText");
testName(shadowRoot.lastChild, "shadowButtonHiddenText");
// Label from a hidden element containing script and style elements.
testName_("buttonScriptStyle", "content");
// Name from subtree on a link containing , etc.
testName_("linkWithCodeSupSubInsDel", "before code sup sub ins del after");
},
{ topLevel: true, chrome: true }
);