Skip to content
Merged
34 changes: 33 additions & 1 deletion build/tasks/esbuild.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
const { build } = require('esbuild');
const path = require('path');
const fs = require('fs');

// [a11y-core]: resolve ip-protection imports to real file in monorepo,
// or fall back to a stub when building axe-core standalone (CI).
// Set A11Y_FINGERPRINT_PATH env var to override the fingerprint module location.
const fingerprintFallback = {
name: 'a11y-fingerprint-fallback',
setup(pluginBuild) {
pluginBuild.onResolve(
{ filter: /ip-protection\/utils\/fingerprint/ },
args => {
if (process.env.A11Y_FINGERPRINT_PATH) {
const envPath = path.resolve(process.env.A11Y_FINGERPRINT_PATH);
if (fs.existsSync(envPath)) {
return { path: envPath };
}
}
const realPath = path.resolve(args.resolveDir, args.path + '.js');
if (fs.existsSync(realPath)) {
return { path: realPath };
}
return {
path: path.resolve(
__dirname,
'../../lib/core/utils/fingerprint-stub.js'
)
};
}
);
}
};

module.exports = function (grunt) {
grunt.registerMultiTask(
Expand All @@ -24,7 +55,8 @@ module.exports = function (grunt) {
outfile: path.join(dest, name),
minify: false,
format: 'esm',
bundle: true
bundle: true,
plugins: [fingerprintFallback]
})
.then(done)
.catch(e => {
Expand Down
9 changes: 9 additions & 0 deletions build/tasks/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,15 @@ function createSchemas() {
description: {
required: true,
type: 'string'
},
violationConfidence: {
type: 'number'
},
needsReviewConfidence: {
type: 'number'
},
defaultCategory: {
type: 'string'
}
}
}
Expand Down
10 changes: 9 additions & 1 deletion lib/checks/aria/aria-prohibited-attr-evaluate.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,15 @@ export default function ariaProhibitedAttrEvaluate(

let messageKey = role !== null ? 'hasRole' : 'noRole';
messageKey += prohibited.length > 1 ? 'Plural' : 'Singular';
this.data({ role, nodeName, messageKey, prohibited });
// a11y-rule-aria-prohibited-attr: reviewPayload for bulk NR
const accessibleName = axe.commons.text.accessibleText(node);
this.data({
role,
nodeName,
messageKey,
prohibited,
reviewPayload: { visualHelperData: { accessibleName } }
});

// `subtreeDescendant` to override namedFromContents
const textContent = subtreeText(virtualNode, { subtreeDescendant: true });
Expand Down
15 changes: 14 additions & 1 deletion lib/checks/color/color-contrast-evaluate.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,20 @@ export default function colorContrastEvaluate(node, options, virtualNode) {
fontWeight: bold ? 'bold' : 'normal',
messageKey: missing,
expectedContrastRatio: expected + ':1',
shadowColor: shadowColor ? shadowColor.toHexString() : undefined
shadowColor: shadowColor ? shadowColor.toHexString() : undefined,
// a11y-rule-color-contrast: reviewPayload for bulk NR
reviewPayload: {
visualHelperData: {
fgColor: fgColor ? fgColor.toHexString() : null,
bgColor: bgColor ? bgColor.toHexString() : null,
isLargeText: !isSmallFont,
textContent: visibleText
? visibleText.length <= 100
? visibleText
: visibleText.slice(0, visibleText.lastIndexOf(' ', 100) || 100)
: null
}
}
});

// We don't know, so we'll put it into Can't Tell
Expand Down
21 changes: 20 additions & 1 deletion lib/checks/label/label-content-name-mismatch-evaluate.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,29 @@ function labelContentNameMismatchEvaluate(node, options, virtualNode) {
isHumanInterpretable(accText) < 1 ||
isHumanInterpretable(visibleText) < 1
) {
// a11y-rule-label-content-name-mismatch: reviewPayload for bulk NR
this.data({
reviewPayload: {
visualHelperData: {
accessibleName: accText || null
}
}
});
return undefined;
}

return isStringContained(visibleText, accText);
const result = isStringContained(visibleText, accText);
if (!result) {
// a11y-rule-label-content-name-mismatch: reviewPayload for bulk NR
this.data({
reviewPayload: {
visualHelperData: {
accessibleName: accText || null
}
}
});
}
return result;
}

export default labelContentNameMismatchEvaluate;
4 changes: 4 additions & 0 deletions lib/core/reporters/helpers/process-aggregate.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,9 @@ function trimElementSpec(elmSpec = {}, runOptions) {
if (runOptions.xpath) {
serialElm.xpath = elmSpec.xpath ?? ['/'];
}
// [a11y-core]: propagate htmlHash to final output
if (elmSpec.htmlHash) {
serialElm.htmlHash = elmSpec.htmlHash;
}
return serialElm;
}
7 changes: 6 additions & 1 deletion lib/core/utils/dq-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import cache from '../base/cache';
import memoize from './memoize';
import getNodeAttributes from './get-node-attributes';
import VirtualNode from '../../core/base/virtual-node/virtual-node';
import { computeFingerprintHash } from '../../../../ip-protection/utils/fingerprint';

const CACHE_KEY = 'DqElm.RunOptions';

Expand Down Expand Up @@ -341,6 +342,9 @@ const DqElement = memoize(function DqElement(elm, options, spec) {
this.nodeIndexes = [this._virtualNode.nodeIndex];
}

// [a11y-core]: compute htmlHash before truncation
this._htmlHash = computeFingerprintHash(this._element?.outerHTML);

/**
* The generated HTML source code of the element
* @type {String|null}
Expand Down Expand Up @@ -411,7 +415,8 @@ DqElement.prototype = {
xpath: this.xpath,
ancestry: this.ancestry,
nodeIndexes: this.nodeIndexes,
fromFrame: this.fromFrame
fromFrame: this.fromFrame,
htmlHash: this._htmlHash
};
if (this._includeElementInJson) {
spec.element = this._element;
Expand Down
7 changes: 7 additions & 0 deletions lib/core/utils/fingerprint-stub.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Fallback stub used when axe-core builds outside the a11y-engine monorepo.
// The real fingerprint logic lives in ip-protection/utils/fingerprint.js
// and is resolved by the esbuild plugin during monorepo builds.
// eslint-disable-next-line no-unused-vars
export function computeFingerprintHash(_outerHTML) {
return null;
}
3 changes: 2 additions & 1 deletion lib/rules/accesskeys.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"tags": ["cat.keyboard", "best-practice"],
"metadata": {
"description": "Ensure every accesskey attribute value is unique",
"help": "accesskey attribute value should be unique"
"help": "accesskey attribute value should be unique",
"violationConfidence": 99
},
"all": [],
"any": [],
Expand Down
3 changes: 2 additions & 1 deletion lib/rules/aria-allowed-attr.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"actIds": ["5c01ea"],
"metadata": {
"description": "Ensure an element's role supports its ARIA attributes",
"help": "Elements must only use supported ARIA attributes"
"help": "Elements must only use supported ARIA attributes",
"needsReviewConfidence": 50
},
"all": ["aria-allowed-attr"],
"any": [],
Expand Down
3 changes: 2 additions & 1 deletion lib/rules/aria-allowed-role.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"tags": ["cat.aria", "best-practice"],
"metadata": {
"description": "Ensure role attribute has an appropriate value for the element",
"help": "ARIA role should be appropriate for the element"
"help": "ARIA role should be appropriate for the element",
"needsReviewConfidence": 50
},
"all": [],
"any": ["aria-allowed-role"],
Expand Down
3 changes: 2 additions & 1 deletion lib/rules/aria-hidden-focus.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"actIds": ["6cfa84"],
"metadata": {
"description": "Ensure aria-hidden elements are not focusable nor contain focusable elements",
"help": "ARIA hidden element must not be focusable or contain focusable elements"
"help": "ARIA hidden element must not be focusable or contain focusable elements",
"needsReviewConfidence": 50
},
"all": [
"focusable-modal-open",
Expand Down
3 changes: 2 additions & 1 deletion lib/rules/aria-input-field-name.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"actIds": ["e086e5"],
"metadata": {
"description": "Ensure every ARIA input field has an accessible name",
"help": "ARIA input fields must have an accessible name"
"help": "ARIA input fields must have an accessible name",
"needsReviewConfidence": 50
},
"all": [],
"any": ["aria-label", "aria-labelledby", "non-empty-title"],
Expand Down
3 changes: 2 additions & 1 deletion lib/rules/aria-meter-name.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
],
"metadata": {
"description": "Ensure every ARIA meter node has an accessible name",
"help": "ARIA meter nodes must have an accessible name"
"help": "ARIA meter nodes must have an accessible name",
"violationConfidence": 99
},
"all": [],
"any": ["aria-label", "aria-labelledby", "non-empty-title"],
Expand Down
5 changes: 4 additions & 1 deletion lib/rules/aria-prohibited-attr.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
"actIds": ["5c01ea"],
"metadata": {
"description": "Ensure ARIA attributes are not prohibited for an element's role",
"help": "Elements must only use permitted ARIA attributes"
"help": "Elements must only use permitted ARIA attributes",
"violationConfidence": 90,
"needsReviewConfidence": 50,
"defaultCategory": "names-and-labels"
},
"all": [],
"any": [],
Expand Down
3 changes: 2 additions & 1 deletion lib/rules/aria-required-children.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"actIds": ["bc4a75", "ff89c9"],
"metadata": {
"description": "Ensure elements with an ARIA role that require child roles contain them",
"help": "Certain ARIA roles must contain particular children"
"help": "Certain ARIA roles must contain particular children",
"needsReviewConfidence": 50
},
"all": [],
"any": ["aria-required-children"],
Expand Down
3 changes: 2 additions & 1 deletion lib/rules/aria-toggle-field-name.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"actIds": ["e086e5"],
"metadata": {
"description": "Ensure every ARIA toggle field has an accessible name",
"help": "ARIA toggle fields must have an accessible name"
"help": "ARIA toggle fields must have an accessible name",
"needsReviewConfidence": 50
},
"all": [],
"any": [
Expand Down
3 changes: 2 additions & 1 deletion lib/rules/aria-treeitem-name.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"tags": ["cat.aria", "best-practice"],
"metadata": {
"description": "Ensure every ARIA treeitem node has an accessible name",
"help": "ARIA treeitem nodes should have an accessible name"
"help": "ARIA treeitem nodes should have an accessible name",
"violationConfidence": 99
},
"all": [],
"any": [
Expand Down
3 changes: 2 additions & 1 deletion lib/rules/aria-valid-attr-value.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"actIds": ["6a7281"],
"metadata": {
"description": "Ensure all ARIA attributes have valid values",
"help": "ARIA attributes must conform to valid values"
"help": "ARIA attributes must conform to valid values",
"needsReviewConfidence": 50
},
"all": ["aria-valid-attr-value", "aria-errormessage", "aria-level"],
"any": [],
Expand Down
3 changes: 2 additions & 1 deletion lib/rules/blink.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
],
"metadata": {
"description": "Ensure <blink> elements are not used",
"help": "<blink> elements are deprecated and must not be used"
"help": "<blink> elements are deprecated and must not be used",
"violationConfidence": 99
},
"all": [],
"any": [],
Expand Down
3 changes: 2 additions & 1 deletion lib/rules/button-name.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"actIds": ["97a4e1", "m6b1q3"],
"metadata": {
"description": "Ensure buttons have discernible text",
"help": "Buttons must have discernible text"
"help": "Buttons must have discernible text",
"needsReviewConfidence": 50
},
"all": [],
"any": [
Expand Down
3 changes: 2 additions & 1 deletion lib/rules/bypass.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"actIds": ["cf77f2", "047fe0", "b40fd1", "3e12e1", "ye5d6e"],
"metadata": {
"description": "Ensure each page has at least one mechanism for a user to bypass navigation and jump straight to the content",
"help": "Page must have means to bypass repeated blocks"
"help": "Page must have means to bypass repeated blocks",
"needsReviewConfidence": 50
},
"all": [],
"any": ["internal-link-present", "header-present", "landmark"],
Expand Down
4 changes: 3 additions & 1 deletion lib/rules/color-contrast-enhanced.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
"actIds": ["09o5cg"],
"metadata": {
"description": "Ensure the contrast between foreground and background colors meets WCAG 2 AAA enhanced contrast ratio thresholds",
"help": "Elements must meet enhanced color contrast ratio thresholds"
"help": "Elements must meet enhanced color contrast ratio thresholds",
"needsReviewConfidence": 50,
"defaultCategory": "text-contrast-enhanced"
},
"all": [],
"any": ["color-contrast-enhanced"],
Expand Down
4 changes: 3 additions & 1 deletion lib/rules/color-contrast.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
"actIds": ["afw4f7", "09o5cg"],
"metadata": {
"description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds",
"help": "Elements must meet minimum color contrast ratio thresholds"
"help": "Elements must meet minimum color contrast ratio thresholds",
"needsReviewConfidence": 50,
"defaultCategory": "text-contrast"
},
"all": [],
"any": ["color-contrast"],
Expand Down
4 changes: 3 additions & 1 deletion lib/rules/css-orientation-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
"actIds": ["b33eff"],
"metadata": {
"description": "Ensure content is not locked to any specific display orientation, and the content is operable in all display orientations",
"help": "CSS Media queries must not lock display orientation"
"help": "CSS Media queries must not lock display orientation",
"violationConfidence": 90,
"needsReviewConfidence": 50
},
"all": ["css-orientation-lock"],
"any": [],
Expand Down
3 changes: 2 additions & 1 deletion lib/rules/duplicate-id-aria.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"actIds": ["3ea0c8"],
"metadata": {
"description": "Ensure every id attribute value used in ARIA and in labels is unique",
"help": "IDs used in ARIA and labels must be unique"
"help": "IDs used in ARIA and labels must be unique",
"needsReviewConfidence": 50
},
"all": [],
"any": ["duplicate-id-aria"],
Expand Down
3 changes: 2 additions & 1 deletion lib/rules/focus-order-semantics.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
],
"metadata": {
"description": "Ensure elements in the focus order have a role appropriate for interactive content",
"help": "Elements in the focus order should have an appropriate role"
"help": "Elements in the focus order should have an appropriate role",
"violationConfidence": 90
},
"all": [],
"any": ["has-widget-role", "valid-scrollable-semantics"],
Expand Down
3 changes: 2 additions & 1 deletion lib/rules/form-field-multiple-labels.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
],
"metadata": {
"description": "Ensure form field does not have multiple label elements",
"help": "Form field must not have multiple label elements"
"help": "Form field must not have multiple label elements",
"needsReviewConfidence": 50
},
"all": [],
"any": [],
Expand Down
3 changes: 2 additions & 1 deletion lib/rules/frame-title-unique.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"actIds": ["4b1c6c"],
"metadata": {
"description": "Ensure <iframe> and <frame> elements contain a unique title attribute",
"help": "Frames must have a unique title attribute"
"help": "Frames must have a unique title attribute",
"needsReviewConfidence": 50
},
"all": [],
"any": [],
Expand Down
Loading
Loading