/* 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"; requestLongerTimeout(2); /* import-globals-from ../../mochitest/name.js */ loadScripts({ name: "name.js", dir: MOCHITESTS_DIR }); /** * Rules for name tests that are inspired by * accessible/tests/mochitest/name/markuprules.xul * * Each element in the list of rules represents a name calculation rule for a * particular test case. * * The rules have the following format: * { attr } - calculated from attribute * { elm } - calculated from another element * { fromsubtree } - calculated from element's subtree * */ const ARIARule = [{ attr: "aria-labelledby" }, { attr: "aria-label" }]; const HTMLControlHeadRule = [...ARIARule, { elm: "label" }]; const rules = { CSSContent: [{ elm: "style" }, { fromsubtree: true }], HTMLARIAGridCell: [...ARIARule, { fromsubtree: true }, { attr: "title" }], HTMLControl: [ ...HTMLControlHeadRule, { fromsubtree: true }, { attr: "title" }, ], HTMLElm: [...ARIARule, { attr: "title" }], HTMLImg: [...ARIARule, { attr: "alt" }, { attr: "title" }], HTMLImgEmptyAlt: [...ARIARule, { attr: "title" }, { attr: "alt" }], HTMLInputButton: [ ...HTMLControlHeadRule, { attr: "value" }, { attr: "title" }, ], HTMLInputImage: [ ...HTMLControlHeadRule, { attr: "alt" }, { attr: "value" }, { attr: "title" }, ], HTMLInputImageNoValidSrc: [ ...HTMLControlHeadRule, { attr: "alt" }, { attr: "value" }, ], HTMLInputReset: [...HTMLControlHeadRule, { attr: "value" }], HTMLInputSubmit: [...HTMLControlHeadRule, { attr: "value" }], HTMLLink: [...ARIARule, { fromsubtree: true }, { attr: "title" }], HTMLLinkImage: [...ARIARule, { fromsubtree: true }, { attr: "title" }], HTMLOption: [ ...ARIARule, { attr: "label" }, { fromsubtree: true }, { attr: "title" }, ], HTMLTable: [ ...ARIARule, { elm: "caption" }, { attr: "summary" }, { attr: "title" }, ], }; const markupTests = [ { id: "btn", ruleset: "HTMLControl", markup: ` test2 test3 `, expected: ["test2 test3", "test1", "test4", "press me", "test5"], }, { id: "btn", ruleset: "HTMLInputButton", markup: ` test2 test3 `, expected: [ "test2 test3", "test1", "test4", "name from value", "name from title", ], }, { id: "btn-submit", ruleset: "HTMLInputSubmit", markup: ` test2 test3 `, expected: ["test2 test3", "test1", "test4", "name from value"], }, { id: "btn-reset", ruleset: "HTMLInputReset", markup: ` test2 test3 `, expected: ["test2 test3", "test1", "test4", "name from value"], }, { id: "btn-image", ruleset: "HTMLInputImage", markup: ` test2 test3 `, expected: [ "test2 test3", "test1", "test4", "name from alt", "name from value", "name from title", ], }, { id: "btn-image", ruleset: "HTMLInputImageNoValidSrc", markup: ` test2 test3 `, expected: [ "test2 test3", "test1", "test4", "name from alt", "name from value", ], }, { id: "opt", ruleset: "HTMLOption", markup: ` test2 test3 `, expected: ["test2 test3", "test1", "test4", "option1", "test5"], }, { id: "img", ruleset: "HTMLImg", markup: ` test2 test3 Mozilla logo`, expected: [ "test2 test3", "Logo of Mozilla", "Mozilla logo", "This is a logo", ], }, { id: "tc", ruleset: "HTMLElm", markup: ` test2 test3

This is a paragraph

This is a link
  • This is a list
`, expected: ["test2 test3", "test1", "test5"], }, { id: "gc", ruleset: "HTMLARIAGridCell", markup: ` test2 test3

This is a paragraph

This is a link
  • Listitem1
  • Listitem2
`, 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_1 lby_tst6_2
caption_tst6
cell1 cell2
`, expected: [ "lby_tst6_1 lby_tst6_2", "arialabel_tst6", "caption_tst6", "summary_tst6", "title_tst6", ], }, { id: "btn", ruleset: "CSSContent", markup: `
`, 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: ` test2 test3 test5`, expected: ["test2 test3", "test1", "test5", "test4"], }, { id: "a-img", ruleset: "HTMLLinkImage", markup: ` test2 test3 test5`, 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 } ) ); /** * Test caching of the document title. */ addAccessibleTask( ``, async function (browser, docAcc) { let nameChanged = waitForEvent(EVENT_NAME_CHANGE, docAcc); await invokeContentTask(browser, [], () => { content.document.title = "new title"; }); await nameChanged; testName(docAcc, "new title"); }, { chrome: true, topLevel: true, iframe: true, remoteIframe: true } ); /** * Test that the name is updated when the content of a hidden aria-labelledby * subtree changes. */ addAccessibleTask( `
a
`, async function (browser, docAcc) { const button = findAccessibleChildByID(docAcc, "button"); testName(button, "a"); info("Changing label textContent"); let nameChanged = waitForEvent(EVENT_NAME_CHANGE, button); await invokeContentTask(browser, [], () => { content.document.getElementById("label").textContent = "c"; }); await nameChanged; testName(button, "c"); info("Change label text node's data"); nameChanged = waitForEvent(EVENT_NAME_CHANGE, button); await invokeContentTask(browser, [], () => { content.document.getElementById("label").firstChild.data = "d"; }); await nameChanged; testName(button, "d"); info("Prepending text node to label"); nameChanged = waitForEvent(EVENT_NAME_CHANGE, button); await invokeContentTask(browser, [], () => { content.document .getElementById("label") .prepend(content.document.createTextNode("b")); }); await nameChanged; testName(button, "bd"); }, { chrome: true, topLevel: true, iframe: true, remoteIframe: true } );