Skip to content

Commit dfbc245

Browse files
authored
chore: Release 4.11.4 (#5081)
### Bug Fixes - **commons/text:** exclude natively hidden elements from aria-labelledby accessible name ([#5076](#5076)) ([df34adf](df34adf)), closes [#4704](#4704) - **utils/getAncestry:** escape node name ([#5079](#5079)) ([6e68d0a](6e68d0a)), closes [#5078](#5078)
2 parents c71e3dd + be1a0ab commit dfbc245

10 files changed

Lines changed: 97 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
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.
44

5+
### [4.11.4](https://github.com/dequelabs/axe-core/compare/v4.11.3...v4.11.4) (2026-04-23)
6+
7+
### Bug Fixes
8+
9+
- **commons/text:** exclude natively hidden elements from aria-labelledby accessible name ([#5076](https://github.com/dequelabs/axe-core/issues/5076)) ([df34adf](https://github.com/dequelabs/axe-core/commit/df34adfc1967919d667d40a76ab5c85b6e47ddfe)), closes [#4704](https://github.com/dequelabs/axe-core/issues/4704)
10+
- **utils/getAncestry:** escape node name ([#5079](https://github.com/dequelabs/axe-core/issues/5079)) ([6e68d0a](https://github.com/dequelabs/axe-core/commit/6e68d0a5d26999b996152df82238bc3f3a041cb3)), closes [#5078](https://github.com/dequelabs/axe-core/issues/5078)
11+
512
### [4.11.3](https://github.com/dequelabs/axe-core/compare/v4.11.2...v4.11.3) (2026-04-13)
613

714
### Bug Fixes

bower.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "axe-core",
3-
"version": "4.11.3",
3+
"version": "4.11.4",
44
"deprecated": true,
55
"contributors": [
66
{

build/cherry-pick.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ const conventionalCommitsParser = require('conventional-commits-parser');
55
const chalk = require('chalk');
66
const { version } = require('../package.json');
77

8+
// Node's default execSync buffer (~1 MiB) is too small for `git log` on long histories.
9+
const GIT_LOG_EXEC_OPTS = { maxBuffer: 50 * 1024 * 1024 };
10+
811
const releaseType = process.argv[2];
912
let ignoreCommits = process.argv[3];
1013

@@ -50,7 +53,8 @@ function getCommits(branch) {
5053
// all commits are too large for execSync buffer size so we'll just get since the last 3 years
5154
const date = new Date(new Date().setFullYear(new Date().getFullYear() - 3));
5255
const stdout = execSync(
53-
`git log ${branch || ''} --abbrev-commit --since=${date.getFullYear()}`
56+
`git log ${branch || ''} --no-merges --abbrev-commit --since=${date.getFullYear()}`,
57+
GIT_LOG_EXEC_OPTS
5458
).toString();
5559
const allCommits = stdout
5660
.split(/commit (?=[\w\d]{8}[\n\r])/)

lib/commons/text/accessible-text-virtual.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import titleText from './title-text';
77
import sanitize from './sanitize';
88
import isVisibleToScreenReaders from '../dom/is-visible-to-screenreader';
99
import isIconLigature from '../text/is-icon-ligature';
10+
import { nativelyHidden } from '../dom/visibility-methods';
1011

1112
/**
1213
* Finds virtual node and calls accessibleTextVirtual()
@@ -84,11 +85,14 @@ function shouldIgnoreHidden(virtualNode, context) {
8485
return false;
8586
}
8687

88+
// When traversing aria-labelledby references, include hidden nodes except natively hidden elements
89+
if (context.includeHidden && !nativelyHidden(virtualNode)) {
90+
return false;
91+
}
92+
8793
if (
8894
// If the parent isn't ignored, the text node should not be either
89-
virtualNode.props.nodeType !== 1 ||
90-
// If the target of aria-labelledby is hidden, ignore all descendents
91-
context.includeHidden
95+
virtualNode.props.nodeType !== 1
9296
) {
9397
return false;
9498
}

lib/core/utils/get-ancestry.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import escapeSelector from './escape-selector';
12
import getShadowSelector from './get-shadow-selector';
23

34
function generateAncestry(node) {
4-
const nodeName = node.nodeName.toLowerCase();
5+
const nodeName = escapeSelector(node.nodeName.toLowerCase());
56
const parentElement = node.parentElement;
67
const parentNode = node.parentNode;
78

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "axe-core",
33
"description": "Accessibility engine for automated Web UI testing",
4-
"version": "4.11.3",
4+
"version": "4.11.4",
55
"license": "MPL-2.0",
66
"engines": {
77
"node": ">=4"

sri-history.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,5 +410,9 @@
410410
"4.11.3": {
411411
"axe.js": "sha256-MgD9sqZmQpnqTg+i7glZ32Zo9TQZ81mOpgg0HABdwoE=",
412412
"axe.min.js": "sha256-BWVH7wRf9Q7eSaVMmaNWiQ/4lAxUFqVKnJEAhsVFM90="
413+
},
414+
"4.11.4": {
415+
"axe.js": "sha256-MFYbxYKg+pR6osY47p1hwmvMOb7f5kMTHtPU7APqp/A=",
416+
"axe.min.js": "sha256-+4OkN42Xjst9La5Io6N3ioTJcaxpLzr9RdcU+6icDw0="
413417
}
414418
}

test/commons/text/accessible-text.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1649,6 +1649,47 @@ describe('text.accessibleTextVirtual', () => {
16491649
});
16501650
});
16511651

1652+
describe('natively hidden elements', () => {
1653+
const tags = ['style', 'script', 'noscript', 'template'];
1654+
1655+
it('should not use content as accessible name when directly referenced by aria-labelledby', () => {
1656+
tags.forEach(tag => {
1657+
fixture.innerHTML = `<div id="t1" aria-labelledby="el-id"></div><${tag} id="el-id">content</${tag}>`;
1658+
axe.testUtils.flatTreeSetup(fixture);
1659+
const target = axe.utils.querySelectorAll(axe._tree, '#t1')[0];
1660+
assert.equal(axe.commons.text.accessibleTextVirtual(target), '', tag);
1661+
});
1662+
});
1663+
1664+
it('should not use content when inside a hidden container referenced by aria-labelledby', () => {
1665+
tags.forEach(tag => {
1666+
fixture.innerHTML = `
1667+
<div id="t1" aria-labelledby="hidden-container"></div>
1668+
<div id="hidden-container" aria-hidden="true">
1669+
<${tag}>content</${tag}>
1670+
Expected label
1671+
</div>
1672+
`;
1673+
axe.testUtils.flatTreeSetup(fixture);
1674+
const target = axe.utils.querySelectorAll(axe._tree, '#t1')[0];
1675+
assert.equal(
1676+
axe.commons.text.accessibleTextVirtual(target),
1677+
'Expected label',
1678+
tag
1679+
);
1680+
});
1681+
});
1682+
1683+
it('should ignore aria-label as accessible name when directly referenced by aria-labelledby', () => {
1684+
tags.forEach(tag => {
1685+
fixture.innerHTML = `<div id="t1" aria-labelledby="el-id"></div><${tag} id="el-id" aria-label="aria-label"></${tag}>`;
1686+
axe.testUtils.flatTreeSetup(fixture);
1687+
const target = axe.utils.querySelectorAll(axe._tree, '#t1')[0];
1688+
assert.equal(axe.commons.text.accessibleTextVirtual(target), '', tag);
1689+
});
1690+
});
1691+
});
1692+
16521693
describe('text.accessibleText acceptance tests', () => {
16531694
'use strict';
16541695
// Tests borrowed from the AccName 1.1 testing docs

test/core/utils/get-ancestry.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,31 @@ describe('axe.utils.getAncestry', () => {
7171
'div:nth-child(2)'
7272
]);
7373
});
74+
75+
it('escapes the node name', () => {
76+
fixture.innerHTML = `
77+
<div>
78+
<hello="world">
79+
<button id="target1">button</button>
80+
</hello="world">
81+
</div>
82+
<emoji-👍>
83+
<button id="target2">button</button>
84+
</emoji-👍>
85+
`;
86+
87+
const sel1 = axe.utils.getAncestry(document.querySelector('#target1'));
88+
assert.equal(
89+
sel1,
90+
'html > body > div:nth-child(1) > div:nth-child(1) > hello\\=\\"world\\" > button'
91+
);
92+
assert.isNotNull(document.querySelector(sel1));
93+
94+
const sel2 = axe.utils.getAncestry(document.querySelector('#target2'));
95+
assert.equal(
96+
sel2,
97+
'html > body > div:nth-child(1) > emoji-👍:nth-child(2) > button'
98+
);
99+
assert.isNotNull(document.querySelector(sel2));
100+
});
74101
});

0 commit comments

Comments
 (0)