зеркало из https://github.com/mozilla/gecko-dev.git
540 строки
16 KiB
JavaScript
540 строки
16 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/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
|
|
*
|
|
*
|
|
* Options include:
|
|
* * waitFor - changes in the subtree will result in an accessible event
|
|
* being fired, the test must only continue after the event
|
|
* is receieved.
|
|
*/
|
|
const ARIARule = [
|
|
{ attr: "aria-labelledby" },
|
|
{ attr: "aria-label", waitFor: EVENT_NAME_CHANGE },
|
|
];
|
|
const HTMLControlHeadRule = [...ARIARule, { elm: "label", isSibling: true }];
|
|
const rules = {
|
|
CSSContent: [{ elm: "style", isSibling: true }, { fromsubtree: true }],
|
|
HTMLARIAGridCell: [...ARIARule, { fromsubtree: true }, { attr: "title" }],
|
|
HTMLControl: [
|
|
...HTMLControlHeadRule,
|
|
{ fromsubtree: true },
|
|
{ attr: "title" },
|
|
],
|
|
HTMLElm: [...ARIARule, { attr: "title" }],
|
|
HTMLImg: [
|
|
...ARIARule,
|
|
{ attr: "alt", waitFor: EVENT_NAME_CHANGE },
|
|
{ attr: "title" },
|
|
],
|
|
HTMLImgEmptyAlt: [...ARIARule, { attr: "title" }, { attr: "alt" }],
|
|
HTMLInputButton: [
|
|
...HTMLControlHeadRule,
|
|
{ attr: "value" },
|
|
{ attr: "title" },
|
|
],
|
|
HTMLInputImage: [
|
|
...HTMLControlHeadRule,
|
|
{ attr: "alt", waitFor: EVENT_NAME_CHANGE },
|
|
{ attr: "value" },
|
|
{ attr: "title" },
|
|
],
|
|
HTMLInputImageNoValidSrc: [
|
|
...HTMLControlHeadRule,
|
|
{ attr: "alt", waitFor: EVENT_NAME_CHANGE },
|
|
{ attr: "value" },
|
|
],
|
|
HTMLInputReset: [
|
|
...HTMLControlHeadRule,
|
|
{ attr: "value", waitFor: EVENT_TEXT_INSERTED },
|
|
],
|
|
HTMLInputSubmit: [
|
|
...HTMLControlHeadRule,
|
|
{ attr: "value", waitFor: EVENT_TEXT_INSERTED },
|
|
],
|
|
HTMLLink: [...ARIARule, { fromsubtree: true }, { attr: "title" }],
|
|
HTMLLinkImage: [...ARIARule, { elm: "img" }, { 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: `
|
|
<span id="l1">test2</span>
|
|
<span id="l2">test3</span>
|
|
<label for="btn">test4</label>
|
|
<button id="btn"
|
|
aria-label="test1"
|
|
aria-labelledby="l1 l2"
|
|
title="test5">press me</button>`,
|
|
expected: ["test2 test3", "test1", "test4", "press me", "test5"],
|
|
},
|
|
{
|
|
id: "btn",
|
|
ruleset: "HTMLInputButton",
|
|
markup: `
|
|
<span id="l1">test2</span>
|
|
<span id="l2">test3</span>
|
|
<label for="btn">test4</label>
|
|
<input id="btn"
|
|
type="button"
|
|
aria-label="test1"
|
|
aria-labelledby="l1 l2"
|
|
value="name from value"
|
|
alt="no name from al"
|
|
src="no name from src"
|
|
data="no name from data"
|
|
title="name from title"/>`,
|
|
expected: [
|
|
"test2 test3",
|
|
"test1",
|
|
"test4",
|
|
"name from value",
|
|
"name from title",
|
|
],
|
|
},
|
|
{
|
|
id: "btn-submit",
|
|
ruleset: "HTMLInputSubmit",
|
|
markup: `
|
|
<span id="l1">test2</span>
|
|
<span id="l2">test3</span>
|
|
<label for="btn-submit">test4</label>
|
|
<input id="btn-submit"
|
|
type="submit"
|
|
aria-label="test1"
|
|
aria-labelledby="l1 l2"
|
|
value="name from value"
|
|
alt="no name from atl"
|
|
src="no name from src"
|
|
data="no name from data"
|
|
title="no name from title"/>`,
|
|
expected: ["test2 test3", "test1", "test4", "name from value"],
|
|
},
|
|
{
|
|
id: "btn-reset",
|
|
ruleset: "HTMLInputReset",
|
|
markup: `
|
|
<span id="l1">test2</span>
|
|
<span id="l2">test3</span>
|
|
<label for="btn-reset">test4</label>
|
|
<input id="btn-reset"
|
|
type="reset"
|
|
aria-label="test1"
|
|
aria-labelledby="l1 l2"
|
|
value="name from value"
|
|
alt="no name from alt"
|
|
src="no name from src"
|
|
data="no name from data"
|
|
title="no name from title"/>`,
|
|
expected: ["test2 test3", "test1", "test4", "name from value"],
|
|
},
|
|
{
|
|
id: "btn-image",
|
|
ruleset: "HTMLInputImage",
|
|
markup: `
|
|
<span id="l1">test2</span>
|
|
<span id="l2">test3</span>
|
|
<label for="btn-image">test4</label>
|
|
<input id="btn-image"
|
|
type="image"
|
|
aria-label="test1"
|
|
aria-labelledby="l1 l2"
|
|
alt="name from alt"
|
|
value="name from value"
|
|
src="http://example.com/a11y/accessible/tests/mochitest/moz.png"
|
|
data="no name from data"
|
|
title="name from title"/>`,
|
|
expected: [
|
|
"test2 test3",
|
|
"test1",
|
|
"test4",
|
|
"name from alt",
|
|
"name from value",
|
|
"name from title",
|
|
],
|
|
},
|
|
{
|
|
id: "btn-image",
|
|
ruleset: "HTMLInputImageNoValidSrc",
|
|
markup: `
|
|
<span id="l1">test2</span>
|
|
<span id="l2">test3</span>
|
|
<label for="btn-image">test4</label>
|
|
<input id="btn-image"
|
|
type="image"
|
|
aria-label="test1"
|
|
aria-labelledby="l1 l2"
|
|
alt="name from alt"
|
|
value="name from value"
|
|
data="no name from data"
|
|
title="no name from title"/>`,
|
|
expected: [
|
|
"test2 test3",
|
|
"test1",
|
|
"test4",
|
|
"name from alt",
|
|
"name from value",
|
|
],
|
|
},
|
|
{
|
|
id: "opt",
|
|
ruleset: "HTMLOption",
|
|
markup: `
|
|
<span id="l1">test2</span>
|
|
<span id="l2">test3</span>
|
|
<select>
|
|
<option id="opt"
|
|
aria-label="test1"
|
|
aria-labelledby="l1 l2"
|
|
label="test4"
|
|
title="test5">option1</option>
|
|
<option>option2</option>
|
|
</select>`,
|
|
expected: ["test2 test3", "test1", "test4", "option1", "test5"],
|
|
},
|
|
{
|
|
id: "img",
|
|
ruleset: "HTMLImg",
|
|
markup: `
|
|
<span id="l1">test2</span>
|
|
<span id="l2">test3</span>
|
|
<img id="img"
|
|
aria-label="Logo of Mozilla"
|
|
aria-labelledby="l1 l2"
|
|
alt="Mozilla logo"
|
|
title="This is a logo"
|
|
src="http://example.com/a11y/accessible/tests/mochitest/moz.png"/>`,
|
|
expected: [
|
|
"test2 test3",
|
|
"Logo of Mozilla",
|
|
"Mozilla logo",
|
|
"This is a logo",
|
|
],
|
|
},
|
|
{
|
|
id: "imgemptyalt",
|
|
ruleset: "HTMLImgEmptyAlt",
|
|
markup: `
|
|
<span id="l1">test2</span>
|
|
<span id="l2">test3</span>
|
|
<img id="imgemptyalt"
|
|
aria-label="Logo of Mozilla"
|
|
aria-labelledby="l1 l2"
|
|
title="This is a logo"
|
|
alt=""
|
|
src="http://example.com/a11y/accessible/tests/mochitest/moz.png"/>`,
|
|
expected: ["test2 test3", "Logo of Mozilla", "This is a logo", ""],
|
|
},
|
|
{
|
|
id: "tc",
|
|
ruleset: "HTMLElm",
|
|
markup: `
|
|
<span id="l1">test2</span>
|
|
<span id="l2">test3</span>
|
|
<label for="tc">test4</label>
|
|
<table>
|
|
<tr>
|
|
<td id="tc"
|
|
aria-label="test1"
|
|
aria-labelledby="l1 l2"
|
|
title="test5">
|
|
<p>This is a paragraph</p>
|
|
<a href="#">This is a link</a>
|
|
<ul>
|
|
<li>This is a list</li>
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
</table>`,
|
|
expected: ["test2 test3", "test1", "test5"],
|
|
},
|
|
{
|
|
id: "gc",
|
|
ruleset: "HTMLARIAGridCell",
|
|
markup: `
|
|
<span id="l1">test2</span>
|
|
<span id="l2">test3</span>
|
|
<label for="gc">test4</label>
|
|
<table>
|
|
<tr>
|
|
<td id="gc"
|
|
role="gridcell"
|
|
aria-label="test1"
|
|
aria-labelledby="l1 l2"
|
|
title="This is a paragraph This is a link This is a list">
|
|
<p>This is a paragraph</p>
|
|
<a href="#">This is a link</a>
|
|
<ul>
|
|
<li>Listitem1</li>
|
|
<li>Listitem2</li>
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
</table>`,
|
|
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: `
|
|
<span id="l1">lby_tst6_1</span>
|
|
<span id="l2">lby_tst6_2</span>
|
|
<label for="t">label_tst6</label>
|
|
<table id="t"
|
|
aria-label="arialabel_tst6"
|
|
aria-labelledby="l1 l2"
|
|
summary="summary_tst6"
|
|
title="title_tst6">
|
|
<caption>caption_tst6</caption>
|
|
<tr>
|
|
<td>cell1</td>
|
|
<td>cell2</td>
|
|
</tr>
|
|
</table>`,
|
|
expected: [
|
|
"lby_tst6_1 lby_tst6_2",
|
|
"arialabel_tst6",
|
|
"caption_tst6",
|
|
"summary_tst6",
|
|
"title_tst6",
|
|
],
|
|
},
|
|
{
|
|
id: "btn",
|
|
ruleset: "CSSContent",
|
|
markup: `
|
|
<div role="main">
|
|
<style>
|
|
button::before {
|
|
content: "do not ";
|
|
}
|
|
</style>
|
|
<button id="btn">press me</button>
|
|
</div>`,
|
|
expected: ["do not press me", "press me"],
|
|
},
|
|
{
|
|
// TODO: uncomment when Bug-1256382 is resoved.
|
|
// id: 'li',
|
|
// ruleset: 'CSSContent',
|
|
// markup: `
|
|
// <style>
|
|
// ul {
|
|
// list-style-type: decimal;
|
|
// }
|
|
// </style>
|
|
// <ul id="ul">
|
|
// <li id="li">Listitem</li>
|
|
// </ul>`,
|
|
// expected: ['1. Listitem', `${String.fromCharCode(0x2022)} Listitem`]
|
|
// }, {
|
|
id: "a",
|
|
ruleset: "HTMLLink",
|
|
markup: `
|
|
<span id="l1">test2</span>
|
|
<span id="l2">test3</span>
|
|
<a id="a"
|
|
aria-label="test1"
|
|
aria-labelledby="l1 l2"
|
|
title="test4">test5</a>`,
|
|
expected: ["test2 test3", "test1", "test5", "test4"],
|
|
},
|
|
{
|
|
id: "a-img",
|
|
ruleset: "HTMLLinkImage",
|
|
markup: `
|
|
<span id="l1">test2</span>
|
|
<span id="l2">test3</span>
|
|
<a id="a-img"
|
|
aria-label="test1"
|
|
aria-labelledby="l1 l2"
|
|
title="test4"><img alt="test5"/></a>`,
|
|
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, parent, id } structure that contains an
|
|
* accessible, its parent 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, parent, acc } = target;
|
|
let { waitFor, attr } = rule;
|
|
|
|
testName(acc, expected);
|
|
|
|
if (waitFor) {
|
|
let [event] = await contentSpawnMutation(
|
|
browser,
|
|
{
|
|
expected: [[waitFor, waitFor === EVENT_REORDER ? parent : id]],
|
|
},
|
|
(contentId, contentAttr) =>
|
|
content.document.getElementById(contentId).removeAttribute(contentAttr),
|
|
[id, attr]
|
|
);
|
|
|
|
// Update accessible just in case it is now defunct.
|
|
target.acc = findAccessibleChildByID(event.accessible, id);
|
|
} else {
|
|
await invokeSetAttribute(browser, id, attr);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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, parent, id } structure that contains an
|
|
* accessible, its parent 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, parent, acc } = target;
|
|
let { isSibling, elm } = rule;
|
|
|
|
testName(acc, expected);
|
|
let [event] = await contentSpawnMutation(
|
|
browser,
|
|
{
|
|
expected: [[EVENT_REORDER, isSibling ? parent : id]],
|
|
},
|
|
contentElm => content.document.querySelector(`${contentElm}`).remove(),
|
|
[elm]
|
|
);
|
|
|
|
// 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, parent, id } structure that contains an
|
|
* accessible, its parent 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 [event] = await contentSpawnMutation(
|
|
browser,
|
|
{
|
|
expected: [[EVENT_REORDER, id]],
|
|
},
|
|
contentId => {
|
|
let elm = content.document.getElementById(contentId);
|
|
while (elm.firstChild) {
|
|
elm.firstChild.remove();
|
|
}
|
|
},
|
|
[id]
|
|
);
|
|
|
|
// 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, parent, id } structure that contains an
|
|
* accessible, its parent 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, topic, data) {
|
|
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);
|
|
// Find target's parent accessible from an accessible subtree.
|
|
let parent = getAccessibleDOMNodeID(acc.parent);
|
|
let target = { id, parent, acc };
|
|
await testNameRule(browser, target, rules[ruleset], expected);
|
|
Services.obs.removeObserver(observer, "accessible-event");
|
|
},
|
|
{ iframe: true, remoteIframe: true }
|
|
)
|
|
);
|