Skip to content

Commit

Permalink
feat(landmark-no-more-than-one-contentinfo): add rule ensuring no mor…
Browse files Browse the repository at this point in the history
…e than one contentinfo
  • Loading branch information
sulsanaul authored and WilcoFiers committed Mar 3, 2018
1 parent 1e709e0 commit 82217ef
Show file tree
Hide file tree
Showing 14 changed files with 352 additions and 4 deletions.
1 change: 1 addition & 0 deletions doc/rule-descriptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
| label | Ensures every form element has a label | cat.forms, wcag2a, wcag332, wcag131, section508, section508.22.n | true |
| landmark-main-is-top-level | The main landmark should not be contained in another landmark | best-practice | true |
| landmark-no-more-than-one-banner | A banner landmark identifies site-oriented content at the beginning of each page within a website | best-practice | true |
| landmark-no-more-than-one-contentinfo | A contentinfo landmark is a way to identify common information at the bottom of each page within a website | best-practice | true |
| landmark-one-main | Ensures a navigation point to the primary content of the page. If the page contains iframes, each iframe should contain either no main landmarks or just one. | best-practice | true |
| layout-table | Ensures presentational <table> elements do not use <th>, <caption> elements or the summary attribute | cat.semantics, wcag2a, wcag131 | true |
| link-in-text-block | Links can be distinguished without relying on color | cat.color, experimental, wcag2a, wcag141 | true |
Expand Down
2 changes: 1 addition & 1 deletion lib/checks/keyboard/has-no-more-than-one-banner.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const banners = axe.utils.querySelectorAll(virtualNode, 'header, [role=banner]');
const sectioning = ['main', 'section', 'aside', 'nav', 'article'];
const sectioning = ['article','aside','main','nav','section'];
var count = 0;

function isBanner(node){
Expand Down
29 changes: 29 additions & 0 deletions lib/checks/keyboard/has-no-more-than-one-contentinfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const contentinfos = axe.utils.querySelectorAll(virtualNode, 'footer, [role=contentinfo]');
const sectioning = ['article','aside','main','nav','section'];
var count = 0;

function isContentinfo(node){
var parent = axe.commons.dom.getComposedParent(node);
while (parent){
if (sectioning.includes(parent.tagName.toLowerCase())){
return false;
}
parent = axe.commons.dom.getComposedParent(parent);
}
return true;
}

for (var i=0; i<contentinfos.length; i++){
var node = contentinfos[i].actualNode;
var role = node.getAttribute('role');
if (!!role){
role = role.toLowerCase();
}
if (role==='contentinfo' || isContentinfo(node)){
count++;
}
if (count>1){
return false;
}
}
return true;
11 changes: 11 additions & 0 deletions lib/checks/keyboard/has-no-more-than-one-contentinfo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"id": "has-no-more-than-one-contentinfo",
"evaluate": "has-no-more-than-one-contentinfo.js",
"metadata": {
"impact": "moderate",
"messages": {
"pass": "Document has no more than one contentinfo landmark",
"fail": "Document has more than one contentinfo landmark"
}
}
}
16 changes: 16 additions & 0 deletions lib/rules/landmark-no-more-than-one-contentinfo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"id": "landmark-no-more-than-one-contentinfo",
"selector": "html",
"tags": [
"best-practice"
],
"metadata": {
"description": "A contentinfo landmark is a way to identify common information at the bottom of each page within a website",
"help": "Page must not contain more than one contentinfo landmark"
},
"all": [],
"any": [
"has-no-more-than-one-contentinfo"
],
"none": []
}
50 changes: 50 additions & 0 deletions test/checks/keyboard/has-no-more-than-one-contentinfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
describe('has-no-more-than-one-contentinfo', function () {
'use strict';

var fixture = document.getElementById('fixture');
var checkContext = new axe.testUtils.MockCheckContext();
var checkSetup = axe.testUtils.checkSetup;
var shadowCheckSetup = axe.testUtils.shadowCheckSetup;
var shadowSupported = axe.testUtils.shadowSupport.v1;

afterEach(function () {
fixture.innerHTML = '';
checkContext.reset();
});

it('should return false if there is more than one element with role contentinfo', function () {
var params = checkSetup('<div id="target"><div role="contentinfo"></div><div role="contentinfo"></div></div>');
assert.isFalse(checks['has-no-more-than-one-contentinfo'].evaluate.apply(checkContext, params));

});

it('should return false if there is more than one footer serving as a contentinfo', function () {
var params = checkSetup('<div id="target"><footer></footer><footer></footer></div>');
assert.isFalse(checks['has-no-more-than-one-contentinfo'].evaluate.apply(checkContext, params));
});

it('should return true if there are multiple footers contained in sectioning elements', function(){
var params = checkSetup('<div role="main" id="target"><footers></footers><footers></footers></div>');
assert.isTrue(checks['has-no-more-than-one-contentinfo'].evaluate.apply(checkContext, params));
});

it('should return true if there is only one element with role contentinfo', function(){
var params = checkSetup('<div role="contentinfo" id="target"></div>');
assert.isTrue(checks['has-no-more-than-one-contentinfo'].evaluate.apply(checkContext, params));
});

it('should return true if there is only one footer serving as a contentinfo', function(){
var params = checkSetup('<footer id="target"></footer>');
assert.isTrue(checks['has-no-more-than-one-contentinfo'].evaluate.apply(checkContext, params));
});

(shadowSupported ? it : xit)
('should return false if there is a second contentinfo inside the shadow dom', function () {
var params = shadowCheckSetup(
'<div role="contentinfo" id="target"></div>',
'<div role="contentinfo"></div>'
);
assert.isFalse(checks['has-no-more-than-one-contentinfo'].evaluate.apply(checkContext, params));
});

});
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,33 @@
<script src="/axe.js"></script>
</head>
<body>
<main>
<header>
Top level header
</header>
<article>
<header>
Header in article
</header>
</article>
<aside>
<header>
Header 1 contained in main landmark
Header in aside
</header>
</aside>
<main>
<header>
Header 2 contained in main landmark
Header in main landmark
</header>
</main>
<nav>
<header>
Header in nav
</header>
</nav>
<section>
<header>
Header in section
</header>
</section>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en" id="fail2">
<head>
<meta charset="utf8">
<script src="/axe.js"></script>
</head>
<body>
<footer>
Footer 1
</footer>
<footer>
Footer 2
</footer>
<iframe id="frame2" src="level2.html"></iframe>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!doctype html>
<html lang="en" id="pass2">
<head>
<meta charset="utf8">
<script src="/axe.js"></script>
</head>
<body>
<footer>
Top level footer
</footer>
<article>
<footer>
Footer in article
</footer>
</article>
<aside>
<footer>
Footer in aside
</footer>
</aside>
<main>
<footer>
Footer in main landmark
</footer>
</main>
<nav>
<footer>
Footer in nav
</footer>
</nav>
<section>
<footer>
Footer in section
</footer>
</section>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!doctype html>
<html lang="en" id="fail3">
<head>
<meta charset="utf8">
<script src="/axe.js"></script>
</head>
<body>
<main>
<div role="contentinfo">
Div 1 with role contentinfo in main landmark
</div>
<div role="contentinfo">
Div 2 with role contentinfo in main landmark
</div>
</main>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<!doctype html>
<html lang="en" id="fail1">
<head>
<meta charset="utf8">
<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 role="contentinfo">
Div 1 with role of "contentinfo"
</div>
<div role="contentinfo">
Div 2 with role of "contentinfo"
</div>
<iframe id="frame1" src="frames/level1-fail.html"></iframe>
<div id="mocha"></div>
<script src="landmark-no-more-than-one-contentinfo-fail.js"></script>
<script src="/test/integration/adapter.js"></script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
describe('landmark-no-more-than-one-contentinfo test failure', function () {
'use strict';
var results;
before(function (done) {
function start() {
axe.run({ runOnly: { type: 'rule', values: ['landmark-no-more-than-one-contentinfo'] } }, function (err, r) {
assert.isNull(err);
results = r;
done();
});
}
if (document.readyState !== 'complete') {
window.addEventListener('load', start);
} else {
start();
}
});

describe('violations', function () {
it('should find 3', function () {
assert.lengthOf(results.violations[0].nodes, 3);
});

it('should find #fail1', function () {
assert.deepEqual(results.violations[0].nodes[0].target, ['#fail1']);
});

it('should find #frame1, #fail2', function () {
assert.deepEqual(results.violations[0].nodes[1].target, ['#frame1', '#fail2']);
});

it('should find #frame1, #frame2, #fail3', function () {
assert.deepEqual(results.violations[0].nodes[2].target, ['#frame1', '#frame2', '#fail3']);
});

});

describe('passes', function () {
it('should find 0', function () {
assert.lengthOf(results.passes, 0);
});
});

it('should find 0 inapplicable', function () {
assert.lengthOf(results.inapplicable, 0);
});

it('should find 0 incomplete', function () {
assert.lengthOf(results.incomplete, 0);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!doctype html>
<html lang="en" id="pass1">
<head>
<meta charset="utf8">
<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>
<p>No contentinfo landmarks</p>
<iframe id="frame1" src="frames/level1.html"></iframe>
<div id="mocha"></div>
<script src="landmark-no-more-than-one-contentinfo-pass.js"></script>
<script src="/test/integration/adapter.js"></script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
describe('landmark-no-more-than-one-contentinfo test pass', function () {
'use strict';
var results;
before(function (done) {
function start() {
axe.run({ runOnly: { type: 'rule', values: ['landmark-no-more-than-one-contentinfo'] } }, function (err, r) {
assert.isNull(err);
results = r;
done();
});
}
if (document.readyState !== 'complete') {
window.addEventListener('load', start);
} else {
start();
}
});

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 #pass1', function () {
assert.deepEqual(results.passes[0].nodes[0].target, ['#pass1']);
});

it('should find #frame1, #pass2', function () {
assert.deepEqual(results.passes[0].nodes[1].target, ['#frame1', '#pass2']);
});
});

it('should find 0 inapplicable', function () {
assert.lengthOf(results.inapplicable, 0);
});

it('should find 0 incomplete', function () {
assert.lengthOf(results.incomplete, 0);
});

});

0 comments on commit 82217ef

Please sign in to comment.