Bug 1787284: [Part 4] Core tests for MEMBER_OF relation r=eeejay

Depends on D159119

Differential Revision: https://phabricator.services.mozilla.com/D159120
This commit is contained in:
Morgan Rae Reschenberg 2022-10-31 19:49:50 +00:00
parent 5ba1794a07
commit b0dedce119
4 changed files with 261 additions and 176 deletions

View File

@ -17,7 +17,7 @@ support-files =
# Caching tests
[browser_caching_actions.js]
skip-if =
skip-if =
(apple_catalina || apple_silicon) && !debug # Bug 1779541
[browser_caching_attributes.js]
[browser_caching_description.js]
@ -27,6 +27,7 @@ skip-if = os != 'win'
[browser_caching_name.js]
skip-if = (os == "linux" && bits == 64) || (debug && os == "mac") || (debug && os == "win") #Bug 1388256
[browser_caching_relations.js]
[browser_caching_relations_002.js]
[browser_caching_states.js]
[browser_caching_table.js]
[browser_caching_text.js]

View File

@ -5,9 +5,6 @@
"use strict";
requestLongerTimeout(2);
/* import-globals-from ../../mochitest/relations.js */
loadScripts({ name: "relations.js", dir: MOCHITESTS_DIR });
/**
* A test specification that has the following format:
* [
@ -23,176 +20,6 @@ const attrRelationsSpec = [
["aria-flowto", RELATION_FLOWS_TO, RELATION_FLOWS_FROM],
];
/**
* Test the accessible relation.
*
* @param identifier [in] identifier to get an accessible, may be ID
* attribute or DOM element or accessible object
* @param relType [in] relation type (see constants above)
* @param relatedIdentifiers [in] identifier or array of identifiers of
* expected related accessibles
*/
async function testCachedRelation(identifier, relType, relatedIdentifiers) {
const relDescr = getRelationErrorMsg(identifier, relType);
const relDescrStart = getRelationErrorMsg(identifier, relType, true);
info(`Testing ${relDescr}`);
if (!relatedIdentifiers) {
await untilCacheOk(function() {
let r = getRelationByType(identifier, relType);
if (r) {
info(`Fetched ${r.targetsCount} relations from cache`);
} else {
info("Could not fetch relations");
}
return r && !r.targetsCount;
}, relDescrStart + " has no targets, as expected");
return;
}
const relatedIds =
relatedIdentifiers instanceof Array
? relatedIdentifiers
: [relatedIdentifiers];
await untilCacheOk(function() {
let r = getRelationByType(identifier, relType);
if (r) {
info(
`Fetched ${r.targetsCount} relations from cache, looking for ${relatedIds.length}`
);
} else {
info("Could not fetch relations");
}
return r && r.targetsCount == relatedIds.length;
}, "Found correct number of expected relations");
let targets = [];
for (let idx = 0; idx < relatedIds.length; idx++) {
targets.push(getAccessible(relatedIds[idx]));
}
if (targets.length != relatedIds.length) {
return;
}
await untilCacheOk(function() {
const relation = getRelationByType(identifier, relType);
const actualTargets = relation ? relation.getTargets() : null;
if (!actualTargets) {
info("Could not fetch relations");
return false;
}
// Check if all given related accessibles are targets of obtained relation.
for (let idx = 0; idx < targets.length; idx++) {
let isFound = false;
for (let relatedAcc of actualTargets.enumerate(Ci.nsIAccessible)) {
if (targets[idx] == relatedAcc) {
isFound = true;
break;
}
}
if (!isFound) {
info(
prettyName(relatedIds[idx]) +
" could not be found in relation: " +
relDescr
);
return false;
}
}
return true;
}, "All given related accessibles are targets of fetched relation.");
await untilCacheOk(function() {
const relation = getRelationByType(identifier, relType);
const actualTargets = relation ? relation.getTargets() : null;
if (!actualTargets) {
info("Could not fetch relations");
return false;
}
// Check if all obtained targets are given related accessibles.
for (let relatedAcc of actualTargets.enumerate(Ci.nsIAccessible)) {
let wasFound = false;
for (let idx = 0; idx < targets.length; idx++) {
if (relatedAcc == targets[idx]) {
wasFound = true;
}
}
if (!wasFound) {
info(
prettyName(relatedAcc) +
" was found, but shouldn't be in relation: " +
relDescr
);
return false;
}
}
return true;
}, "No unexpected targets found.");
}
async function testRelated(
browser,
accDoc,
attr,
hostRelation,
dependantRelation
) {
let host = findAccessibleChildByID(accDoc, "host");
let dependant1 = findAccessibleChildByID(accDoc, "dependant1");
let dependant2 = findAccessibleChildByID(accDoc, "dependant2");
/**
* Test data has the format of:
* {
* desc {String} description for better logging
* attrs {?Array} an optional list of attributes to update
* expected {Array} expected relation values for dependant1, dependant2
* and host respectively.
* }
*/
const tests = [
{
desc: "No attribute",
expected: [null, null, null],
},
{
desc: "Set attribute",
attrs: [{ key: attr, value: "dependant1" }],
expected: [host, null, dependant1],
},
{
desc: "Change attribute",
attrs: [{ key: attr, value: "dependant2" }],
expected: [null, host, dependant2],
},
{
desc: "Remove attribute",
attrs: [{ key: attr }],
expected: [null, null, null],
},
];
for (let { desc, attrs, expected } of tests) {
info(desc);
if (attrs) {
for (let { key, value } of attrs) {
await invokeSetAttribute(browser, "host", key, value);
}
}
await testCachedRelation(dependant1, dependantRelation, expected[0]);
await testCachedRelation(dependant2, dependantRelation, expected[1]);
await testCachedRelation(host, hostRelation, expected[2]);
}
}
/**
* Test caching of relations between accessible objects.
*/

View File

@ -0,0 +1,83 @@
/* 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";
/**
* Test MEMBER_OF relation caching on HTML radio buttons
*/
addAccessibleTask(
`
<input type="radio" id="r1">I have no name<br>
<input type="radio" id="r2">I also have no name<br>
<input type="radio" id="r3" name="n">I have a name<br>
<input type="radio" id="r4" name="a">I have a different name<br>
<fieldset role="radiogroup">
<input type="radio" id="r5" name="n">I have an already used name
and am in a different part of the tree
<input type="radio" id="r6" name="r">I have a different name but am
in the same group
</fieldset>`,
async function(browser, accDoc) {
const r1 = findAccessibleChildByID(accDoc, "r1");
const r2 = findAccessibleChildByID(accDoc, "r2");
const r3 = findAccessibleChildByID(accDoc, "r3");
const r4 = findAccessibleChildByID(accDoc, "r4");
const r5 = findAccessibleChildByID(accDoc, "r5");
const r6 = findAccessibleChildByID(accDoc, "r6");
await testCachedRelation(r1, RELATION_MEMBER_OF, null);
await testCachedRelation(r2, RELATION_MEMBER_OF, null);
await testCachedRelation(r3, RELATION_MEMBER_OF, [r3, r5]);
await testCachedRelation(r4, RELATION_MEMBER_OF, r4);
await testCachedRelation(r5, RELATION_MEMBER_OF, [r3, r5]);
await testCachedRelation(r6, RELATION_MEMBER_OF, r6);
await invokeContentTask(browser, [], () => {
content.document.getElementById("r5").name = "a";
});
await testCachedRelation(r3, RELATION_MEMBER_OF, r3);
await testCachedRelation(r4, RELATION_MEMBER_OF, [r5, r4]);
await testCachedRelation(r5, RELATION_MEMBER_OF, [r5, r4]);
},
{ chrome: true, iframe: true, remoteIframe: true }
);
/*
* Test MEMBER_OF relation caching on aria radio buttons
*/
addAccessibleTask(
`
<div role="radio" id="r1">I have no radio group</div><br>
<fieldset role="radiogroup" id="fs">
<div role="radio" id="r2">hello</div><br>
<div role="radio" id="r3">world</div><br>
</fieldset>`,
async function(browser, accDoc) {
const r1 = findAccessibleChildByID(accDoc, "r1");
const r2 = findAccessibleChildByID(accDoc, "r2");
let r3 = findAccessibleChildByID(accDoc, "r3");
await testCachedRelation(r1, RELATION_MEMBER_OF, null);
await testCachedRelation(r2, RELATION_MEMBER_OF, [r2, r3]);
await testCachedRelation(r3, RELATION_MEMBER_OF, [r2, r3]);
const r = waitForEvent(EVENT_INNER_REORDER, "fs");
await invokeContentTask(browser, [], () => {
let innerRadio = content.document.getElementById("r3");
content.document.body.appendChild(innerRadio);
});
await r;
r3 = findAccessibleChildByID(accDoc, "r3");
await testCachedRelation(r1, RELATION_MEMBER_OF, null);
await testCachedRelation(r2, RELATION_MEMBER_OF, r2);
await testCachedRelation(r3, RELATION_MEMBER_OF, null);
},
{
chrome: true,
iframe: true,
remoteIframe: true,
}
);

View File

@ -4,6 +4,8 @@
"use strict";
/* exported testCachedRelation, testRelated */
// Load the shared-head file first.
/* import-globals-from ../shared-head.js */
Services.scriptloader.loadSubScript(
@ -12,8 +14,180 @@ Services.scriptloader.loadSubScript(
);
// Loading and common.js from accessible/tests/mochitest/ for all tests, as
// well as promisified-events.js.
// well as promisified-events.js and relations.js.
/* import-globals-from ../../mochitest/relations.js */
loadScripts(
{ name: "common.js", dir: MOCHITESTS_DIR },
{ name: "promisified-events.js", dir: MOCHITESTS_DIR }
{ name: "promisified-events.js", dir: MOCHITESTS_DIR },
{ name: "relations.js", dir: MOCHITESTS_DIR }
);
/**
* Test the accessible relation.
*
* @param identifier [in] identifier to get an accessible, may be ID
* attribute or DOM element or accessible object
* @param relType [in] relation type (see constants above)
* @param relatedIdentifiers [in] identifier or array of identifiers of
* expected related accessibles
*/
async function testCachedRelation(identifier, relType, relatedIdentifiers) {
const relDescr = getRelationErrorMsg(identifier, relType);
const relDescrStart = getRelationErrorMsg(identifier, relType, true);
info(`Testing ${relDescr}`);
if (!relatedIdentifiers) {
await untilCacheOk(function() {
let r = getRelationByType(identifier, relType);
if (r) {
info(`Fetched ${r.targetsCount} relations from cache`);
} else {
info("Could not fetch relations");
}
return r && !r.targetsCount;
}, relDescrStart + " has no targets, as expected");
return;
}
const relatedIds =
relatedIdentifiers instanceof Array
? relatedIdentifiers
: [relatedIdentifiers];
await untilCacheOk(function() {
let r = getRelationByType(identifier, relType);
if (r) {
info(
`Fetched ${r.targetsCount} relations from cache, looking for ${relatedIds.length}`
);
} else {
info("Could not fetch relations");
}
return r && r.targetsCount == relatedIds.length;
}, "Found correct number of expected relations");
let targets = [];
for (let idx = 0; idx < relatedIds.length; idx++) {
targets.push(getAccessible(relatedIds[idx]));
}
if (targets.length != relatedIds.length) {
return;
}
await untilCacheOk(function() {
const relation = getRelationByType(identifier, relType);
const actualTargets = relation ? relation.getTargets() : null;
if (!actualTargets) {
info("Could not fetch relations");
return false;
}
// Check if all given related accessibles are targets of obtained relation.
for (let idx = 0; idx < targets.length; idx++) {
let isFound = false;
for (let relatedAcc of actualTargets.enumerate(Ci.nsIAccessible)) {
if (targets[idx] == relatedAcc) {
isFound = true;
break;
}
}
if (!isFound) {
info(
prettyName(relatedIds[idx]) +
" could not be found in relation: " +
relDescr
);
return false;
}
}
return true;
}, "All given related accessibles are targets of fetched relation.");
await untilCacheOk(function() {
const relation = getRelationByType(identifier, relType);
const actualTargets = relation ? relation.getTargets() : null;
if (!actualTargets) {
info("Could not fetch relations");
return false;
}
// Check if all obtained targets are given related accessibles.
for (let relatedAcc of actualTargets.enumerate(Ci.nsIAccessible)) {
let wasFound = false;
for (let idx = 0; idx < targets.length; idx++) {
if (relatedAcc == targets[idx]) {
wasFound = true;
}
}
if (!wasFound) {
info(
prettyName(relatedAcc) +
" was found, but shouldn't be in relation: " +
relDescr
);
return false;
}
}
return true;
}, "No unexpected targets found.");
}
async function testRelated(
browser,
accDoc,
attr,
hostRelation,
dependantRelation
) {
let host = findAccessibleChildByID(accDoc, "host");
let dependant1 = findAccessibleChildByID(accDoc, "dependant1");
let dependant2 = findAccessibleChildByID(accDoc, "dependant2");
/**
* Test data has the format of:
* {
* desc {String} description for better logging
* attrs {?Array} an optional list of attributes to update
* expected {Array} expected relation values for dependant1, dependant2
* and host respectively.
* }
*/
const tests = [
{
desc: "No attribute",
expected: [null, null, null],
},
{
desc: "Set attribute",
attrs: [{ key: attr, value: "dependant1" }],
expected: [host, null, dependant1],
},
{
desc: "Change attribute",
attrs: [{ key: attr, value: "dependant2" }],
expected: [null, host, dependant2],
},
{
desc: "Remove attribute",
attrs: [{ key: attr }],
expected: [null, null, null],
},
];
for (let { desc, attrs, expected } of tests) {
info(desc);
if (attrs) {
for (let { key, value } of attrs) {
await invokeSetAttribute(browser, "host", key, value);
}
}
await testCachedRelation(dependant1, dependantRelation, expected[0]);
await testCachedRelation(dependant2, dependantRelation, expected[1]);
await testCachedRelation(host, hostRelation, expected[2]);
}
}