ENG-1747: Test 925 False Negative on A Elements with role=link

Bug (ENG-1747): Access Engine test 925 (an A element must not carry an alt attribute) scoped its candidate set to a[data-ae_ar="null"][data-ae_vis][data-ae_avat] — anchors with no ARIA role at all. Any role attribute took the anchor out of scope, including role="link", which does not change what the element is. Markup such as <a href="foo" role="link" alt="should not be here">Testing this out</a> was silently skipped — a false negative.

Impact: alt is not a valid attribute on A. Authors add it expecting it to name the link, but browsers ignore it, so the link is often left with a misleading or missing accessible name. Those mistakes went unreported on any link that also spelled out role="link".

Test Logic (test 925):

Common Scenarios:

Fix: Extend the candidate set with the computed-role attribute added in ENG-1730 (data-ae_computed_role, the explicit-or-implicit ARIA role):

a[data-ae_ar="null"][data-ae_vis][data-ae_avat], a[data-ae_computed_role="link"][data-ae_vis][data-ae_avat]

with [alt] appended to each branch for the failing set. The computed-role branch matches role="link" in any letter case, and a role list whose first valid token is link (role="foo link"), mirroring browser behaviour. The original data-ae_ar="null" branch is retained so a role-less A without an href (implicit role "") stays in scope exactly as before — the change only adds coverage, it never removes it.
N/A — out of scope
PASS — not flagged (correct)
FAIL — flagged (correct; the role=link cases are the ENG-1747 repro, the rest are regression guards)

N/A Tests — Test 925 Does Not Apply

Anchor with role="button" and an alt attribute other role

Test

The anchor is exposed as a button, not a link, so test 925 does not apply. Only role=link (or no role) is in scope. Should not be flagged.

Anchor with role="img" and an alt attribute other role

Test

An anchor exposed as an image is out of scope for this test, unchanged by ENG-1747. Should not be flagged.

Anchor with role="link" hidden with display:none hidden

(Anchor is present but hidden via display:none.)

Test 925 only evaluates elements that are not intentionally hidden: the selector requires [data-ae_vis]. Should not be flagged.

Anchor with role="link" and aria-hidden="true" not exposed

Not available to assistive technologies, so the selector's [data-ae_avat] requirement excludes it. Should not be flagged.

PASS Tests — In Scope, No Violation

Baseline: role-less anchor with no alt attribute no role

Test

In scope, carries no alt. Should not be flagged.

Anchor with role="link" and no alt attribute role=link

Test

Newly in scope after ENG-1747, and compliant. Should not be flagged.

Anchor with role="link" wrapping an image that carries the alt alt on img

A goat on a hillside

Correct markup: the alt belongs to the img, not the anchor. Test 925 looks only at alt on the A itself. Should not be flagged.

FAIL Tests — Correctly Flagged

ENG-1747 repro cases — missed before the fix

Ticket repro: role="link" anchor with a populated alt role=link

Testing this out

The markup from ENG-1747. Pre-fix: not flagged ❌. Post-fix: flagged ✓.

role="link" anchor with an empty alt empty alt

Test

An empty alt is still an alt on an element that does not define it. Pre-fix: not flagged ❌. Post-fix: flagged ✓.

Uppercase role="LINK" anchor with an alt case

Test

ARIA role tokens are case-insensitive and the computed role is normalised to lower case. Pre-fix: not flagged ❌. Post-fix: flagged ✓.

Multi-token role="foo link" anchor with an alt token list

Test

Browsers skip unknown tokens and use the first valid one, so this element is a link. The computed role resolves to link even though the recorded data-ae_ar is foo. Pre-fix: not flagged ❌. Post-fix: flagged ✓. (Tests 37 and 412 also flag this element for the invalid foo token — expected and unrelated to test 925.)

role="link" anchor with no href and an alt no href

Test

Without href the anchor has no implicit link role, but the explicit role="link" makes it one. Pre-fix: not flagged ❌. Post-fix: flagged ✓. (Test 1119 also flags this element for having neither href nor a tabindex — expected and unrelated to test 925.)

Regression guards — flagged before and after the fix

Role-less anchor with an alt no role

Test

The original test 925 case. Must stay flagged ✓.

Role-less anchor with no href and an alt no role, no href

Test

Its computed role is empty (no href), so it is only in scope through the retained data-ae_ar="null" branch. This case guards against narrowing the selector to the computed role alone. Must stay flagged ✓. (Test 1119 also flags it for having neither href nor a tabindex — expected and unrelated to test 925.)

Real-World Scenarios

Image link where the alt was put on the anchor instead of the image role=link

The most common form of this mistake: the image is left without a name and the anchor carries an attribute browsers ignore. Pre-fix: not flagged ❌. Post-fix: flagged ✓. (Test 1386 flags the nested image separately for having no accessible name — that is the other half of the same authoring mistake.)

Templated media card spraying alt across the block role=link

Antelope print Antelope print

A template applies the same alt to every element in the card, including the anchor. Pre-fix: not flagged ❌. Post-fix: flagged ✓.