diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7988fcc0c5..4e484e9a73 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -64,7 +64,7 @@ jobs: run: | npm run prepare npm run build - - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: axe-core path: axe.js @@ -136,7 +136,7 @@ jobs: - *install-deps - &restore-axe-build name: Restore axe build - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: axe-core - name: Run ACT Tests diff --git a/CHANGELOG.md b/CHANGELOG.md index 33d846e41d..6f693aa3b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [4.11.3](https://github.com/dequelabs/axe-core/compare/v4.11.2...v4.11.3) (2026-04-13) + +### Bug Fixes + +- **aria-allowed-attr:** restrict br and wbr elements to aria-hidden only ([#4974](https://github.com/dequelabs/axe-core/issues/4974)) ([1d80163](https://github.com/dequelabs/axe-core/commit/1d801636f058f2abd885c488baff954872b13846)) +- **target-size:** ignore position: fixed elements that are offscreen when page is scrolled ([#5066](https://github.com/dequelabs/axe-core/issues/5066)) ([5906273](https://github.com/dequelabs/axe-core/commit/5906273841cbd7ac9e08af730dffc244cf42b39b)), closes [#5065](https://github.com/dequelabs/axe-core/issues/5065) + ### [4.11.2](https://github.com/dequelabs/axe-core/compare/v4.11.1...v4.11.2) (2026-03-30) ### Bug Fixes diff --git a/bower.json b/bower.json index 8469cdf9ac..cdcf5df692 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "axe-core", - "version": "4.11.2", + "version": "4.11.3", "deprecated": true, "contributors": [ { diff --git a/doc/standards-object.md b/doc/standards-object.md index 42766eca38..eb460c1795 100644 --- a/doc/standards-object.md +++ b/doc/standards-object.md @@ -95,7 +95,7 @@ The [`htmlElms`](../lib/standards/html-elms.js) object defines valid HTML elemen ### Used by Rules -- `aria-allowed-attr` - Checks if the attribute can be used on the element from the `noAriaAttrs` property. +- `aria-allowed-attr` - Checks if the attribute can be used on the element from the `noAriaAttrs` and `allowedAriaAttrs` properties. - `aria-allowed-role` - Checks if the role can be used on the HTML element from the `allowedRoles` property. - `aria-required-attrs` - Checks if any required attrs are defined implicitly on the element from the `implicitAttrs` property. @@ -110,6 +110,7 @@ The [`htmlElms`](../lib/standards/html-elms.js) object defines valid HTML elemen - `interactive` - `allowedRoles` - boolean or array(required). If element is allowed to use ARIA roles, a value of `true` means any role while a list of roles means only those are allowed. A value of `false` means no roles are allowed. - `noAriaAttrs` - boolean(optional. Defaults `true`). If the element is allowed to use global ARIA attributes and any allowed for the elements role. +- `allowedAriaAttrs` - array(optional). If specified, restricts which ARIA attributes may be used with this element (when no explicit role is set). Used by the `aria-allowed-attr` rule. - `shadowRoot` - boolean(optional. Default `false`). If the element is allowed to have a shadow root. - `implicitAttrs` - object(optional. Default `{}`). Any implicit ARIA attributes for the element and their default value. - `namingMethods` - array(optional. Default `[]`). The [native text method](../lib/commons/text/native-text-methods.js) used to calculate the accessible name of the element. diff --git a/lib/checks/aria/aria-allowed-attr-elm-evaluate.js b/lib/checks/aria/aria-allowed-attr-elm-evaluate.js new file mode 100644 index 0000000000..01af3b4f28 --- /dev/null +++ b/lib/checks/aria/aria-allowed-attr-elm-evaluate.js @@ -0,0 +1,45 @@ +import { getExplicitRole } from '../../commons/aria'; +import { getElementSpec, getGlobalAriaAttrs } from '../../commons/standards'; + +export default function ariaAllowedAttrElmEvaluate(node, options, virtualNode) { + const elmSpec = getElementSpec(virtualNode); + + // If no allowedAriaAttrs restriction, this check doesn't apply + if (!elmSpec.allowedAriaAttrs) { + return true; + } + + // If element has an explicit role, defer to the role-based check + const explicitRole = getExplicitRole(virtualNode); + if (explicitRole) { + return true; + } + + const { allowedAriaAttrs } = elmSpec; + const globalAriaAttrs = getGlobalAriaAttrs(); + const invalid = []; + + for (const attrName of virtualNode.attrNames) { + if ( + globalAriaAttrs.includes(attrName) && + !allowedAriaAttrs.includes(attrName) + ) { + invalid.push(attrName); + } + } + + if (!invalid.length) { + return true; + } + + const messageKey = invalid.length > 1 ? 'plural' : 'singular'; + this.data({ + messageKey, + nodeName: virtualNode.props.nodeName, + values: invalid + .map(attrName => attrName + '="' + virtualNode.attr(attrName) + '"') + .join(', ') + }); + + return false; +} diff --git a/lib/checks/aria/aria-allowed-attr-elm.json b/lib/checks/aria/aria-allowed-attr-elm.json new file mode 100644 index 0000000000..ebbdf67e96 --- /dev/null +++ b/lib/checks/aria/aria-allowed-attr-elm.json @@ -0,0 +1,13 @@ +{ + "id": "aria-allowed-attr-elm", + "evaluate": "aria-allowed-attr-elm-evaluate", + "metadata": { + "messages": { + "pass": "ARIA attributes are allowed for this element", + "fail": { + "singular": "ARIA attribute is not allowed on ${data.nodeName} elements: ${data.values}", + "plural": "ARIA attributes are not allowed on ${data.nodeName} elements: ${data.values}" + } + } + } +} diff --git a/lib/commons/dom/find-nearby-elms.js b/lib/commons/dom/find-nearby-elms.js index a0e421137c..b6e69e52be 100644 --- a/lib/commons/dom/find-nearby-elms.js +++ b/lib/commons/dom/find-nearby-elms.js @@ -1,5 +1,5 @@ import getNodeGrid from './get-node-grid'; -import { memoize } from '../../core/utils'; +import isFixedPosition from './is-fixed-position'; export default function findNearbyElms(vNode, margin = 0) { const grid = getNodeGrid(vNode); @@ -7,7 +7,7 @@ export default function findNearbyElms(vNode, margin = 0) { return []; // Elements not in the grid don't have ._grid } const rect = vNode.boundingClientRect; - const selfIsFixed = hasFixedPosition(vNode); + const selfIsFixed = isFixedPosition(vNode); const gridPosition = grid.getGridPositionOfRect(rect, margin); const neighbors = []; @@ -17,7 +17,7 @@ export default function findNearbyElms(vNode, margin = 0) { vNeighbor && vNeighbor !== vNode && !neighbors.includes(vNeighbor) && - selfIsFixed === hasFixedPosition(vNeighbor) + selfIsFixed === isFixedPosition(vNeighbor) ) { neighbors.push(vNeighbor); } @@ -26,13 +26,3 @@ export default function findNearbyElms(vNode, margin = 0) { return neighbors; } - -const hasFixedPosition = memoize(vNode => { - if (!vNode) { - return false; - } - if (vNode.getComputedStylePropertyValue('position') === 'fixed') { - return true; - } - return hasFixedPosition(vNode.parent); -}); diff --git a/lib/commons/dom/index.js b/lib/commons/dom/index.js index 3449238aa1..93e3f353a6 100644 --- a/lib/commons/dom/index.js +++ b/lib/commons/dom/index.js @@ -28,6 +28,7 @@ export { default as hasLangText } from './has-lang-text'; export { default as idrefs } from './idrefs'; export { default as insertedIntoFocusOrder } from './inserted-into-focus-order'; export { default as isCurrentPageLink } from './is-current-page-link'; +export { default as isFixedPosition } from './is-fixed-position'; export { default as isFocusable } from './is-focusable'; export { default as isHiddenWithCSS } from './is-hidden-with-css'; export { default as isHiddenForEveryone } from './is-hidden-for-everyone'; diff --git a/lib/commons/dom/is-fixed-position.js b/lib/commons/dom/is-fixed-position.js new file mode 100644 index 0000000000..d065694cfa --- /dev/null +++ b/lib/commons/dom/is-fixed-position.js @@ -0,0 +1,45 @@ +import memoize from '../../core/utils/memoize'; +import { nodeLookup } from '../../core/utils'; + +/** + * Determines if an element is inside a position:fixed subtree, even if the element itself is positioned differently. + * @param {VirtualNode|Element} node + * @param {Boolean} [options.skipAncestors] If the ancestor tree should not be used + * @return {Boolean} The element's position state + */ +export default function isFixedPosition(node, { skipAncestors } = {}) { + const { vNode } = nodeLookup(node); + + // detached element + if (!vNode) { + return false; + } + + if (skipAncestors) { + return isFixedSelf(vNode); + } + + return isFixedAncestors(vNode); +} + +/** + * Check the element for position:fixed + */ +const isFixedSelf = memoize(function isFixedSelfMemoized(vNode) { + return vNode.getComputedStylePropertyValue('position') === 'fixed'; +}); + +/** + * Check the element and ancestors for position:fixed + */ +const isFixedAncestors = memoize(function isFixedAncestorsMemoized(vNode) { + if (isFixedSelf(vNode)) { + return true; + } + + if (!vNode.parent) { + return false; + } + + return isFixedAncestors(vNode.parent); +}); diff --git a/lib/commons/dom/is-offscreen.js b/lib/commons/dom/is-offscreen.js index d0e3b75f71..4883be5688 100644 --- a/lib/commons/dom/is-offscreen.js +++ b/lib/commons/dom/is-offscreen.js @@ -2,6 +2,7 @@ import getComposedParent from './get-composed-parent'; import getElementCoordinates from './get-element-coordinates'; import getViewportSize from './get-viewport-size'; import { nodeLookup } from '../../core/utils'; +import isFixedPosition from './is-fixed-position'; function noParentScrolled(element, offset) { element = getComposedParent(element); @@ -37,39 +38,43 @@ function isOffscreen(element, { isAncestor } = {}) { return undefined; } - let leftBoundary; const docElement = document.documentElement; const styl = window.getComputedStyle(domNode); const dir = window .getComputedStyle(document.body || docElement) .getPropertyValue('direction'); - const coords = getElementCoordinates(domNode); + const isFixed = isFixedPosition(domNode); + const coords = isFixed + ? domNode.getBoundingClientRect() + : getElementCoordinates(domNode); + + // Consider 0 height/ width elements at origin visible + if (coords.top === 0 && coords.bottom === 0) { + return false; + } + if (coords.left === 0 && coords.right === 0) { + return false; + } - // bottom edge beyond if ( - coords.bottom < 0 && + coords.bottom <= 0 && (noParentScrolled(domNode, coords.bottom) || styl.position === 'absolute') ) { return true; } - if (coords.left === 0 && coords.right === 0) { - //This is an edge case, an empty (zero-width) element that isn't positioned 'off screen'. - return false; + const viewportSize = getViewportSize(window); + if (isFixed && coords.top >= viewportSize.height) { + return true; // Positioned below the viewport } - if (dir === 'ltr') { - if (coords.right <= 0) { - return true; - } - } else { - leftBoundary = Math.max( - docElement.scrollWidth, - getViewportSize(window).width - ); - if (coords.left >= leftBoundary) { - return true; - } + const rightEdge = Math.max(docElement.scrollWidth, viewportSize.width); + if ((isFixed || dir === 'rtl') && coords.left >= rightEdge) { + return true; // Positioned right of the viewport, preventing right scrolling + } + + if ((isFixed || dir === 'ltr') && coords.right <= 0) { + return true; // Positioned left of the viewport, preventing left scrolling } return false; diff --git a/lib/core/utils/frame-messenger/channel-store.js b/lib/core/utils/frame-messenger/channel-store.js index 59ba07878f..1cb12256c2 100644 --- a/lib/core/utils/frame-messenger/channel-store.js +++ b/lib/core/utils/frame-messenger/channel-store.js @@ -8,14 +8,16 @@ export function storeReplyHandler( sendToParent = true ) { assert( - !channels[channelId], + !Object.prototype.hasOwnProperty.call(channels, channelId), `A replyHandler already exists for this message channel.` ); channels[channelId] = { replyHandler, sendToParent }; } export function getReplyHandler(channelId) { - return channels[channelId]; + return Object.prototype.hasOwnProperty.call(channels, channelId) + ? channels[channelId] + : undefined; } export function deleteReplyHandler(channelId) { diff --git a/lib/core/utils/respondable.js b/lib/core/utils/respondable.js index 7a1c5c4da8..cbacaf9328 100644 --- a/lib/core/utils/respondable.js +++ b/lib/core/utils/respondable.js @@ -39,7 +39,13 @@ export default function respondable( */ function messageListener(data, responder) { const { topic, message, keepalive } = data; - const topicHandler = topicHandlers[topic]; + const topicHandler = Object.prototype.hasOwnProperty.call( + topicHandlers, + topic + ) + ? topicHandlers[topic] + : undefined; + if (!topicHandler) { return; } @@ -92,7 +98,10 @@ respondable.subscribe = function subscribe(topic, topicHandler) { typeof topicHandler === 'function', 'Subscriber callback must be a function' ); - assert(!topicHandlers[topic], `Topic ${topic} is already registered to.`); + assert( + !Object.prototype.hasOwnProperty.call(topicHandlers, topic), + `Topic ${topic} is already registered to.` + ); topicHandlers[topic] = topicHandler; }; diff --git a/lib/rules/aria-allowed-attr.json b/lib/rules/aria-allowed-attr.json index d907596925..4ef67632c1 100644 --- a/lib/rules/aria-allowed-attr.json +++ b/lib/rules/aria-allowed-attr.json @@ -16,7 +16,7 @@ "description": "Ensure an element's role supports its ARIA attributes", "help": "Elements must only use supported ARIA attributes" }, - "all": ["aria-allowed-attr"], + "all": ["aria-allowed-attr", "aria-allowed-attr-elm"], "any": [], "none": ["aria-unsupported-attr"] } diff --git a/lib/standards/html-elms.js b/lib/standards/html-elms.js index 041d2a9438..76e296f212 100644 --- a/lib/standards/html-elms.js +++ b/lib/standards/html-elms.js @@ -133,7 +133,8 @@ const htmlElms = { br: { contentTypes: ['phrasing', 'flow'], allowedRoles: ['presentation', 'none'], - namingMethods: ['titleText', 'singleSpace'] + namingMethods: ['titleText', 'singleSpace'], + allowedAriaAttrs: ['aria-hidden'] }, button: { contentTypes: ['interactive', 'phrasing', 'flow'], @@ -969,7 +970,8 @@ const htmlElms = { }, wbr: { contentTypes: ['phrasing', 'flow'], - allowedRoles: ['presentation', 'none'] + allowedRoles: ['presentation', 'none'], + allowedAriaAttrs: ['aria-hidden'] } }; diff --git a/locales/_template.json b/locales/_template.json index 771d26ef37..7e3304e215 100644 --- a/locales/_template.json +++ b/locales/_template.json @@ -426,6 +426,13 @@ "plural": "Abstract roles cannot be directly used: ${data.values}" } }, + "aria-allowed-attr-elm": { + "pass": "ARIA attributes are allowed for this element", + "fail": { + "singular": "ARIA attribute is not allowed on ${data.nodeName} elements: ${data.values}", + "plural": "ARIA attributes are not allowed on ${data.nodeName} elements: ${data.values}" + } + }, "aria-allowed-attr": { "pass": "ARIA attributes are used correctly for the defined role", "fail": { diff --git a/package-lock.json b/package-lock.json index fcdf925de4..4bd9814ced 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "axe-core", - "version": "4.11.2", + "version": "4.11.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "axe-core", - "version": "4.11.2", + "version": "4.11.3", "license": "MPL-2.0", "devDependencies": { "@axe-core/webdriverjs": "^4.10.2", @@ -1543,9 +1543,9 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.0.tgz", - "integrity": "sha512-fNEdfc0yi16lt6IZo2Qxk3knHVdfMYX33czNb4v8yWhemoBhibCpQK/uYHtSKIiO+p/zd3+8fYVXhQdOVV608w==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.2.tgz", + "integrity": "sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==", "dev": true, "dependencies": { "@babel/compat-data": "^7.29.0", @@ -1641,9 +1641,9 @@ } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.0.tgz", - "integrity": "sha512-TgUkdp71C9pIbBcHudc+gXZnihEDOjUAmXO1VO4HHGES7QLZcShR0stfKIxLSNIYx2fqhmJChOjm/wkF8wv4gA==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.2.tgz", + "integrity": "sha512-Lc94FOD5+0aXhdb0Tdg3RUtqT6yWbI/BbFWvlaSJ3gAb9Ks+99nHRDKADVqC37er4eCB0fHyWT+y+K3QOvJKbw==", "dev": true, "dependencies": { "core-js-pure": "^3.48.0" @@ -2046,9 +2046,9 @@ "dev": true }, "node_modules/@hapi/tlds": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.4.tgz", - "integrity": "sha512-Fq+20dxsxLaUn5jSSWrdtSRcIUba2JquuorF9UW1wIJS5cSUwxIsO2GIhaWynPRflvxSzFN+gxKte2HEW1OuoA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.6.tgz", + "integrity": "sha512-xdi7A/4NZokvV0ewovme3aUO5kQhW9pQ2YD1hRqZGhhSi5rBv4usHYidVocXSi9eihYsznZxLtAiEYYUL6VBGw==", "dev": true, "engines": { "node": ">=14.0.0" @@ -2426,18 +2426,18 @@ } }, "node_modules/@sinonjs/fake-timers": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.1.0.tgz", - "integrity": "sha512-cqfapCxwTGsrR80FEgOoPsTonoefMBY7dnUEbQ+GRcved0jvkJLzvX6F4WtN+HBqbPX/SiFsIRUp+IrCW/2I2w==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.2.1.tgz", + "integrity": "sha512-QdfpQFIwYrTK8lFsII4bJ1AO1ZLbw7B+oxfP+/qSsiTrVerFp7aY2O+d2GNGrTxP58ezEbjbf7mTTLMsd7M7XQ==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.1" } }, "node_modules/@sinonjs/samsam": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.3.tgz", - "integrity": "sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-9.0.3.tgz", + "integrity": "sha512-ZgYY7Dc2RW+OUdnZ1DEHg00lhRt+9BjymPKHog4PRFzr1U3MbK57+djmscWyKxzO1qfunHqs4N45WWyKIFKpiQ==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.1", @@ -2460,9 +2460,9 @@ "dev": true }, "node_modules/@standard-schema/spec": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", - "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true }, "node_modules/@testim/chrome-version": { @@ -2794,14 +2794,23 @@ } }, "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.14.0.tgz", + "integrity": "sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==", "dev": true, "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/axios/node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "dev": true, + "engines": { + "node": ">=10" } }, "node_modules/babel-plugin-polyfill-corejs2": { @@ -3688,9 +3697,9 @@ } }, "node_modules/commander": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", "dev": true, "engines": { "node": ">=20" @@ -4247,9 +4256,9 @@ } }, "node_modules/core-js": { - "version": "3.48.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", - "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", + "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==", "dev": true, "hasInstallScript": true, "funding": { @@ -5852,9 +5861,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "dev": true, "funding": [ { @@ -5921,9 +5930,9 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dev": true, "dependencies": { "asynckit": "^0.4.0", @@ -6389,9 +6398,9 @@ } }, "node_modules/globals": { - "version": "17.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz", - "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==", + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz", + "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==", "dev": true, "engines": { "node": ">=18" @@ -7903,9 +7912,9 @@ } }, "node_modules/joi": { - "version": "18.0.2", - "resolved": "https://registry.npmjs.org/joi/-/joi-18.0.2.tgz", - "integrity": "sha512-RuCOQMIt78LWnktPoeBL0GErkNaJPTBGcYuyaBvUOQSpcpcLfWrHPPihYdOGbV5pam9VTWbeoF7TsGiHugcjGA==", + "version": "18.1.2", + "resolved": "https://registry.npmjs.org/joi/-/joi-18.1.2.tgz", + "integrity": "sha512-rF5MAmps5esSlhCA+N1b6IYHDw9j/btzGaqfgie522jS02Ju/HXBxamlXVlKEHAxoMKQL77HWI8jlqWsFuekZA==", "dev": true, "dependencies": { "@hapi/address": "^5.1.1", @@ -7914,7 +7923,7 @@ "@hapi/pinpoint": "^2.0.1", "@hapi/tlds": "^1.1.1", "@hapi/topo": "^6.0.2", - "@standard-schema/spec": "^1.0.0" + "@standard-schema/spec": "^1.1.0" }, "engines": { "node": ">= 20" @@ -8520,18 +8529,17 @@ } }, "node_modules/lint-staged": { - "version": "16.2.7", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.7.tgz", - "integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.4.0.tgz", + "integrity": "sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==", "dev": true, "dependencies": { - "commander": "^14.0.2", + "commander": "^14.0.3", "listr2": "^9.0.5", - "micromatch": "^4.0.8", - "nano-spawn": "^2.0.0", - "pidtree": "^0.6.0", + "picomatch": "^4.0.3", "string-argv": "^0.3.2", - "yaml": "^2.8.1" + "tinyexec": "^1.0.4", + "yaml": "^2.8.2" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -8543,6 +8551,18 @@ "url": "https://opencollective.com/lint-staged" } }, + "node_modules/lint-staged/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/listr2": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", @@ -8685,9 +8705,9 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "dev": true }, "node_modules/lodash.debounce": { @@ -9230,9 +9250,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -9495,18 +9515,6 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, - "node_modules/nano-spawn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz", - "integrity": "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==", - "dev": true, - "engines": { - "node": ">=20.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/nano-spawn?sponsor=1" - } - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -10510,18 +10518,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", - "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -11387,15 +11383,15 @@ } }, "node_modules/serve-handler": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", - "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.7.tgz", + "integrity": "sha512-CinAq1xWb0vR3twAv9evEU8cNWkXCb9kd5ePAHUKJBkOsUpR1wt/CvGdeca7vqumL1U5cSaeVQ6zZMxiJ3yWsg==", "dev": true, "dependencies": { "bytes": "3.0.0", "content-disposition": "0.5.2", "mime-types": "2.1.18", - "minimatch": "3.1.2", + "minimatch": "3.1.5", "path-is-inside": "1.0.2", "path-to-regexp": "3.3.0", "range-parser": "1.2.0" @@ -11551,15 +11547,15 @@ "dev": true }, "node_modules/sinon": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.1.tgz", - "integrity": "sha512-Z0NVCW45W8Mg5oC/27/+fCqIHFnW8kpkFOq0j9XJIev4Ld0mKmERaZv5DMLAb9fGCevjKwaEeIQz5+MBXfZcDw==", + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.3.tgz", + "integrity": "sha512-0x8TQFr8EjADhSME01u1ZK31yv2+bd6Z5NrBCHVM+n4qL1wFqbxftmeyi3bwlr49FbbzRfrqSFOpyHCOh/YmYA==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.1", - "@sinonjs/fake-timers": "^15.1.0", - "@sinonjs/samsam": "^8.0.3", - "diff": "^8.0.2", + "@sinonjs/fake-timers": "^15.1.1", + "@sinonjs/samsam": "^9.0.3", + "diff": "^8.0.3", "supports-color": "^7.2.0" }, "funding": { @@ -11568,9 +11564,9 @@ } }, "node_modules/sinon/node_modules/diff": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", - "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", "dev": true, "engines": { "node": ">=0.3.1" @@ -11947,9 +11943,9 @@ } }, "node_modules/start-server-and-test": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.1.3.tgz", - "integrity": "sha512-k4EcbNjeg0odaDkAMlIeDVDByqX9PIgL4tivgP2tES6Zd8o+4pTq/HgbWCyA3VHIoZopB+wGnNPKYGGSByNriQ==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.1.5.tgz", + "integrity": "sha512-A/SbXpgXE25ScSkpLLqvGvVZT0ykN6+AzS8tVqMBCTxbJy2Nwuen59opT+afalK5aS+AuQmZs0EsLwjnuDN+/g==", "dev": true, "dependencies": { "arg": "^5.0.2", @@ -11959,7 +11955,7 @@ "execa": "5.1.1", "lazy-ass": "1.6.0", "ps-tree": "1.2.0", - "wait-on": "9.0.3" + "wait-on": "9.0.4" }, "bin": { "server-test": "src/bin/start.js", @@ -12403,6 +12399,15 @@ "ms": "^2.1.1" } }, + "node_modules/tinyexec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", + "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "node_modules/tldts": { "version": "7.0.16", "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.16.tgz", @@ -12867,14 +12872,14 @@ } }, "node_modules/wait-on": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-9.0.3.tgz", - "integrity": "sha512-13zBnyYvFDW1rBvWiJ6Av3ymAaq8EDQuvxZnPIw3g04UqGi4TyoIJABmfJ6zrvKo9yeFQExNkOk7idQbDJcuKA==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-9.0.4.tgz", + "integrity": "sha512-k8qrgfwrPVJXTeFY8tl6BxVHiclK11u72DVKhpybHfUL/K6KM4bdyK9EhIVYGytB5MJe/3lq4Tf0hrjM+pvJZQ==", "dev": true, "dependencies": { - "axios": "^1.13.2", - "joi": "^18.0.1", - "lodash": "^4.17.21", + "axios": "^1.13.5", + "joi": "^18.0.2", + "lodash": "^4.17.23", "minimist": "^1.2.8", "rxjs": "^7.8.2" }, @@ -13197,15 +13202,18 @@ "dev": true }, "node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "dev": true, "bin": { "yaml": "bin.mjs" }, "engines": { "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/yargs": { @@ -14291,9 +14299,9 @@ } }, "@babel/preset-env": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.0.tgz", - "integrity": "sha512-fNEdfc0yi16lt6IZo2Qxk3knHVdfMYX33czNb4v8yWhemoBhibCpQK/uYHtSKIiO+p/zd3+8fYVXhQdOVV608w==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.2.tgz", + "integrity": "sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==", "dev": true, "requires": { "@babel/compat-data": "^7.29.0", @@ -14380,9 +14388,9 @@ } }, "@babel/runtime-corejs3": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.0.tgz", - "integrity": "sha512-TgUkdp71C9pIbBcHudc+gXZnihEDOjUAmXO1VO4HHGES7QLZcShR0stfKIxLSNIYx2fqhmJChOjm/wkF8wv4gA==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.2.tgz", + "integrity": "sha512-Lc94FOD5+0aXhdb0Tdg3RUtqT6yWbI/BbFWvlaSJ3gAb9Ks+99nHRDKADVqC37er4eCB0fHyWT+y+K3QOvJKbw==", "dev": true, "requires": { "core-js-pure": "^3.48.0" @@ -14619,9 +14627,9 @@ "dev": true }, "@hapi/tlds": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.4.tgz", - "integrity": "sha512-Fq+20dxsxLaUn5jSSWrdtSRcIUba2JquuorF9UW1wIJS5cSUwxIsO2GIhaWynPRflvxSzFN+gxKte2HEW1OuoA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.6.tgz", + "integrity": "sha512-xdi7A/4NZokvV0ewovme3aUO5kQhW9pQ2YD1hRqZGhhSi5rBv4usHYidVocXSi9eihYsznZxLtAiEYYUL6VBGw==", "dev": true }, "@hapi/topo": { @@ -14925,18 +14933,18 @@ } }, "@sinonjs/fake-timers": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.1.0.tgz", - "integrity": "sha512-cqfapCxwTGsrR80FEgOoPsTonoefMBY7dnUEbQ+GRcved0jvkJLzvX6F4WtN+HBqbPX/SiFsIRUp+IrCW/2I2w==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.2.1.tgz", + "integrity": "sha512-QdfpQFIwYrTK8lFsII4bJ1AO1ZLbw7B+oxfP+/qSsiTrVerFp7aY2O+d2GNGrTxP58ezEbjbf7mTTLMsd7M7XQ==", "dev": true, "requires": { "@sinonjs/commons": "^3.0.1" } }, "@sinonjs/samsam": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.3.tgz", - "integrity": "sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-9.0.3.tgz", + "integrity": "sha512-ZgYY7Dc2RW+OUdnZ1DEHg00lhRt+9BjymPKHog4PRFzr1U3MbK57+djmscWyKxzO1qfunHqs4N45WWyKIFKpiQ==", "dev": true, "requires": { "@sinonjs/commons": "^3.0.1", @@ -14958,9 +14966,9 @@ "dev": true }, "@standard-schema/spec": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", - "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true }, "@testim/chrome-version": { @@ -15232,14 +15240,22 @@ "dev": true }, "axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.14.0.tgz", + "integrity": "sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==", "dev": true, "requires": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" + }, + "dependencies": { + "proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "dev": true + } } }, "babel-plugin-polyfill-corejs2": { @@ -15892,9 +15908,9 @@ } }, "commander": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", "dev": true }, "compare-func": { @@ -16332,9 +16348,9 @@ "dev": true }, "core-js": { - "version": "3.48.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", - "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", + "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==", "dev": true }, "core-js-compat": { @@ -17545,9 +17561,9 @@ "dev": true }, "follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "dev": true }, "for-in": { @@ -17584,9 +17600,9 @@ } }, "form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dev": true, "requires": { "asynckit": "^0.4.0", @@ -17933,9 +17949,9 @@ } }, "globals": { - "version": "17.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz", - "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==", + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz", + "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==", "dev": true }, "globule": { @@ -19012,9 +19028,9 @@ } }, "joi": { - "version": "18.0.2", - "resolved": "https://registry.npmjs.org/joi/-/joi-18.0.2.tgz", - "integrity": "sha512-RuCOQMIt78LWnktPoeBL0GErkNaJPTBGcYuyaBvUOQSpcpcLfWrHPPihYdOGbV5pam9VTWbeoF7TsGiHugcjGA==", + "version": "18.1.2", + "resolved": "https://registry.npmjs.org/joi/-/joi-18.1.2.tgz", + "integrity": "sha512-rF5MAmps5esSlhCA+N1b6IYHDw9j/btzGaqfgie522jS02Ju/HXBxamlXVlKEHAxoMKQL77HWI8jlqWsFuekZA==", "dev": true, "requires": { "@hapi/address": "^5.1.1", @@ -19023,7 +19039,7 @@ "@hapi/pinpoint": "^2.0.1", "@hapi/tlds": "^1.1.1", "@hapi/topo": "^6.0.2", - "@standard-schema/spec": "^1.0.0" + "@standard-schema/spec": "^1.1.0" } }, "jquery": { @@ -19516,18 +19532,25 @@ } }, "lint-staged": { - "version": "16.2.7", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.7.tgz", - "integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.4.0.tgz", + "integrity": "sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==", "dev": true, "requires": { - "commander": "^14.0.2", + "commander": "^14.0.3", "listr2": "^9.0.5", - "micromatch": "^4.0.8", - "nano-spawn": "^2.0.0", - "pidtree": "^0.6.0", + "picomatch": "^4.0.3", "string-argv": "^0.3.2", - "yaml": "^2.8.1" + "tinyexec": "^1.0.4", + "yaml": "^2.8.2" + }, + "dependencies": { + "picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true + } } }, "listr2": { @@ -19631,9 +19654,9 @@ } }, "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "dev": true }, "lodash.debounce": { @@ -20017,9 +20040,9 @@ "dev": true }, "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -20206,12 +20229,6 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, - "nano-spawn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz", - "integrity": "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==", - "dev": true - }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -20981,12 +20998,6 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, - "pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", - "dev": true - }, "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -21648,15 +21659,15 @@ } }, "serve-handler": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", - "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.7.tgz", + "integrity": "sha512-CinAq1xWb0vR3twAv9evEU8cNWkXCb9kd5ePAHUKJBkOsUpR1wt/CvGdeca7vqumL1U5cSaeVQ6zZMxiJ3yWsg==", "dev": true, "requires": { "bytes": "3.0.0", "content-disposition": "0.5.2", "mime-types": "2.1.18", - "minimatch": "3.1.2", + "minimatch": "3.1.5", "path-is-inside": "1.0.2", "path-to-regexp": "3.3.0", "range-parser": "1.2.0" @@ -21779,22 +21790,22 @@ "dev": true }, "sinon": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.1.tgz", - "integrity": "sha512-Z0NVCW45W8Mg5oC/27/+fCqIHFnW8kpkFOq0j9XJIev4Ld0mKmERaZv5DMLAb9fGCevjKwaEeIQz5+MBXfZcDw==", + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.3.tgz", + "integrity": "sha512-0x8TQFr8EjADhSME01u1ZK31yv2+bd6Z5NrBCHVM+n4qL1wFqbxftmeyi3bwlr49FbbzRfrqSFOpyHCOh/YmYA==", "dev": true, "requires": { "@sinonjs/commons": "^3.0.1", - "@sinonjs/fake-timers": "^15.1.0", - "@sinonjs/samsam": "^8.0.3", - "diff": "^8.0.2", + "@sinonjs/fake-timers": "^15.1.1", + "@sinonjs/samsam": "^9.0.3", + "diff": "^8.0.3", "supports-color": "^7.2.0" }, "dependencies": { "diff": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", - "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", "dev": true } } @@ -22089,9 +22100,9 @@ } }, "start-server-and-test": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.1.3.tgz", - "integrity": "sha512-k4EcbNjeg0odaDkAMlIeDVDByqX9PIgL4tivgP2tES6Zd8o+4pTq/HgbWCyA3VHIoZopB+wGnNPKYGGSByNriQ==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.1.5.tgz", + "integrity": "sha512-A/SbXpgXE25ScSkpLLqvGvVZT0ykN6+AzS8tVqMBCTxbJy2Nwuen59opT+afalK5aS+AuQmZs0EsLwjnuDN+/g==", "dev": true, "requires": { "arg": "^5.0.2", @@ -22101,7 +22112,7 @@ "execa": "5.1.1", "lazy-ass": "1.6.0", "ps-tree": "1.2.0", - "wait-on": "9.0.3" + "wait-on": "9.0.4" }, "dependencies": { "debug": { @@ -22442,6 +22453,12 @@ } } }, + "tinyexec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", + "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", + "dev": true + }, "tldts": { "version": "7.0.16", "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.16.tgz", @@ -22766,14 +22783,14 @@ } }, "wait-on": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-9.0.3.tgz", - "integrity": "sha512-13zBnyYvFDW1rBvWiJ6Av3ymAaq8EDQuvxZnPIw3g04UqGi4TyoIJABmfJ6zrvKo9yeFQExNkOk7idQbDJcuKA==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-9.0.4.tgz", + "integrity": "sha512-k8qrgfwrPVJXTeFY8tl6BxVHiclK11u72DVKhpybHfUL/K6KM4bdyK9EhIVYGytB5MJe/3lq4Tf0hrjM+pvJZQ==", "dev": true, "requires": { - "axios": "^1.13.2", - "joi": "^18.0.1", - "lodash": "^4.17.21", + "axios": "^1.13.5", + "joi": "^18.0.2", + "lodash": "^4.17.23", "minimist": "^1.2.8", "rxjs": "^7.8.2" } @@ -23015,9 +23032,9 @@ "dev": true }, "yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "dev": true }, "yargs": { diff --git a/package.json b/package.json index 239cdd0683..918e2522e8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "axe-core", "description": "Accessibility engine for automated Web UI testing", - "version": "4.11.2", + "version": "4.11.3", "license": "MPL-2.0", "engines": { "node": ">=4" diff --git a/sri-history.json b/sri-history.json index ad8030df6f..4bf35a11c9 100644 --- a/sri-history.json +++ b/sri-history.json @@ -406,5 +406,9 @@ "4.11.2": { "axe.js": "sha256-l0az3GGxixpCq0IHsMxs3F4nvNGl/89Ypf8icbh6Xfk=", "axe.min.js": "sha256-dacAeIgHzdkplnXlC66fYnqfnzaOkpb+pxf3Ejreyxo=" + }, + "4.11.3": { + "axe.js": "sha256-MgD9sqZmQpnqTg+i7glZ32Zo9TQZ81mOpgg0HABdwoE=", + "axe.min.js": "sha256-BWVH7wRf9Q7eSaVMmaNWiQ/4lAxUFqVKnJEAhsVFM90=" } } diff --git a/test/checks/aria/aria-allowed-attr-elm.js b/test/checks/aria/aria-allowed-attr-elm.js new file mode 100644 index 0000000000..2d90568e2f --- /dev/null +++ b/test/checks/aria/aria-allowed-attr-elm.js @@ -0,0 +1,92 @@ +describe('aria-allowed-attr-elm', () => { + 'use strict'; + + const queryFixture = axe.testUtils.queryFixture; + const checkContext = axe.testUtils.MockCheckContext(); + + afterEach(() => { + checkContext.reset(); + }); + + it('should pass for br with aria-hidden', () => { + const vNode = queryFixture(''); + + assert.isTrue( + axe.testUtils + .getCheckEvaluate('aria-allowed-attr-elm') + .call(checkContext, null, null, vNode) + ); + }); + + it('should pass for wbr with aria-hidden', () => { + const vNode = queryFixture(''); + + assert.isTrue( + axe.testUtils + .getCheckEvaluate('aria-allowed-attr-elm') + .call(checkContext, null, null, vNode) + ); + }); + + it('should fail for br with disallowed aria attribute', () => { + const vNode = queryFixture('
'); + + assert.isFalse( + axe.testUtils + .getCheckEvaluate('aria-allowed-attr-elm') + .call(checkContext, null, null, vNode) + ); + assert.deepEqual(checkContext._data, { + messageKey: 'singular', + nodeName: 'br', + values: 'aria-busy="true"' + }); + }); + + it('should fail for wbr with disallowed aria attribute', () => { + const vNode = queryFixture(''); + + assert.isFalse( + axe.testUtils + .getCheckEvaluate('aria-allowed-attr-elm') + .call(checkContext, null, null, vNode) + ); + assert.deepEqual(checkContext._data, { + messageKey: 'singular', + nodeName: 'wbr', + values: 'aria-busy="true"' + }); + }); + + it('should pass for br with non-global aria attribute', () => { + const vNode = queryFixture('
'); + + assert.isTrue( + axe.testUtils + .getCheckEvaluate('aria-allowed-attr-elm') + .call(checkContext, null, null, vNode) + ); + }); + + it('should pass for br with explicit role', () => { + const vNode = queryFixture( + '
' + ); + + assert.isTrue( + axe.testUtils + .getCheckEvaluate('aria-allowed-attr-elm') + .call(checkContext, null, null, vNode) + ); + }); + + it('should pass for element without allowedAriaAttrs restriction', () => { + const vNode = queryFixture('
'); + + assert.isTrue( + axe.testUtils + .getCheckEvaluate('aria-allowed-attr-elm') + .call(checkContext, null, null, vNode) + ); + }); +}); diff --git a/test/commons/dom/is-fixed-position.js b/test/commons/dom/is-fixed-position.js new file mode 100644 index 0000000000..c6ff247e44 --- /dev/null +++ b/test/commons/dom/is-fixed-position.js @@ -0,0 +1,104 @@ +describe('dom.isFixedPosition', () => { + const isFixedPosition = axe.commons.dom.isFixedPosition; + const { queryFixture, queryShadowFixture } = axe.testUtils; + + it('returns true for element with "position: fixed"', () => { + const vNode = queryFixture( + '
' + ); + + assert.isTrue(isFixedPosition(vNode)); + }); + + it('returns false for element without position', () => { + const vNode = queryFixture('
'); + + assert.isFalse(isFixedPosition(vNode)); + }); + + for (const position of ['relative', 'absolute', 'sticky']) { + it(`returns false for element with "position: ${position}"`, () => { + const vNode = queryFixture( + `
` + ); + + assert.isFalse(isFixedPosition(vNode)); + }); + } + + it('returns true for ancestor with position: fixed', () => { + const vNode = queryFixture( + '
' + ); + + assert.isTrue(isFixedPosition(vNode)); + }); + + it('returns true for ancestor with "position: fixed" even when the element is positioned differently', () => { + const vNode = queryFixture( + '
' + ); + + assert.isTrue(isFixedPosition(vNode)); + }); + + it('returns false on detached elements', function () { + var el = document.createElement('div'); + el.innerHTML = 'I am not visible because I am detached!'; + + assert.isFalse(axe.commons.dom.isFixedPosition(el)); + }); + + describe('options.skipAncestors', () => { + it('returns false for ancestor with "position: fixed"', () => { + const vNode = queryFixture( + '
' + ); + + assert.isFalse(isFixedPosition(vNode, { skipAncestors: true })); + }); + }); + + describe('Shadow DOM', () => { + it('returns false when no element in the composed tree has position: fixed', () => { + const vNode = queryShadowFixture( + '
', + '' + ); + assert.isFalse(isFixedPosition(vNode)); + }); + + it('returns true for element in shadow tree with position: fixed', () => { + const vNode = queryShadowFixture( + '
', + '
' + ); + + assert.isTrue(isFixedPosition(vNode)); + }); + + it('returns true when ancestor outside shadow tree has position: fixed', () => { + const vNode = queryShadowFixture( + '
', + '' + ); + assert.isTrue(isFixedPosition(vNode)); + }); + + it('returns true when ancestor outside shadow is fixed and target in shadow has a different position', () => { + const vNode = queryShadowFixture( + '
', + '' + ); + assert.isTrue(isFixedPosition(vNode)); + }); + + it('returns false with skipAncestors when only ancestor outside shadow tree is fixed', () => { + const vNode = queryShadowFixture( + '
', + '' + ); + assert.isFalse(isFixedPosition(vNode, { skipAncestors: true })); + }); + }); +}); diff --git a/test/commons/dom/is-offscreen.js b/test/commons/dom/is-offscreen.js index e755eda1a2..4455c3a16f 100644 --- a/test/commons/dom/is-offscreen.js +++ b/test/commons/dom/is-offscreen.js @@ -1,161 +1,247 @@ -describe('dom.isOffscreen', function () { - 'use strict'; - var fixture = document.getElementById('fixture'); - var shadowSupport = axe.testUtils.shadowSupport; +describe('dom.isOffscreen', () => { + const { isOffscreen } = axe.commons.dom; + const fixture = document.getElementById('fixture'); + const { queryFixture, fixtureSetup, flatTreeSetup, shadowSupport } = + axe.testUtils; - afterEach(function () { - fixture.innerHTML = ''; + afterEach(() => { document.body.style.direction = 'ltr'; }); - it('should detect elements positioned outside the left edge', function () { - fixture.innerHTML = - '
Offscreen?
'; - var el = document.getElementById('target'); + after(() => { + window.scrollTo(0, 0); + }); + + it('should be false for 0 height elements at the top of the viewport', () => { + assert.isFalse(isOffscreen(document.body)); + assert.isFalse(isOffscreen(document.documentElement)); + + const vNode = queryFixture('
'); + assert.isFalse(isOffscreen(vNode)); + }); - assert.isTrue(axe.commons.dom.isOffscreen(el)); + it('should detect elements positioned outside the left edge', () => { + const vNode = queryFixture( + '
Offscreen?
' + ); + assert.isTrue(isOffscreen(vNode)); }); - it('should detect elements positioned to but not beyond the left edge', function () { - fixture.innerHTML = - '
Offscreen?
'; - var el = document.getElementById('target'); + it('should detect elements positioned to but not beyond the left edge', () => { + const vNode = queryFixture( + '
Offscreen?
' + ); - assert.isTrue(axe.commons.dom.isOffscreen(el)); + assert.isTrue(isOffscreen(vNode)); }); - it('should not detect elements at the left edge with a zero width', function () { - fixture.innerHTML = - '
'; - var el = document.getElementById('target'); + it('should not detect elements at the left edge with a zero width', () => { + const vNode = queryFixture( + '
' + ); - assert.isFalse(axe.commons.dom.isOffscreen(el)); + assert.isFalse(isOffscreen(vNode)); }); - it('should detect elements positioned outside the top edge', function () { - fixture.innerHTML = - '
Offscreen?
'; - var el = document.getElementById('target'); - assert.isTrue(axe.commons.dom.isOffscreen(el)); + it('should detect elements positioned outside the top edge', () => { + const vNode = queryFixture( + '
Offscreen?
' + ); + assert.isTrue(isOffscreen(vNode)); }); - it('should never detect elements positioned outside the bottom edge', function () { - fixture.innerHTML = - '
Offscreen?
'; - var el = document.getElementById('target'); + it('should never detect elements positioned outside the bottom edge', () => { + const vNode = queryFixture( + '
Offscreen?
' + ); - assert.isFalse(axe.commons.dom.isOffscreen(el)); + assert.isFalse(isOffscreen(vNode)); }); - it('should detect elements positioned that bleed inside the left edge', function () { - fixture.innerHTML = - '
Offscreen?
'; - var el = document.getElementById('target'); + it('should detect elements positioned that bleed inside the left edge', () => { + const vNode = queryFixture( + '
Offscreen?
' + ); - assert.isFalse(axe.commons.dom.isOffscreen(el)); + assert.isFalse(isOffscreen(vNode)); }); - it('should detect elements positioned outside the right edge', function () { - fixture.innerHTML = - '
Offscreen?
'; - var el = document.getElementById('target'); + it('should detect elements positioned outside the right edge', () => { + const vNode = queryFixture( + '
Offscreen?
' + ); - assert.isFalse(axe.commons.dom.isOffscreen(el)); + assert.isFalse(isOffscreen(vNode)); }); - it('should detect elements positioned outside the top edge', function () { - fixture.innerHTML = - '
Offscreen?
'; - var el = document.getElementById('target'); + it('should detect elements positioned outside the top edge', () => { + const vNode = queryFixture( + '
Offscreen?
' + ); - assert.isFalse(axe.commons.dom.isOffscreen(el)); + assert.isFalse(isOffscreen(vNode)); }); - it('should detect elements positioned outside the bottom edge', function () { - fixture.innerHTML = - '
Offscreen?
'; - var el = document.getElementById('target'); + it('should detect elements positioned outside the bottom edge', () => { + const vNode = queryFixture( + '
Offscreen?
' + ); - assert.isFalse(axe.commons.dom.isOffscreen(el)); + assert.isFalse(isOffscreen(vNode)); }); - it('should detect elements that are made off-screen by a parent', function () { - fixture.innerHTML = - '
' + - '
Offscreen?
' + - '
'; - - var el = document.getElementById('target'); + it('should detect elements that are made off-screen by a parent', () => { + const vNode = queryFixture(` +
+
Offscreen?
+
+ `); - assert.isTrue(axe.commons.dom.isOffscreen(el)); + assert.isTrue(isOffscreen(vNode)); }); - it('should NOT detect elements positioned outside the right edge on LTR documents', function () { - fixture.innerHTML = - '
Offscreen?
'; - var el = document.getElementById('target'); + it('should NOT detect elements positioned outside the right edge on LTR documents', () => { + const vNode = queryFixture( + '
Offscreen?
' + ); - assert.isFalse(axe.commons.dom.isOffscreen(el)); + assert.isFalse(isOffscreen(vNode)); }); - it('should detect elements positioned outside the right edge on RTL documents', function () { + it('should detect elements positioned outside the right edge on RTL documents', () => { document.body.style.direction = 'rtl'; - fixture.innerHTML = - '
Offscreen?
'; - var el = document.getElementById('target'); + const vNode = queryFixture( + '
Offscreen?
' + ); + assert.isTrue(isOffscreen(vNode)); + }); - assert.isTrue(axe.commons.dom.isOffscreen(el)); + it('should NOT detect elements positioned outside the left edge on RTL documents', () => { + document.body.style.direction = 'rtl'; + const vNode = queryFixture( + '
Offscreen?
' + ); + + assert.isFalse(isOffscreen(vNode)); }); - it('should NOT detect elements positioned outside the left edge on RTL documents', function () { + it('should detect elements positioned outside the right edge on RTL documents', () => { document.body.style.direction = 'rtl'; - fixture.innerHTML = - '
Offscreen?
'; - var el = document.getElementById('target'); - - assert.isFalse(axe.commons.dom.isOffscreen(el)); - }); - - it('should not detect elements positioned because of a scroll', function () { - fixture.innerHTML = - '
' + - '
goobye
' + - '
high
' + - '
hello
' + - '
'; - var viz = document.getElementById('visible'); - assert.isFalse(axe.commons.dom.isOffscreen(viz)); - var scrollme = document.getElementById('scrollme'); + const vNode = queryFixture( + '
Offscreen?
' + ); + + assert.isTrue(isOffscreen(vNode)); + }); + + it('should not detect elements positioned because of a scroll', () => { + fixtureSetup(` +
+
goodbye
+
high
+
hello
+
+ `); + const viz = document.getElementById('visible'); + assert.isFalse(isOffscreen(viz)); + const scrollme = document.getElementById('scrollme'); scrollme.scrollIntoView(); - assert.isFalse(axe.commons.dom.isOffscreen(viz)); + assert.isFalse(isOffscreen(viz)); }); - it('should return undefined if actual ndoe is undefined', function () { - assert.isUndefined(axe.commons.dom.isOffscreen()); + it('should return undefined if actual node is undefined', () => { + assert.isUndefined(isOffscreen()); }); - (shadowSupport.v1 ? it : xit)( - 'should detect on screen shadow nodes', - function () { - fixture.innerHTML = '
'; - var shadow = fixture.querySelector('div').attachShadow({ mode: 'open' }); - shadow.innerHTML = '
Offscreen?
'; - - var el = shadow.querySelector('#target'); - assert.isFalse(axe.commons.dom.isOffscreen(el)); - } - ); + (shadowSupport.v1 ? it : xit)('should detect on screen shadow nodes', () => { + fixture.innerHTML = '
'; + const shadow = fixture.querySelector('div').attachShadow({ mode: 'open' }); + shadow.innerHTML = '
Offscreen?
'; + flatTreeSetup(fixture); - (shadowSupport.v1 ? it : xit)( - 'should detect off screen shadow nodes', - function () { - fixture.innerHTML = '
'; - var shadow = fixture.querySelector('div').attachShadow({ mode: 'open' }); - shadow.innerHTML = - '
Offscreen?
'; + const el = shadow.querySelector('#target'); + assert.isFalse(isOffscreen(el)); + }); - var el = shadow.querySelector('#target'); - assert.isTrue(axe.commons.dom.isOffscreen(el)); - } - ); + (shadowSupport.v1 ? it : xit)('should detect off screen shadow nodes', () => { + fixture.innerHTML = '
'; + const shadow = fixture.querySelector('div').attachShadow({ mode: 'open' }); + shadow.innerHTML = + '
Offscreen?
'; + flatTreeSetup(fixture); + + const el = shadow.querySelector('#target'); + assert.isTrue(isOffscreen(el)); + }); + + describe('positioned: fixed', () => { + it('should detect elements positioned outside the top edge', () => { + const vNode = queryFixture( + '
Offscreen?
' + ); + + assert.isTrue(isOffscreen(vNode)); + }); + + it('should detect elements positioned outside the top edge when scrolled', () => { + const vNode = queryFixture(` +
+
Offscreen?
+
+ `); + + assert.isTrue(isOffscreen(vNode)); + window.scrollTo(0, document.body.scrollHeight); + assert.isTrue(isOffscreen(vNode)); + }); + + it('should detect elements positioned outside the bottom edge', () => { + const vNode = queryFixture( + `
Offscreen?
` + ); + assert.isTrue(isOffscreen(vNode)); + }); + + it('should consider elements in the viewport, but beyond the window size as on screen', () => { + const vNode = queryFixture(` +
+

Hello World

+
+ Offscreen? +
+
+ `); + assert.isFalse(isOffscreen(vNode)); + }); + + it('should detect elements positioned outside the right edge (LTR)', () => { + const vNode = queryFixture( + `
Offscreen?
` + ); + assert.isTrue(isOffscreen(vNode)); + }); + + it('should detect elements positioned outside the right edge (RTL)', () => { + document.body.style.direction = 'rtl'; + const vNode = queryFixture( + `
Offscreen?
` + ); + assert.isTrue(isOffscreen(vNode)); + }); + + it('should detect elements positioned outside the left edge (LTR)', () => { + const vNode = queryFixture( + `
Offscreen?
` + ); + assert.isTrue(isOffscreen(vNode)); + }); + + it('should detect elements positioned outside the left edge on RTL documents', () => { + document.body.style.direction = 'rtl'; + const vNode = queryFixture( + `
Offscreen?
` + ); + assert.isTrue(isOffscreen(vNode)); + }); + }); }); diff --git a/test/commons/dom/is-visible.js b/test/commons/dom/is-visible.js index db85fafc3c..7ea710a2af 100644 --- a/test/commons/dom/is-visible.js +++ b/test/commons/dom/is-visible.js @@ -5,6 +5,7 @@ describe('dom.isVisible', function () { var queryFixture = axe.testUtils.queryFixture; var shadowSupported = axe.testUtils.shadowSupport.v1; var computedStyleStub; + var flatTreeSetup = axe.testUtils.flatTreeSetup; afterEach(function () { document.getElementById('fixture').innerHTML = ''; @@ -21,6 +22,7 @@ describe('dom.isVisible', function () { it('should return false if computedStyle return null for whatever reason', function () { computedStyleStub = sinon.stub(window, 'getComputedStyle').returns(null); var el = document.createElement('div'); + flatTreeSetup(fixture); assert.isFalse(axe.commons.dom.isVisible(el)); }); @@ -28,6 +30,7 @@ describe('dom.isVisible', function () { fixture.innerHTML = '
Hello!
'; var el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isTrue(axe.commons.dom.isVisible(el)); }); @@ -36,6 +39,7 @@ describe('dom.isVisible', function () { '
hi
'; var el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isTrue(axe.commons.dom.isVisible(el)); }); @@ -44,6 +48,7 @@ describe('dom.isVisible', function () { '
StickySticky
'; var el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isTrue(axe.commons.dom.isVisible(el)); }); @@ -53,6 +58,7 @@ describe('dom.isVisible', function () { '
Hi
' + ''; var el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isTrue(axe.commons.dom.isVisible(el)); }); @@ -60,6 +66,7 @@ describe('dom.isVisible', function () { fixture.innerHTML = '
Hi
'; var el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isFalse(axe.commons.dom.isVisible(el)); }); @@ -67,6 +74,7 @@ describe('dom.isVisible', function () { fixture.innerHTML = '
Hi
'; var el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isFalse(axe.commons.dom.isVisible(el)); }); @@ -110,6 +118,7 @@ describe('dom.isVisible', function () { '
Hi
'; var el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isTrue(axe.commons.dom.isVisible(el)); }); @@ -118,6 +127,7 @@ describe('dom.isVisible', function () { ''; var el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isTrue(axe.commons.dom.isVisible(el)); }); @@ -128,6 +138,7 @@ describe('dom.isVisible', function () { ''; var el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isTrue(axe.commons.dom.isVisible(el)); }); @@ -138,6 +149,7 @@ describe('dom.isVisible', function () { ''; var el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isTrue(axe.commons.dom.isVisible(el)); }); @@ -153,6 +165,7 @@ describe('dom.isVisible', function () { fixture.innerHTML = '
Hi
'; el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isFalse(axe.commons.dom.isVisible(el)); }); @@ -168,6 +181,7 @@ describe('dom.isVisible', function () { fixture.innerHTML = '
Hi
'; el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isFalse(axe.commons.dom.isVisible(el)); }); @@ -182,6 +196,7 @@ describe('dom.isVisible', function () { fixture.innerHTML = '
Hi
'; el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isTrue(axe.commons.dom.isVisible(el)); }); @@ -198,6 +213,7 @@ describe('dom.isVisible', function () { '
' + '
Hi
' + '
'; el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isFalse(axe.commons.dom.isVisible(el)); }); @@ -213,6 +229,7 @@ describe('dom.isVisible', function () { '
' + '
Hi
' + '
'; el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isTrue(axe.commons.dom.isVisible(el)); }); @@ -228,6 +245,7 @@ describe('dom.isVisible', function () { fixture.innerHTML = '
Hi
'; el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isFalse(axe.commons.dom.isVisible(el)); }); @@ -235,6 +253,7 @@ describe('dom.isVisible', function () { fixture.innerHTML = ''; var el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isFalse(axe.commons.dom.isVisible(el)); }); @@ -242,6 +261,7 @@ describe('dom.isVisible', function () { fixture.innerHTML = '
Hello!
'; var el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isFalse(axe.commons.dom.isVisible(el)); }); @@ -249,6 +269,7 @@ describe('dom.isVisible', function () { fixture.innerHTML = '
Hello!
'; var el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isFalse(axe.commons.dom.isVisible(el)); }); @@ -257,6 +278,7 @@ describe('dom.isVisible', function () { '
Hello!
'; var el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isFalse(axe.commons.dom.isVisible(el)); }); @@ -265,6 +287,7 @@ describe('dom.isVisible', function () { '
Hello!
'; var el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isFalse(axe.commons.dom.isVisible(el)); }); @@ -342,6 +365,7 @@ describe('dom.isVisible', function () { '
Hi
'; var el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isFalse(axe.commons.dom.isVisible(el)); }); @@ -352,6 +376,7 @@ describe('dom.isVisible', function () { ''; var el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isFalse(axe.commons.dom.isVisible(el)); }); @@ -403,6 +428,7 @@ describe('dom.isVisible', function () { '
StickySticky
'; var el = document.getElementById('target'); + flatTreeSetup(fixture); assert.isFalse(axe.commons.dom.isVisible(el)); }); }); diff --git a/test/integration/full/meta-refresh-no-exceptions/meta-refresh-pass.js b/test/integration/full/meta-refresh-no-exceptions/meta-refresh-pass.js index 6cd665da31..c663d3af6d 100644 --- a/test/integration/full/meta-refresh-no-exceptions/meta-refresh-pass.js +++ b/test/integration/full/meta-refresh-no-exceptions/meta-refresh-pass.js @@ -3,7 +3,6 @@ describe('meta-refresh-no-exceptions pass', function () { it('should pass', function (done) { axe.run({ runOnly: 'meta-refresh-no-exceptions' }, function (err, results) { - console.log(results); try { assert.isNull(err); assert.lengthOf(results.violations, 0, 'violations'); diff --git a/test/integration/full/target-size/fixed-scroll.html b/test/integration/full/target-size/fixed-scroll.html new file mode 100644 index 0000000000..0be7c89b28 --- /dev/null +++ b/test/integration/full/target-size/fixed-scroll.html @@ -0,0 +1,40 @@ + + + + Target-size position: fixed and scrolled + + + + + + + +
+ +
+

+ +

+

+ +

+
+ + + + + diff --git a/test/integration/full/target-size/fixed-scroll.js b/test/integration/full/target-size/fixed-scroll.js new file mode 100644 index 0000000000..e68bcbaf8d --- /dev/null +++ b/test/integration/full/target-size/fixed-scroll.js @@ -0,0 +1,45 @@ +describe('target-size position: fixed and scrolled', () => { + 'use strict'; + let results; + + before(done => { + axe.testUtils.awaitNestedLoad(() => { + window.scrollTo(0, document.body.scrollHeight); + const options = { + runOnly: ['target-size'], + elementRef: true + }; + const context = { + // ignore the mocha links + exclude: '#mocha' + }; + axe.run(context, options, (err, r) => { + if (err) { + return done(err); + } + results = r; + done(); + }); + }); + }); + + describe('violations', function () { + it('should find 0', function () { + assert.lengthOf(results.violations, 0); + }); + }); + + describe('passes', function () { + it('should find 2', function () { + assert.lengthOf(results.passes[0].nodes, 2); + }); + }); + + it('should find 0 inapplicable', function () { + assert.lengthOf(results.inapplicable, 0); + }); + + it('should find 0 incomplete', function () { + assert.lengthOf(results.incomplete, 0); + }); +}); diff --git a/test/integration/full/target-size/target-size.js b/test/integration/full/target-size/target-size.js index 68287e6a8b..42256a5fbb 100644 --- a/test/integration/full/target-size/target-size.js +++ b/test/integration/full/target-size/target-size.js @@ -29,7 +29,6 @@ describe('target-size test', function () { results.passes[0].nodes.forEach(function (node) { node.element.className += ' passes'; }); - console.log(results); done(); }); }); diff --git a/test/integration/full/target-size/too-many-rects.js b/test/integration/full/target-size/too-many-rects.js index d814850247..1effc114d9 100644 --- a/test/integration/full/target-size/too-many-rects.js +++ b/test/integration/full/target-size/too-many-rects.js @@ -18,7 +18,6 @@ describe('target-size too many rects test', () => { done(err); } results = r; - console.log(results); done(); }); }); diff --git a/test/integration/rules/aria-allowed-attr/failures.html b/test/integration/rules/aria-allowed-attr/failures.html index 21c84b0e50..4c582d7d4d 100644 --- a/test/integration/rules/aria-allowed-attr/failures.html +++ b/test/integration/rules/aria-allowed-attr/failures.html @@ -13,3 +13,6 @@
+ +
+ diff --git a/test/integration/rules/aria-allowed-attr/failures.json b/test/integration/rules/aria-allowed-attr/failures.json index 02d9579b81..03a4569f50 100644 --- a/test/integration/rules/aria-allowed-attr/failures.json +++ b/test/integration/rules/aria-allowed-attr/failures.json @@ -9,6 +9,8 @@ ["#fail5"], ["#fail6"], ["#fail7"], - ["#fail8"] + ["#fail8"], + ["#fail9"], + ["#fail10"] ] } diff --git a/test/integration/rules/aria-allowed-attr/passes.html b/test/integration/rules/aria-allowed-attr/passes.html index a21c68c3b1..3242c31707 100644 --- a/test/integration/rules/aria-allowed-attr/passes.html +++ b/test/integration/rules/aria-allowed-attr/passes.html @@ -2173,3 +2173,7 @@
+ + +
+ diff --git a/test/integration/rules/aria-allowed-attr/passes.json b/test/integration/rules/aria-allowed-attr/passes.json index 0974e07250..6c68e108ab 100644 --- a/test/integration/rules/aria-allowed-attr/passes.json +++ b/test/integration/rules/aria-allowed-attr/passes.json @@ -104,6 +104,8 @@ ["#pass99"], ["#pass100"], ["#pass101"], - ["#pass102"] + ["#pass102"], + ["#pass103"], + ["#pass104"] ] } diff --git a/test/integration/virtual-rules/summary-name.js b/test/integration/virtual-rules/summary-name.js index 1c711bd77c..38d7ac3119 100644 --- a/test/integration/virtual-rules/summary-name.js +++ b/test/integration/virtual-rules/summary-name.js @@ -26,7 +26,6 @@ describe('summary-name virtual-rule', () => { vSummary.children = []; appendSerialChild(vDetails, vSummary); const results = axe.runVirtualRule('summary-name', vSummary); - console.log(results); assert.lengthOf(results.passes, 0); assert.lengthOf(results.violations, 1); assert.lengthOf(results.incomplete, 0);