genUiSemanticsAudit function
Reads a recording made by genUiSemantics and reports what a person using a screen reader could not work with.
Written to run over the same recording the golden test keeps, so a catalog gets its accessibility checked by the file it already has rather than by a second pass nobody remembers to run.
final findings = genUiSemanticsAudit(recorded);
expect(findings, isEmpty, reason: findings.join('\n'));
allowEmpty names the components that are decoration and are meant to
expose nothing, so that the rule stays sharp for every other component.
Implementation
List<GenUiAuditFinding> genUiSemanticsAudit(
Map<String, List<GenUiSemanticNode>> recorded, {
Set<String> allowEmpty = const <String>{},
}) {
final findings = <GenUiAuditFinding>[];
for (final entry in recorded.entries) {
final String component = entry.key;
final List<GenUiSemanticNode> nodes = entry.value;
if (nodes.isEmpty) {
if (!allowEmpty.contains(component)) {
findings.add(
GenUiAuditFinding(
GenUiAuditRule.exposesNothing,
component: component,
),
);
}
continue;
}
final controlNames = <String>[];
for (final node in nodes) {
final bool operable = node.actions.any(_operableActions.contains);
// A tooltip counts as a name here. It is weaker than one — Android
// announces it, other platforms are less reliable — and the recording
// shows which of the two a control has, so the distinction stays
// visible without failing a build over it.
if (node.name.isEmpty && node.tooltip.isEmpty) {
if (operable) {
findings.add(
GenUiAuditFinding(
GenUiAuditRule.unnamedControl,
component: component,
detail: node.role,
),
);
} else if (node.role != 'text' && node.role != 'group') {
findings.add(
GenUiAuditFinding(
GenUiAuditRule.unnamedNode,
component: component,
detail: node.role,
),
);
}
} else if (operable) {
controlNames.add(node.name.isEmpty ? node.tooltip : node.name);
}
}
final seen = <String>{};
for (final name in controlNames) {
if (!seen.add(name)) {
findings.add(
GenUiAuditFinding(
GenUiAuditRule.ambiguousControls,
component: component,
detail: name,
),
);
}
}
}
return findings;
}