diff --git a/lib/rules/template-no-invalid-aria-attributes.js b/lib/rules/template-no-invalid-aria-attributes.js index 4a5c7a2a48..6eead8bf76 100644 --- a/lib/rules/template-no-invalid-aria-attributes.js +++ b/lib/rules/template-no-invalid-aria-attributes.js @@ -11,16 +11,22 @@ function isNumeric(value) { return !Number.isNaN(Number(value)); } -function isValidAriaValue(attrName, value) { - const attrDef = aria.get(attrName); - if (!attrDef) { - return true; - } +// In aria-query 5.3.2, `allowundefined: true` is set only on the four +// boolean-like ARIA state attributes — `aria-expanded`, `aria-hidden`, +// `aria-grabbed`, `aria-selected` — whose WAI-ARIA 1.2 value tables list +// the literal string `"undefined"` as a spec-valid value meaning "state +// is not applicable" (e.g. https://www.w3.org/TR/wai-aria-1.2/#aria-expanded). +// The flag is nominally type-agnostic, but in practice this function only +// green-lights `"undefined"` for that boolean-like subset; no non-boolean +// ARIA attribute in aria-query currently sets `allowundefined`. +function allowsUndefinedLiteral(attrDef, value) { + return value === 'undefined' && Boolean(attrDef.allowundefined); +} - if (value === 'undefined') { - return Boolean(attrDef.allowundefined); +function validateByType(attrDef, value) { + if (allowsUndefinedLiteral(attrDef, value)) { + return true; } - switch (attrDef.type) { case 'boolean': { return isBoolean(value); @@ -45,7 +51,9 @@ function isValidAriaValue(attrName, value) { return isNumeric(value) && !isBoolean(value); } case 'token': { - // aria-query stores boolean values as actual booleans, convert for comparison + // aria-query stores boolean values as actual booleans; stringify for comparison. + // The string literal 'undefined' that appears in some values arrays (e.g. + // aria-orientation) passes through this check naturally — no special-casing. const permittedValues = attrDef.values.map((v) => typeof v === 'boolean' ? v.toString() : v ); @@ -60,6 +68,14 @@ function isValidAriaValue(attrName, value) { } } +function isValidAriaValue(attrName, value) { + const attrDef = aria.get(attrName); + if (!attrDef) { + return true; + } + return validateByType(attrDef, value); +} + function getExpectedTypeDescription(attrName) { const attrDef = aria.get(attrName); if (!attrDef) { diff --git a/tests/lib/rules/template-no-invalid-aria-attributes.js b/tests/lib/rules/template-no-invalid-aria-attributes.js index 7f8e10b8e6..163792bf3e 100644 --- a/tests/lib/rules/template-no-invalid-aria-attributes.js +++ b/tests/lib/rules/template-no-invalid-aria-attributes.js @@ -31,7 +31,24 @@ ruleTester.run('template-no-invalid-aria-attributes', rule, { '', '', '', + // Boolean-type attributes with allowundefined: true per aria-query — the + // string "undefined" is spec-valid (WAI-ARIA 1.2 value tables for these + // attrs list true/false/undefined). All 4 share the same code path. '', + '', + '', + '', + + // Token-type aria-orientation lists "undefined" in its values array; + // passes the natural token check (no special-casing needed). + '', + '', + + // aria-pressed is tristate WITHOUT allowundefined — string "undefined" + // is NOT accepted. Explicit valid values still work. + '', + '', + '', '', ], invalid: [ @@ -121,6 +138,18 @@ ruleTester.run('template-no-invalid-aria-attributes', rule, { output: null, errors: [{ messageId: 'invalidAriaAttributeValue' }], }, + { + code: '', + output: null, + errors: [{ messageId: 'invalidAriaAttributeValue' }], + }, + // aria-pressed is tristate WITHOUT allowundefined — string "undefined" + // is spec-invalid here (aria-query doesn't mark it allowundefined). + { + code: '', + output: null, + errors: [{ messageId: 'invalidAriaAttributeValue' }], + }, ], }); @@ -149,7 +178,21 @@ hbsRuleTester.run('template-no-invalid-aria-attributes', rule, { '', '', '
', + // Boolean-type attrs with allowundefined (spec-valid "undefined" literal): '
', + '
', + '
', + '
', + + // Token-type aria-orientation — "undefined" passes via values list: + '
', + '
', + + // aria-pressed is tristate WITHOUT allowundefined; valid values: + '', + '', + '', + '', ], invalid: [ @@ -223,5 +266,16 @@ hbsRuleTester.run('template-no-invalid-aria-attributes', rule, { output: null, errors: [{ messageId: 'invalidAriaAttributeValue' }], }, + { + code: '
', + output: null, + errors: [{ messageId: 'invalidAriaAttributeValue' }], + }, + // aria-pressed has no allowundefined — "undefined" is spec-invalid here. + { + code: '', + output: null, + errors: [{ messageId: 'invalidAriaAttributeValue' }], + }, ], });