diff --git a/package.json b/package.json index 7a183398f40..e6d468f7c51 100644 --- a/package.json +++ b/package.json @@ -155,7 +155,7 @@ "@microsoft/applicationinsights-web": "^2.8.15", "@testing-library/user-event": "^14.5.2", "ajv": "^8.12.0", - "axe-core": "4.7.2", + "axe-core": "4.8.4", "classnames": "^2.5.1", "idb-keyval": "^6.2.1", "lodash": "^4.17.21", diff --git a/packages/report/package.json b/packages/report/package.json index 066295bf885..8e7727e6e17 100644 --- a/packages/report/package.json +++ b/packages/report/package.json @@ -19,7 +19,7 @@ }, "dependencies": { "@fluentui/react": "^8.96.1", - "axe-core": "4.7.2", + "axe-core": "4.8.4", "classnames": "^2.5.1", "lodash": "^4.17.21", "luxon": "^3.4.4", diff --git a/packages/ui/package.json b/packages/ui/package.json index 61f26d792d6..a972de8848a 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -18,7 +18,7 @@ }, "dependencies": { "@fluentui/react": "^8.96.1", - "axe-core": "4.7.2", + "axe-core": "4.8.4", "classnames": "^2.5.1", "lodash": "^4.17.21", "luxon": "^3.4.4", diff --git a/src/DetailsView/components/load-assessment-data-schema-provider.ts b/src/DetailsView/components/load-assessment-data-schema-provider.ts index 9f0e6f20f96..2dae9a2a793 100644 --- a/src/DetailsView/components/load-assessment-data-schema-provider.ts +++ b/src/DetailsView/components/load-assessment-data-schema-provider.ts @@ -24,6 +24,8 @@ export class LoadAssessmentDataSchemaProvider { const deprecatedRequirements = [ { assessmentKey: 'automated-checks', requirementKey: 'aria-roledescription' }, { assessmentKey: 'automated-checks', requirementKey: 'duplicate-id' }, + { assessmentKey: 'automated-checks', requirementKey: 'duplicate-id-active' }, + { assessmentKey: 'automated-checks', requirementKey: 'duplicate-id-aria' }, ]; deprecatedRequirements.forEach(requirement => { if (this.getAssessments(schema)[requirement.assessmentKey] === undefined) { diff --git a/src/common/components/cards/rich-resolution-content.tsx b/src/common/components/cards/rich-resolution-content.tsx index aa2fcb4af6b..90bb764c2d4 100644 --- a/src/common/components/cards/rich-resolution-content.tsx +++ b/src/common/components/cards/rich-resolution-content.tsx @@ -72,6 +72,16 @@ export const RichResolutionContent = NamedFC( ); } + case 'web/duplicate-id-aria': { + return ( +
+ Document has multiple elements referenced with ARIA with the same id + attribute. Examine any duplicate ID values and rename them. Duplicate IDs + are common validation errors that may break the accessibility of labels, + e.g., form fields, table header cells. +
+ ); + } case 'web/th-has-data-cells': { return (
diff --git a/src/common/configs/a11y-insights-rule-resources.ts b/src/common/configs/a11y-insights-rule-resources.ts index 1f95d9fc8d5..be1d993b82e 100644 --- a/src/common/configs/a11y-insights-rule-resources.ts +++ b/src/common/configs/a11y-insights-rule-resources.ts @@ -18,9 +18,12 @@ export const getA11yInsightsWebRuleUrl = (ruleId: string) => { const webRulesWithResources = [ 'aria-alt', 'aria-allowed-attr', + 'aria-conditional-attr', + 'aria-deprecated-role', 'aria-hidden-body', 'aria-hidden-focus', 'aria-input-field-name', + 'aria-prohibited-attr', 'aria-required-attr', 'aria-required-attr', 'aria-required-children', diff --git a/src/scanner/axe-extension.d.ts b/src/scanner/axe-extension.d.ts index 772be8ae8ed..b5a42e31747 100644 --- a/src/scanner/axe-extension.d.ts +++ b/src/scanner/axe-extension.d.ts @@ -12,7 +12,6 @@ declare module 'axe-core/axe' { }; dom: { isVisible: Function; - idrefs: (node: HTMLElement, attr: string) => HTMLElement[]; }; text: { accessibleText: Function; @@ -31,6 +30,23 @@ declare module 'axe-core/axe' { // this must be surrounded by axe.setup and axe.teardown calls getSelector: (element: HTMLElement) => string; } + interface Dom { + isVisible: Function; + idrefs: (node: HTMLElement, attr: string) => HTMLElement[]; + } + interface Aria { + label: Function; + implicitRole: Function; + getRolesByType: Function; + lookupTable: any; + } + + interface Text { + accessibleText: Function; + isHumanInterpretable: Function; + sanitize: Function; + subtreeText: Function; + } const _audit: { rules: IRuleConfiguration[]; diff --git a/src/scanner/get-rule-inclusions.ts b/src/scanner/get-rule-inclusions.ts index 7eedc7af57d..3bfc153b1a1 100644 --- a/src/scanner/get-rule-inclusions.ts +++ b/src/scanner/get-rule-inclusions.ts @@ -15,6 +15,10 @@ export const explicitRuleOverrides: DictionaryStringTo = { status: 'included', reason: 'best practice rule that was investigated with no known false positives, implemented as an automated check.', }, + 'aria-braille-equivalent': { + status: 'excluded', + reason: "only reports needs-review results, but we haven't implemented needs-review content for it yet", + }, 'audio-caption': { status: 'included', reason: 'for parity with video-caption, which axe-core includes by default', @@ -53,6 +57,7 @@ export const explicitRuleOverrides: DictionaryStringTo = { export const needsReviewRules = [ 'aria-input-field-name', 'color-contrast', + 'duplicate-id-aria', 'th-has-data-cells', 'label-content-name-mismatch', 'p-as-heading', diff --git a/src/tests/end-to-end/tests/popup/__snapshots__/launchpad.test.ts.snap b/src/tests/end-to-end/tests/popup/__snapshots__/launchpad.test.ts.snap index 615eb771f55..baa82b71fc4 100644 --- a/src/tests/end-to-end/tests/popup/__snapshots__/launchpad.test.ts.snap +++ b/src/tests/end-to-end/tests/popup/__snapshots__/launchpad.test.ts.snap @@ -289,7 +289,7 @@ exports[`Popup -> Launch Pad content should match snapshot when quick assess fea Navigate to axe-core npm page
- 4.7.2 + 4.8.4 @@ -587,7 +587,7 @@ exports[`Popup -> Launch Pad content should match snapshot when quick assess fea Navigate to axe-core npm page - 4.7.2 + 4.8.4 diff --git a/src/tests/unit/tests/DetailsView/components/load-assessment-data-schema-provider.test.ts b/src/tests/unit/tests/DetailsView/components/load-assessment-data-schema-provider.test.ts index 5bd2102a66c..14037fd55c9 100644 --- a/src/tests/unit/tests/DetailsView/components/load-assessment-data-schema-provider.test.ts +++ b/src/tests/unit/tests/DetailsView/components/load-assessment-data-schema-provider.test.ts @@ -68,6 +68,8 @@ describe(LoadAssessmentDataSchemaProvider, () => { ? { 'aria-roledescription': stepProperties, 'duplicate-id': stepProperties, + 'duplicate-id-active': stepProperties, + 'duplicate-id-aria': stepProperties, } : {}, type: ['object', 'null'], @@ -80,6 +82,8 @@ describe(LoadAssessmentDataSchemaProvider, () => { ? { 'aria-roledescription': statusProperties, 'duplicate-id': statusProperties, + 'duplicate-id-active': statusProperties, + 'duplicate-id-aria': statusProperties, } : {}, type: ['object', 'null'], diff --git a/src/tests/unit/tests/common/components/cards/__snapshots__/rich-resolution-content.test.tsx.snap b/src/tests/unit/tests/common/components/cards/__snapshots__/rich-resolution-content.test.tsx.snap index 73edd7bae3d..0fe06e958bc 100644 --- a/src/tests/unit/tests/common/components/cards/__snapshots__/rich-resolution-content.test.tsx.snap +++ b/src/tests/unit/tests/common/components/cards/__snapshots__/rich-resolution-content.test.tsx.snap @@ -60,6 +60,14 @@ exports[`RichResolutionContent renders static content with id=web/color-contrast `; +exports[`RichResolutionContent renders static content with id=web/duplicate-id-aria 1`] = ` + +
+ Document has multiple elements referenced with ARIA with the same id attribute. Examine any duplicate ID values and rename them. Duplicate IDs are common validation errors that may break the accessibility of labels, e.g., form fields, table header cells. +
+
+`; + exports[`RichResolutionContent renders static content with id=web/label-content-name-mismatch 1`] = ` diff --git a/src/tests/unit/tests/common/components/cards/rich-resolution-content.test.tsx b/src/tests/unit/tests/common/components/cards/rich-resolution-content.test.tsx index 628b59d868a..06d1cf18434 100644 --- a/src/tests/unit/tests/common/components/cards/rich-resolution-content.test.tsx +++ b/src/tests/unit/tests/common/components/cards/rich-resolution-content.test.tsx @@ -18,6 +18,7 @@ describe('RichResolutionContent', () => { it.each([ 'web/aria-input-field-name', 'web/color-contrast', + 'web/duplicate-id-aria', 'web/th-has-data-cells', 'web/label-content-name-mismatch', 'web/p-as-heading', diff --git a/src/tests/unit/tests/common/self-fast-pass.test.ts b/src/tests/unit/tests/common/self-fast-pass.test.ts index 38270506942..b8a47d5acc6 100644 --- a/src/tests/unit/tests/common/self-fast-pass.test.ts +++ b/src/tests/unit/tests/common/self-fast-pass.test.ts @@ -70,6 +70,7 @@ describe('SelfFastPass', () => { testsToRun: [ 'aria-input-field-name', 'color-contrast', + 'duplicate-id-aria', 'th-has-data-cells', 'label-content-name-mismatch', 'p-as-heading', diff --git a/src/tests/unit/tests/scanner/__snapshots__/get-rule-inclusions.test.ts.snap b/src/tests/unit/tests/scanner/__snapshots__/get-rule-inclusions.test.ts.snap index 432906d8bfb..ce3d359781a 100644 --- a/src/tests/unit/tests/scanner/__snapshots__/get-rule-inclusions.test.ts.snap +++ b/src/tests/unit/tests/scanner/__snapshots__/get-rule-inclusions.test.ts.snap @@ -16,9 +16,19 @@ exports[`getRuleInclusions getRuleInclusions matches snapshotted list of product "reason": "best practice rule that was investigated with no known false positives, implemented as an automated check.", "status": "included", }, + "aria-braille-equivalent": { + "reason": "only reports needs-review results, but we haven't implemented needs-review content for it yet", + "status": "excluded", + }, "aria-command-name": { "status": "included", }, + "aria-conditional-attr": { + "status": "included", + }, + "aria-deprecated-role": { + "status": "included", + }, "aria-dialog-name": { "reason": "rule is tagged best-practice", "status": "excluded", @@ -38,6 +48,9 @@ exports[`getRuleInclusions getRuleInclusions matches snapshotted list of product "aria-progressbar-name": { "status": "included", }, + "aria-prohibited-attr": { + "status": "included", + }, "aria-required-attr": { "status": "included", }, @@ -118,7 +131,8 @@ exports[`getRuleInclusions getRuleInclusions matches snapshotted list of product "status": "excluded", }, "duplicate-id-active": { - "status": "included", + "reason": "disabled in axe config", + "status": "excluded", }, "duplicate-id-aria": { "status": "included", diff --git a/src/tests/unit/tests/scanner/accessible-text.test.ts b/src/tests/unit/tests/scanner/accessible-text.test.ts index c2c03266559..88fb04e51d0 100644 --- a/src/tests/unit/tests/scanner/accessible-text.test.ts +++ b/src/tests/unit/tests/scanner/accessible-text.test.ts @@ -19,7 +19,7 @@ describe('axe.commons.text.accessibleText examples', () => { const element1 = fixture.querySelector('#el1'); - const accessibleText = withAxeSetup(() => Axe.commons.text.accessibleText(element1, false)); + const accessibleText = withAxeSetup(() => Axe.commons.text.accessibleText(element1)); expect(accessibleText).toBe('hello'); }); @@ -32,7 +32,7 @@ describe('axe.commons.text.accessibleText examples', () => { const element2 = fixture.querySelector('#el2'); - const accessibleText = withAxeSetup(() => Axe.commons.text.accessibleText(element2, false)); + const accessibleText = withAxeSetup(() => Axe.commons.text.accessibleText(element2)); expect(accessibleText).toBe('hello'); }); @@ -44,7 +44,7 @@ describe('axe.commons.text.accessibleText examples', () => { const span = fixture.querySelector('#del_row2'); - const accessibleText = withAxeSetup(() => Axe.commons.text.accessibleText(span, false)); + const accessibleText = withAxeSetup(() => Axe.commons.text.accessibleText(span)); expect(accessibleText).toBe('Delete HolidayLetter.pdf'); }); diff --git a/yarn.lock b/yarn.lock index 86ea53e04e9..fad0cddddda 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3283,7 +3283,7 @@ __metadata: resolution: "accessibility-insights-report@workspace:packages/report" dependencies: "@fluentui/react": ^8.96.1 - axe-core: 4.7.2 + axe-core: 4.8.4 classnames: ^2.5.1 lodash: ^4.17.21 luxon: ^3.4.4 @@ -3299,7 +3299,7 @@ __metadata: resolution: "accessibility-insights-ui@workspace:packages/ui" dependencies: "@fluentui/react": ^8.96.1 - axe-core: 4.7.2 + axe-core: 4.8.4 classnames: ^2.5.1 lodash: ^4.17.21 luxon: ^3.4.4 @@ -3340,7 +3340,7 @@ __metadata: "@typescript-eslint/eslint-plugin": ^5.61.0 "@typescript-eslint/parser": ^6.18.1 ajv: ^8.12.0 - axe-core: 4.7.2 + axe-core: 4.8.4 case-sensitive-paths-webpack-plugin: ^2.4.0 classnames: ^2.5.1 codecov: ^3.8.3 @@ -3932,10 +3932,10 @@ __metadata: languageName: node linkType: hard -"axe-core@npm:4.7.2": - version: 4.7.2 - resolution: "axe-core@npm:4.7.2" - checksum: 5d86fa0f45213b0e54cbb5d713ce885c4a8fe3a72b92dd915a47aa396d6fd149c4a87fec53aa978511f6d941402256cfeb26f2db35129e370f25a453c688655a +"axe-core@npm:4.8.4": + version: 4.8.4 + resolution: "axe-core@npm:4.8.4" + checksum: 644da2fec17bcf6f834edaab1baa5a75a9a3ee370c323215c73da6d7e45fac11d01470d92d0a3d5f26695e15c1d9b781733dfbe1fe09d505076c58f09ed74e02 languageName: node linkType: hard