diff --git a/build/tasks/aria-supported.js b/build/tasks/aria-supported.js index 3a793cc48b..c989fa4f70 100644 --- a/build/tasks/aria-supported.js +++ b/build/tasks/aria-supported.js @@ -46,18 +46,30 @@ module.exports = function(grunt) { .reduce((out, [key] = item) => { switch (listType) { case 'supported': - if (subject.includes(key)) { + if ( + subject.hasOwnProperty(key) && + subject[key].unsupported === false + ) { out.push([`${key}`, 'Yes']); } break; case 'unsupported': - if (!subject.includes(key)) { + if ( + (subject[key] && subject[key].unsupported === true) || + !subject.hasOwnProperty(key) + ) { out.push([`${key}`, 'No']); } break; case 'all': default: - out.push([`${key}`, subject.includes(key) ? 'Yes' : 'No']); + out.push([ + `${key}`, + subject.hasOwnProperty(key) && + subject[key].unsupported === false + ? 'Yes' + : 'No' + ]); break; } return out; @@ -75,14 +87,11 @@ module.exports = function(grunt) { } in axe-core.`, mdTable([ ['aria-role', 'axe-core support'], - ...getDiff(roles, Object.keys(axe.commons.aria.lookupTable.role)) + ...getDiff(roles, axe.commons.aria.lookupTable.role) ]), mdTable([ ['aria-attribute', 'axe-core support'], - ...getDiff( - aQaria, - Object.keys(axe.commons.aria.lookupTable.attributes) - ) + ...getDiff(aQaria, axe.commons.aria.lookupTable.attributes) ]) ); grunt.file.write(destFile, content); diff --git a/lib/checks/aria/unsupportedrole.js b/lib/checks/aria/unsupportedrole.js new file mode 100644 index 0000000000..04a5e364b1 --- /dev/null +++ b/lib/checks/aria/unsupportedrole.js @@ -0,0 +1,3 @@ +return !axe.commons.aria.isValidRole(node.getAttribute('role'), { + flagUnsupported: true +}); diff --git a/lib/checks/aria/unsupportedrole.json b/lib/checks/aria/unsupportedrole.json new file mode 100644 index 0000000000..48e738f6a6 --- /dev/null +++ b/lib/checks/aria/unsupportedrole.json @@ -0,0 +1,11 @@ +{ + "id": "unsupportedrole", + "evaluate": "unsupportedrole.js", + "metadata": { + "impact": "critical", + "messages": { + "pass": "ARIA role is supported", + "fail": "The role used is not widely supported in assistive technologies" + } + } +} diff --git a/lib/commons/aria/index.js b/lib/commons/aria/index.js index a89ea63900..1b2e22bb7c 100644 --- a/lib/commons/aria/index.js +++ b/lib/commons/aria/index.js @@ -200,7 +200,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, alertdialog: { type: 'widget', @@ -209,7 +210,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, application: { type: 'landmark', @@ -218,7 +220,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, article: { type: 'structure', @@ -233,7 +236,8 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['article'] + implicit: ['article'], + unsupported: false }, banner: { type: 'landmark', @@ -243,7 +247,8 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['header'] + implicit: ['header'], + unsupported: false }, button: { type: 'widget', @@ -260,7 +265,8 @@ lookupTable.role = { 'input[type="reset"]', 'input[type="submit"]', 'summary' - ] + ], + unsupported: false }, cell: { type: 'structure', @@ -276,7 +282,8 @@ lookupTable.role = { owned: null, nameFrom: ['author', 'contents'], context: ['row'], - implicit: ['td', 'th'] + implicit: ['td', 'th'], + unsupported: false }, checkbox: { type: 'widget', @@ -291,7 +298,8 @@ lookupTable.role = { owned: null, nameFrom: ['author', 'contents'], context: null, - implicit: ['input[type="checkbox"]'] + implicit: ['input[type="checkbox"]'], + unsupported: false }, columnheader: { type: 'structure', @@ -312,7 +320,8 @@ lookupTable.role = { owned: null, nameFrom: ['author', 'contents'], context: ['row'], - implicit: ['th'] + implicit: ['th'], + unsupported: false }, combobox: { type: 'composite', @@ -330,11 +339,13 @@ lookupTable.role = { all: ['listbox', 'textbox'] }, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, command: { nameFrom: ['author'], - type: 'abstract' + type: 'abstract', + unsupported: false }, complementary: { type: 'landmark', @@ -344,11 +355,13 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['aside'] + implicit: ['aside'], + unsupported: false }, composite: { nameFrom: ['author'], - type: 'abstract' + type: 'abstract', + unsupported: false }, contentinfo: { type: 'landmark', @@ -358,7 +371,8 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['footer'] + implicit: ['footer'], + unsupported: false }, definition: { type: 'structure', @@ -368,7 +382,8 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['dd', 'dfn'] + implicit: ['dd', 'dfn'], + unsupported: false }, dialog: { type: 'widget', @@ -378,7 +393,8 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['dialog'] + implicit: ['dialog'], + unsupported: false }, directory: { type: 'structure', @@ -387,7 +403,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author', 'contents'], - context: null + context: null, + unsupported: false }, document: { type: 'structure', @@ -397,7 +414,8 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['body'] + implicit: ['body'], + unsupported: false }, 'doc-abstract': { type: 'section', @@ -406,7 +424,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-acknowledgments': { type: 'landmark', @@ -415,7 +434,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-afterword': { type: 'landmark', @@ -424,7 +444,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-appendix': { type: 'landmark', @@ -433,7 +454,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-backlink': { type: 'link', @@ -442,7 +464,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author', 'contents'], - context: null + context: null, + unsupported: false }, 'doc-biblioentry': { type: 'listitem', @@ -457,7 +480,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author'], - context: ['doc-bibliography'] + context: ['doc-bibliography'], + unsupported: false }, 'doc-bibliography': { type: 'landmark', @@ -466,7 +490,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-biblioref': { type: 'link', @@ -475,7 +500,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author', 'contents'], - context: null + context: null, + unsupported: false }, 'doc-chapter': { type: 'landmark', @@ -484,7 +510,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-colophon': { type: 'section', @@ -493,7 +520,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-conclusion': { type: 'landmark', @@ -502,7 +530,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-cover': { type: 'img', @@ -511,7 +540,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-credit': { type: 'section', @@ -520,7 +550,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-credits': { type: 'landmark', @@ -529,7 +560,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-dedication': { type: 'section', @@ -538,7 +570,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-endnote': { type: 'listitem', @@ -553,7 +586,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: ['doc-endnotes'] + context: ['doc-endnotes'], + unsupported: false }, 'doc-endnotes': { type: 'landmark', @@ -562,7 +596,8 @@ lookupTable.role = { }, owned: ['doc-endnote'], namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-epigraph': { type: 'section', @@ -571,7 +606,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-epilogue': { type: 'landmark', @@ -580,7 +616,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-errata': { type: 'landmark', @@ -589,7 +626,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-example': { type: 'section', @@ -598,7 +636,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-footnote': { type: 'section', @@ -607,7 +646,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-foreword': { type: 'landmark', @@ -616,7 +656,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-glossary': { type: 'landmark', @@ -625,7 +666,8 @@ lookupTable.role = { }, owned: ['term', 'definition'], namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-glossref': { type: 'link', @@ -634,7 +676,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author', 'contents'], - context: null + context: null, + unsupported: false }, 'doc-index': { type: 'navigation', @@ -643,7 +686,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-introduction': { type: 'landmark', @@ -652,7 +696,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-noteref': { type: 'link', @@ -661,7 +706,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author', 'contents'], - context: null + context: null, + unsupported: false }, 'doc-notice': { type: 'note', @@ -670,7 +716,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-pagebreak': { type: 'separator', @@ -679,7 +726,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-pagelist': { type: 'navigation', @@ -688,7 +736,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-part': { type: 'landmark', @@ -697,7 +746,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-preface': { type: 'landmark', @@ -706,7 +756,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-prologue': { type: 'landmark', @@ -715,7 +766,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-pullquote': { type: 'none', @@ -724,7 +776,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-qna': { type: 'section', @@ -733,7 +786,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-subtitle': { type: 'sectionhead', @@ -742,7 +796,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-tip': { type: 'note', @@ -751,7 +806,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, 'doc-toc': { type: 'navigation', @@ -760,7 +816,8 @@ lookupTable.role = { }, owned: null, namefrom: ['author'], - context: null + context: null, + unsupported: false }, feed: { type: 'structure', @@ -771,7 +828,12 @@ lookupTable.role = { one: ['article'] }, nameFrom: ['author'], - context: null + context: null, + unsupported: false + }, + figure: { + type: 'structure', + unsupported: true }, form: { type: 'landmark', @@ -781,7 +843,8 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['form'] + implicit: ['form'], + unsupported: false }, grid: { type: 'composite', @@ -802,7 +865,8 @@ lookupTable.role = { }, nameFrom: ['author'], context: null, - implicit: ['table'] + implicit: ['table'], + unsupported: false }, gridcell: { type: 'widget', @@ -822,7 +886,8 @@ lookupTable.role = { owned: null, nameFrom: ['author', 'contents'], context: ['row'], - implicit: ['td', 'th'] + implicit: ['td', 'th'], + unsupported: false }, group: { type: 'structure', @@ -832,7 +897,8 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['details', 'optgroup'] + implicit: ['details', 'optgroup'], + unsupported: false }, heading: { type: 'structure', @@ -843,7 +909,8 @@ lookupTable.role = { owned: null, nameFrom: ['author', 'contents'], context: null, - implicit: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] + implicit: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'], + unsupported: false }, img: { type: 'structure', @@ -853,15 +920,18 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['img'] + implicit: ['img'], + unsupported: false }, input: { nameFrom: ['author'], - type: 'abstract' + type: 'abstract', + unsupported: false }, landmark: { nameFrom: ['author'], - type: 'abstract' + type: 'abstract', + unsupported: false }, link: { type: 'widget', @@ -871,7 +941,8 @@ lookupTable.role = { owned: null, nameFrom: ['author', 'contents'], context: null, - implicit: ['a[href]'] + implicit: ['a[href]'], + unsupported: false }, list: { type: 'structure', @@ -883,7 +954,8 @@ lookupTable.role = { }, nameFrom: ['author'], context: null, - implicit: ['ol', 'ul', 'dl'] + implicit: ['ol', 'ul', 'dl'], + unsupported: false }, listbox: { type: 'composite', @@ -902,7 +974,8 @@ lookupTable.role = { }, nameFrom: ['author'], context: null, - implicit: ['select'] + implicit: ['select'], + unsupported: false }, listitem: { type: 'structure', @@ -918,7 +991,8 @@ lookupTable.role = { owned: null, nameFrom: ['author', 'contents'], context: ['list'], - implicit: ['li', 'dt'] + implicit: ['li', 'dt'], + unsupported: false }, log: { type: 'widget', @@ -927,7 +1001,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, main: { type: 'landmark', @@ -937,7 +1012,8 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['main'] + implicit: ['main'], + unsupported: false }, marquee: { type: 'widget', @@ -946,7 +1022,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, math: { type: 'structure', @@ -956,7 +1033,8 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['math'] + implicit: ['math'], + unsupported: false }, menu: { type: 'composite', @@ -973,7 +1051,8 @@ lookupTable.role = { }, nameFrom: ['author'], context: null, - implicit: ['menu[type="context"]'] + implicit: ['menu[type="context"]'], + unsupported: false }, menubar: { type: 'composite', @@ -987,7 +1066,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, menuitem: { type: 'widget', @@ -1002,7 +1082,8 @@ lookupTable.role = { owned: null, nameFrom: ['author', 'contents'], context: ['menu', 'menubar'], - implicit: ['menuitem[type="command"]'] + implicit: ['menuitem[type="command"]'], + unsupported: false }, menuitemcheckbox: { type: 'widget', @@ -1017,7 +1098,8 @@ lookupTable.role = { owned: null, nameFrom: ['author', 'contents'], context: ['menu', 'menubar'], - implicit: ['menuitem[type="checkbox"]'] + implicit: ['menuitem[type="checkbox"]'], + unsupported: false }, menuitemradio: { type: 'widget', @@ -1033,7 +1115,8 @@ lookupTable.role = { owned: null, nameFrom: ['author', 'contents'], context: ['menu', 'menubar'], - implicit: ['menuitem[type="radio"]'] + implicit: ['menuitem[type="radio"]'], + unsupported: false }, navigation: { type: 'landmark', @@ -1043,14 +1126,16 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['nav'] + implicit: ['nav'], + unsupported: false }, none: { type: 'structure', attributes: null, owned: null, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, note: { type: 'structure', @@ -1059,7 +1144,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, option: { type: 'widget', @@ -1075,14 +1161,16 @@ lookupTable.role = { owned: null, nameFrom: ['author', 'contents'], context: ['listbox'], - implicit: ['option'] + implicit: ['option'], + unsupported: false }, presentation: { type: 'structure', attributes: null, owned: null, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, progressbar: { type: 'widget', @@ -1099,7 +1187,8 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['progress'] + implicit: ['progress'], + unsupported: false }, radio: { type: 'widget', @@ -1116,7 +1205,8 @@ lookupTable.role = { owned: null, nameFrom: ['author', 'contents'], context: null, - implicit: ['input[type="radio"]'] + implicit: ['input[type="radio"]'], + unsupported: false }, radiogroup: { type: 'composite', @@ -1133,7 +1223,8 @@ lookupTable.role = { all: ['radio'] }, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, range: { nameFrom: ['author'], @@ -1151,10 +1242,12 @@ lookupTable.role = { 'section[aria-label]', 'section[aria-labelledby]', 'section[title]' - ] + ], + unsupported: false }, roletype: { - type: 'abstract' + type: 'abstract', + unsupported: false }, row: { type: 'structure', @@ -1174,7 +1267,8 @@ lookupTable.role = { }, nameFrom: ['author', 'contents'], context: ['rowgroup', 'grid', 'treegrid', 'table'], - implicit: ['tr'] + implicit: ['tr'], + unsupported: false }, rowgroup: { type: 'structure', @@ -1186,7 +1280,8 @@ lookupTable.role = { }, nameFrom: ['author', 'contents'], context: ['grid', 'table'], - implicit: ['tbody', 'thead', 'tfoot'] + implicit: ['tbody', 'thead', 'tfoot'], + unsupported: false }, rowheader: { type: 'structure', @@ -1207,7 +1302,8 @@ lookupTable.role = { owned: null, nameFrom: ['author', 'contents'], context: ['row'], - implicit: ['th'] + implicit: ['th'], + unsupported: false }, scrollbar: { type: 'widget', @@ -1222,7 +1318,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, search: { type: 'landmark', @@ -1231,7 +1328,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, searchbox: { type: 'widget', @@ -1249,19 +1347,23 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['input[type="search"]'] + implicit: ['input[type="search"]'], + unsupported: false }, section: { nameFrom: ['author', 'contents'], - type: 'abstract' + type: 'abstract', + unsupported: false }, sectionhead: { nameFrom: ['author', 'contents'], - type: 'abstract' + type: 'abstract', + unsupported: false }, select: { nameFrom: ['author'], - type: 'abstract' + type: 'abstract', + unsupported: false }, separator: { type: 'structure', @@ -1279,7 +1381,8 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['hr'] + implicit: ['hr'], + unsupported: false }, slider: { type: 'widget', @@ -1295,7 +1398,8 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['input[type="range"]'] + implicit: ['input[type="range"]'], + unsupported: false }, spinbutton: { type: 'widget', @@ -1311,7 +1415,8 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['input[type="number"]'] + implicit: ['input[type="number"]'], + unsupported: false }, status: { type: 'widget', @@ -1321,10 +1426,12 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['output'] + implicit: ['output'], + unsupported: false }, structure: { - type: 'abstract' + type: 'abstract', + unsupported: false }, switch: { type: 'widget', @@ -1334,7 +1441,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author', 'contents'], - context: null + context: null, + unsupported: false }, tab: { type: 'widget', @@ -1349,7 +1457,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author', 'contents'], - context: ['tablist'] + context: ['tablist'], + unsupported: false }, table: { type: 'structure', @@ -1361,7 +1470,8 @@ lookupTable.role = { }, nameFrom: ['author'], context: null, - implicit: ['table'] + implicit: ['table'], + unsupported: false }, tablist: { type: 'composite', @@ -1379,7 +1489,8 @@ lookupTable.role = { all: ['tab'] }, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, tabpanel: { type: 'widget', @@ -1388,7 +1499,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, term: { type: 'structure', @@ -1398,13 +1510,15 @@ lookupTable.role = { owned: null, nameFrom: ['author', 'contents'], context: null, - implicit: ['dt'] + implicit: ['dt'], + unsupported: false }, text: { type: 'structure', owned: null, nameFrom: ['author', 'contents'], - context: null + context: null, + unsupported: false }, textbox: { type: 'widget', @@ -1430,7 +1544,8 @@ lookupTable.role = { 'input[type="url"]', 'input:not([type])', 'textarea' - ] + ], + unsupported: false }, timer: { type: 'widget', @@ -1439,7 +1554,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, toolbar: { type: 'structure', @@ -1454,7 +1570,8 @@ lookupTable.role = { owned: null, nameFrom: ['author'], context: null, - implicit: ['menu[type="toolbar"]'] + implicit: ['menu[type="toolbar"]'], + unsupported: false }, tooltip: { type: 'widget', @@ -1463,7 +1580,8 @@ lookupTable.role = { }, owned: null, nameFrom: ['author', 'contents'], - context: null + context: null, + unsupported: false }, tree: { type: 'composite', @@ -1481,7 +1599,8 @@ lookupTable.role = { all: ['treeitem'] }, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, treegrid: { type: 'composite', @@ -1503,7 +1622,8 @@ lookupTable.role = { one: ['rowgroup', 'row'] }, nameFrom: ['author'], - context: null + context: null, + unsupported: false }, treeitem: { type: 'widget', @@ -1520,13 +1640,16 @@ lookupTable.role = { }, owned: null, nameFrom: ['author', 'contents'], - context: ['group', 'tree'] + context: ['group', 'tree'], + unsupported: false }, widget: { - type: 'abstract' + type: 'abstract', + unsupported: false }, window: { nameFrom: ['author'], - type: 'abstract' + type: 'abstract', + unsupported: false } }; diff --git a/lib/commons/aria/roles.js b/lib/commons/aria/roles.js index 33e25cdbe2..6039f65b99 100644 --- a/lib/commons/aria/roles.js +++ b/lib/commons/aria/roles.js @@ -6,12 +6,16 @@ * @memberof axe.commons.aria * @instance * @param {String} role The role to check - * @param {Object} options Use `allowAbstract` if you want abstracts + * @param {Object} options Use `allowAbstract` if you want abstracts, and `flagUnsupported: true` to report unsupported roles * @return {Boolean} */ -aria.isValidRole = function(role, { allowAbstract } = {}) { +aria.isValidRole = function( + role, + { allowAbstract, flagUnsupported = false } = {} +) { const roleDefinition = aria.lookupTable.role[role]; - if (!roleDefinition) { + const isRoleUnsupported = roleDefinition ? roleDefinition.unsupported : false; + if (!roleDefinition || (flagUnsupported && isRoleUnsupported)) { return false; } return allowAbstract ? true : roleDefinition.type !== 'abstract'; diff --git a/lib/rules/aria-roles.json b/lib/rules/aria-roles.json index 36db03dbfe..546889eada 100644 --- a/lib/rules/aria-roles.json +++ b/lib/rules/aria-roles.json @@ -14,6 +14,7 @@ "any": [], "none": [ "invalidrole", - "abstractrole" + "abstractrole", + "unsupportedrole" ] } \ No newline at end of file diff --git a/test/checks/shared/unsupportedrole.js b/test/checks/shared/unsupportedrole.js new file mode 100644 index 0000000000..2a645d5120 --- /dev/null +++ b/test/checks/shared/unsupportedrole.js @@ -0,0 +1,25 @@ +describe('unsupportedrole', function() { + 'use strict'; + + var fixture = document.getElementById('fixture'); + + afterEach(function() { + fixture.innerHTML = ''; + }); + + it('should return true if applied to an unsupported role', function() { + axe.commons.aria.lookupTable.role.mcheddarton = { + type: 'widget', + unsupported: true + }; + fixture.innerHTML = '
Contents
'; + var node = fixture.querySelector('#target'); + assert.isTrue(checks.unsupportedrole.evaluate(node)); + }); + + it('should return false if applied to a supported role', function() { + fixture.innerHTML = ''; + var node = fixture.querySelector('#target'); + assert.isFalse(checks.unsupportedrole.evaluate(node)); + }); +}); diff --git a/test/integration/rules/aria-roles/aria-roles.html b/test/integration/rules/aria-roles/aria-roles.html index 0cb96b2e3f..dd9a235023 100644 --- a/test/integration/rules/aria-roles/aria-roles.html +++ b/test/integration/rules/aria-roles/aria-roles.html @@ -125,4 +125,6 @@
fail
fail
+ +
fail
diff --git a/test/integration/rules/aria-roles/aria-roles.json b/test/integration/rules/aria-roles/aria-roles.json index 79482def76..8930d7f4e7 100644 --- a/test/integration/rules/aria-roles/aria-roles.json +++ b/test/integration/rules/aria-roles/aria-roles.json @@ -3,7 +3,7 @@ "rule": "aria-roles", "violations": [ ["#fail1"], ["#fail2"], ["#fail3"], ["#fail4"], ["#fail5"], ["#fail6"], ["#fail7"], - ["#fail8"], ["#fail9"], ["#fail10"], ["#fail11"], ["#fail12"], ["#fail13"] + ["#fail8"], ["#fail9"], ["#fail10"], ["#fail11"], ["#fail12"], ["#fail13"], ["#fail14"] ], "passes": [ ["#pass1"], ["#pass2"], ["#pass3"], ["#pass4"], ["#pass5"], ["#pass6"], ["#pass7"],