-
Notifications
You must be signed in to change notification settings - Fork 783
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(rule): Added new html-xml-lang-mismatch rule (#999)
The rule checks that for the html element, there is no mismatch between the primary language in non-empty lang and xml:lang attributes, if both are used. See also - https://auto-wcag.github.io/auto-wcag/rules/SC3-1-1-html-xml-lang-match.html for further details of the rule. Closes issue: - #558 ## Reviewer checks **Required fields, to be filled out by PR reviewer(s)** - [x] Follows the commit message policy, appropriate for next version - [x] Has documentation updated, a DU ticket, or requires no documentation change - [x] Includes new tests, or was unnecessary - [x] Code is reviewed for security by: @WilcoFiers
- Loading branch information
1 parent
9ff5d54
commit 7452a51
Showing
22 changed files
with
510 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
const { getBaseLang } = axe.commons.utils; | ||
const primaryLangValue = getBaseLang(node.getAttribute('lang')); | ||
const primaryXmlLangValue = getBaseLang(node.getAttribute('xml:lang')); | ||
|
||
return primaryLangValue === primaryXmlLangValue; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"id": "xml-lang-mismatch", | ||
"evaluate": "xml-lang-mismatch.js", | ||
"metadata": { | ||
"impact": "moderate", | ||
"messages": { | ||
"pass": "Lang and xml:lang attributes have the same base language", | ||
"fail": "Lang and xml:lang attributes do not have the same base language" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* global axe */ | ||
/** | ||
* Convenience function to extract primary language subtag from a given value | ||
* @method getBaseLang | ||
* @memberof axe.commons.utils | ||
* @instance | ||
* @param {String} value value specified as lang or xml:lang attribute | ||
* @return {String} | ||
*/ | ||
axe.utils.getBaseLang = function getBaseLang(lang) { | ||
if (!lang) { | ||
return ''; | ||
} | ||
return lang | ||
.trim() | ||
.split('-')[0] | ||
.toLowerCase(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
"id": "html-xml-lang-mismatch", | ||
"selector": "html[lang][xml\\:lang]", | ||
"matches": "xml-lang-mismatch-matches.js", | ||
"tags": [ | ||
"cat.language", | ||
"wcag2a", | ||
"wcag311" | ||
], | ||
"metadata": { | ||
"description": "Ensure that HTML elements with both valid lang and xml:lang attributes agree on the base language of the page", | ||
"help": "HTML elements with lang and xml:lang must have the same base language" | ||
}, | ||
"all": [ | ||
"xml-lang-mismatch" | ||
], | ||
"any": [], | ||
"none": [] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// using -> "selector": "html[lang][xml\\:lang]" to narrow down html with lang and xml:lang attributes | ||
|
||
// get primary base language for each of the attributes | ||
const { getBaseLang } = axe.commons.utils; | ||
const primaryLangValue = getBaseLang(node.getAttribute('lang')); | ||
const primaryXmlLangValue = getBaseLang(node.getAttribute('xml:lang')); | ||
|
||
// ensure that the value specified is valid lang for both `lang` and `xml:lang` | ||
return ( | ||
axe.utils.validLangs().includes(primaryLangValue) && | ||
axe.utils.validLangs().includes(primaryXmlLangValue) | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
describe('xml-lang-mismatch', function() { | ||
'use strict'; | ||
|
||
var node; | ||
var fixture = document.getElementById('fixture'); | ||
var checkContext = axe.testUtils.MockCheckContext(); | ||
|
||
beforeEach(function() { | ||
// using a div element (instead of html), as the check is agnostic of element type | ||
node = document.createElement('div'); | ||
}); | ||
|
||
afterEach(function() { | ||
fixture.innerHTML = ''; | ||
checkContext.reset(); | ||
}); | ||
|
||
// the rule matches filters out node of type HTML, and tests cover this scenario to ensure other elements are not allowed for this check | ||
// hence below tests are only for HTML element, although the logic in the check looks for matches in value os lang and xml:lang | ||
// rather than node type match - hence the check can be re-used. | ||
|
||
it('should return false if a only lang is supplied', function() { | ||
node.setAttribute('lang', 'en'); | ||
fixture.appendChild(node); | ||
assert.isFalse( | ||
checks['xml-lang-mismatch'].evaluate.call(checkContext, node) | ||
); | ||
}); | ||
|
||
it('should return false if a only xml:lang is supplied albeit with region', function() { | ||
node.setAttribute('xml:lang', 'fr-FR'); | ||
fixture.appendChild(node); | ||
assert.isFalse( | ||
checks['xml-lang-mismatch'].evaluate.call(checkContext, node) | ||
); | ||
}); | ||
|
||
it('should return false if lang is undefined', function() { | ||
node.setAttribute('lang', undefined); | ||
fixture.appendChild(node); | ||
assert.isFalse( | ||
checks['xml-lang-mismatch'].evaluate.call(checkContext, node) | ||
); | ||
}); | ||
|
||
it('should return true if lang and xml:lang is identical', function() { | ||
node.setAttribute('lang', 'en-GB'); | ||
node.setAttribute('xml:lang', 'en-GB'); | ||
fixture.appendChild(node); | ||
assert.isTrue( | ||
checks['xml-lang-mismatch'].evaluate.call(checkContext, node) | ||
); | ||
}); | ||
|
||
it('should return true if lang and xml:lang have identical primary sub tag', function() { | ||
node.setAttribute('lang', 'en-GB'); | ||
node.setAttribute('xml:lang', 'en-US'); | ||
fixture.appendChild(node); | ||
assert.isTrue( | ||
checks['xml-lang-mismatch'].evaluate.call(checkContext, node) | ||
); | ||
}); | ||
|
||
it('should return false if lang and xml:lang are not identical', function() { | ||
node.setAttribute('lang', 'en'); | ||
node.setAttribute('xml:lang', 'fr-FR'); | ||
fixture.appendChild(node); | ||
var actual = checks['xml-lang-mismatch'].evaluate.call(checkContext, node); | ||
assert.isFalse(actual); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
describe('utils.getBaseLang', function() { | ||
'use strict'; | ||
|
||
it('returns base lang as peanut for argument peanut-BUTTER', function() { | ||
var actual = axe.commons.utils.getBaseLang('peanut-BUTTER'); | ||
assert.equal(actual, 'peanut'); | ||
}); | ||
|
||
it('returns base lang as fr for argument FR-CA', function() { | ||
var actual = axe.commons.utils.getBaseLang('FR-CA'); | ||
assert.strictEqual(actual, 'fr'); | ||
}); | ||
|
||
it('returns base lang which is the prefix string before the first - (hyphen)', function() { | ||
var actual = axe.commons.utils.getBaseLang('en-GB'); | ||
assert.equal(actual, 'en'); | ||
}); | ||
|
||
it('returns primary language subtag as base lang for multi hyphenated argument', function() { | ||
var actual = axe.commons.utils.getBaseLang('SOME-random-lang'); | ||
assert.strictEqual(actual, 'some'); | ||
}); | ||
|
||
it('returns an empty string when argument is null or undefined', function() { | ||
var actualNull = axe.commons.utils.getBaseLang(null); | ||
var actualUndefined = axe.commons.utils.getBaseLang(undefined); | ||
var actualEmpty = axe.commons.utils.getBaseLang(); | ||
assert.strictEqual(actualNull, ''); | ||
assert.strictEqual(actualUndefined, ''); | ||
assert.strictEqual(actualEmpty, ''); | ||
}); | ||
}); |
30 changes: 30 additions & 0 deletions
30
test/integration/full/xml-lang-mismatch/xml-lang-mismatch.fail.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
describe('html-xml-lang-mismatch test', function() { | ||
'use strict'; | ||
|
||
var results; | ||
before(function(done) { | ||
axe.run( | ||
{ | ||
runOnly: { | ||
type: 'rule', | ||
values: ['html-xml-lang-mismatch'] | ||
} | ||
}, | ||
function(err, r) { | ||
assert.isNull(err); | ||
results = r; | ||
done(); | ||
} | ||
); | ||
}); | ||
|
||
describe('violations', function() { | ||
it('should find one', function() { | ||
assert.lengthOf(results.violations[0].nodes, 1); | ||
}); | ||
|
||
it('should find html', function() { | ||
assert.deepEqual(results.violations[0].nodes[0].target, ['html']); | ||
}); | ||
}); | ||
}); |
22 changes: 22 additions & 0 deletions
22
test/integration/full/xml-lang-mismatch/xml-lang-mismatch.fail1.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<!DOCTYPE html> | ||
<html lang="fr" xml:lang="en"> | ||
<head> | ||
<link rel="stylesheet" type="text/css" href="/node_modules/mocha/mocha.css" /> | ||
<script src="/node_modules/mocha/mocha.js"></script> | ||
<script src="/node_modules/chai/chai.js"></script> | ||
<script src="/axe.js"></script> | ||
<script> | ||
mocha.setup({ | ||
timeout: 10000, | ||
ui: 'bdd' | ||
}); | ||
var assert = chai.assert; | ||
</script> | ||
</head> | ||
<body> | ||
<div id="mocha" role="complementary"></div> | ||
<script src="/test/testutils.js"></script> | ||
<script src="xml-lang-mismatch.fail.js"></script> | ||
<script src="/test/integration/adapter.js"></script> | ||
</body> | ||
</html> |
22 changes: 22 additions & 0 deletions
22
test/integration/full/xml-lang-mismatch/xml-lang-mismatch.fail2.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<!DOCTYPE html> | ||
<html lang="fr-CA" xml:lang="en-CA"> | ||
<head> | ||
<link rel="stylesheet" type="text/css" href="/node_modules/mocha/mocha.css" /> | ||
<script src="/node_modules/mocha/mocha.js"></script> | ||
<script src="/node_modules/chai/chai.js"></script> | ||
<script src="/axe.js"></script> | ||
<script> | ||
mocha.setup({ | ||
timeout: 10000, | ||
ui: 'bdd' | ||
}); | ||
var assert = chai.assert; | ||
</script> | ||
</head> | ||
<body> | ||
<div id="mocha" role="complementary"></div> | ||
<script src="/test/testutils.js"></script> | ||
<script src="xml-lang-mismatch.fail.js"></script> | ||
<script src="/test/integration/adapter.js"></script> | ||
</body> | ||
</html> |
26 changes: 26 additions & 0 deletions
26
test/integration/full/xml-lang-mismatch/xml-lang-mismatch.inapplicable.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
describe('html-xml-lang-mismatch test', function() { | ||
'use strict'; | ||
|
||
var results; | ||
before(function(done) { | ||
axe.run( | ||
{ | ||
runOnly: { | ||
type: 'rule', | ||
values: ['html-xml-lang-mismatch'] | ||
} | ||
}, | ||
function(err, r) { | ||
assert.isNull(err); | ||
results = r; | ||
done(); | ||
} | ||
); | ||
}); | ||
|
||
describe('inapplicable', function() { | ||
it('should find one', function() { | ||
assert.lengthOf(results.inapplicable, 1); | ||
}); | ||
}); | ||
}); |
22 changes: 22 additions & 0 deletions
22
test/integration/full/xml-lang-mismatch/xml-lang-mismatch.inapplicable1.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<!DOCTYPE html> | ||
<html lang="fr" xml:lang=""> | ||
<head> | ||
<link rel="stylesheet" type="text/css" href="/node_modules/mocha/mocha.css" /> | ||
<script src="/node_modules/mocha/mocha.js"></script> | ||
<script src="/node_modules/chai/chai.js"></script> | ||
<script src="/axe.js"></script> | ||
<script> | ||
mocha.setup({ | ||
timeout: 10000, | ||
ui: 'bdd' | ||
}); | ||
var assert = chai.assert; | ||
</script> | ||
</head> | ||
<body> | ||
<div id="mocha" role="complementary"></div> | ||
<script src="/test/testutils.js"></script> | ||
<script src="xml-lang-mismatch.inapplicable.js"></script> | ||
<script src="/test/integration/adapter.js"></script> | ||
</body> | ||
</html> |
22 changes: 22 additions & 0 deletions
22
test/integration/full/xml-lang-mismatch/xml-lang-mismatch.inapplicable2.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<!DOCTYPE html> | ||
<html lang="" xml:lang=""> | ||
<head> | ||
<link rel="stylesheet" type="text/css" href="/node_modules/mocha/mocha.css" /> | ||
<script src="/node_modules/mocha/mocha.js"></script> | ||
<script src="/node_modules/chai/chai.js"></script> | ||
<script src="/axe.js"></script> | ||
<script> | ||
mocha.setup({ | ||
timeout: 10000, | ||
ui: 'bdd' | ||
}); | ||
var assert = chai.assert; | ||
</script> | ||
</head> | ||
<body> | ||
<div id="mocha" role="complementary"></div> | ||
<script src="/test/testutils.js"></script> | ||
<script src="xml-lang-mismatch.inapplicable.js"></script> | ||
<script src="/test/integration/adapter.js"></script> | ||
</body> | ||
</html> |
30 changes: 30 additions & 0 deletions
30
test/integration/full/xml-lang-mismatch/xml-lang-mismatch.pass.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
describe('html-xml-lang-mismatch test', function() { | ||
'use strict'; | ||
|
||
var results; | ||
before(function(done) { | ||
axe.run( | ||
{ | ||
runOnly: { | ||
type: 'rule', | ||
values: ['html-xml-lang-mismatch'] | ||
} | ||
}, | ||
function(err, r) { | ||
assert.isNull(err); | ||
results = r; | ||
done(); | ||
} | ||
); | ||
}); | ||
|
||
describe('passes', function() { | ||
it('should find one', function() { | ||
assert.lengthOf(results.passes[0].nodes, 1); | ||
}); | ||
|
||
it('should find html', function() { | ||
assert.deepEqual(results.passes[0].nodes[0].target, ['html']); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.
7452a51
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://dequeuniversity.com/rules/axe/3.1/html-xml-lang-mismatch page created.
TBD - update rule help language to explain the relationship between the lang and xml:lang attributes, and why the value of these attributes must be identical.