-
Notifications
You must be signed in to change notification settings - Fork 776
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: new rule landmark-complementary-is-top-level #1239
Changes from 5 commits
65a8485
eb71ca6
2bf6cfd
06bad40
08a6fba
66e434a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"id": "landmark-complementary-is-top-level", | ||
"selector": "aside:not([role]), [role=complementary]", | ||
"tags": [ | ||
"cat.semantics", | ||
"best-practice" | ||
], | ||
"metadata": { | ||
"description": "Ensures the complementary landmark or aside is at top level", | ||
"help": "Aside must not be contained in another landmark" | ||
}, | ||
"all": [], | ||
"any": [ | ||
"landmark-is-top-level" | ||
], | ||
"none": [] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,79 +1,72 @@ | ||
describe('landmark-is-top-level', function() { | ||
'use strict'; | ||
|
||
var fixture = document.getElementById('fixture'); | ||
|
||
var shadowSupported = axe.testUtils.shadowSupport.v1; | ||
var checkSetup = axe.testUtils.checkSetup; | ||
var shadowCheckSetup = axe.testUtils.shadowCheckSetup; | ||
var check = checks['landmark-is-top-level']; | ||
var checkContext = new axe.testUtils.MockCheckContext(); | ||
|
||
afterEach(function() { | ||
fixture.innerHTML = ''; | ||
checkContext.reset(); | ||
}); | ||
|
||
it('should return false if the landmark is in another landmark', function() { | ||
var mainLandmark = document.createElement('main'); | ||
var bannerDiv = document.createElement('div'); | ||
bannerDiv.setAttribute('role', 'banner'); | ||
bannerDiv.appendChild(mainLandmark); | ||
fixture.appendChild(bannerDiv); | ||
assert.isFalse(check.evaluate.call(checkContext, mainLandmark)); | ||
it('should return false if the main landmark is in another landmark', function() { | ||
var params = checkSetup( | ||
'<div role="banner"><main id="target"></main></div>' | ||
); | ||
assert.isFalse(check.evaluate.apply(checkContext, params)); | ||
assert.deepEqual(checkContext._data, { role: 'main' }); | ||
}); | ||
|
||
it('should return false if the complementary landmark is in another landmark', function() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, just had a thought, what happens if the first Question being, do we need to consider There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The check already has Shadow DOM coverage. |
||
var params = checkSetup( | ||
'<main><div role="complementary" id="target"></div></main>' | ||
); | ||
assert.isFalse(check.evaluate.apply(checkContext, params)); | ||
assert.deepEqual(checkContext._data, { role: 'complementary' }); | ||
}); | ||
|
||
it('should return false if div with role set to main is in another landmark', function() { | ||
var mainDiv = document.createElement('div'); | ||
mainDiv.setAttribute('role', 'main'); | ||
var navDiv = document.createElement('div'); | ||
navDiv.setAttribute('role', 'navigation'); | ||
navDiv.appendChild(mainDiv); | ||
fixture.appendChild(navDiv); | ||
assert.isFalse(check.evaluate.call(checkContext, mainDiv)); | ||
var params = checkSetup( | ||
'<div role="navigation"><div role="main" id="target"></div></div>' | ||
); | ||
assert.isFalse(check.evaluate.apply(checkContext, params)); | ||
assert.deepEqual(checkContext._data, { role: 'main' }); | ||
}); | ||
|
||
it('should return true if the landmark is not in another landmark', function() { | ||
var footerLandmark = document.createElement('footer'); | ||
var bannerDiv = document.createElement('div'); | ||
bannerDiv.setAttribute('role', 'banner'); | ||
fixture.appendChild(bannerDiv); | ||
fixture.appendChild(footerLandmark); | ||
assert.isTrue(check.evaluate.call(checkContext, footerLandmark)); | ||
var params = checkSetup( | ||
'<div><footer id="target"></footer><div role="banner"></div></div>' | ||
); | ||
assert.isTrue(check.evaluate.apply(checkContext, params)); | ||
assert.deepEqual(checkContext._data, { role: 'contentinfo' }); | ||
}); | ||
|
||
it('should return true if div with role set to main is not in another landmark', function() { | ||
var mainDiv = document.createElement('div'); | ||
mainDiv.setAttribute('role', 'main'); | ||
var navDiv = document.createElement('div'); | ||
navDiv.setAttribute('role', 'navigation'); | ||
fixture.appendChild(navDiv); | ||
fixture.appendChild(mainDiv); | ||
assert.isTrue(check.evaluate.call(checkContext, mainDiv)); | ||
var params = checkSetup( | ||
'<div><div role="main" id="target"></div><div role="navigation"></div></div>' | ||
); | ||
assert.isTrue(check.evaluate.apply(checkContext, params)); | ||
assert.deepEqual(checkContext._data, { role: 'main' }); | ||
}); | ||
|
||
it('should return true if the landmark is in form landmark', function() { | ||
var bannerDiv = document.createElement('div'); | ||
bannerDiv.setAttribute('role', 'banner'); | ||
var formDiv = document.createElement('div'); | ||
formDiv.setAttribute('role', 'form'); | ||
fixture.appendChild(formDiv); | ||
fixture.appendChild(bannerDiv); | ||
assert.isTrue(check.evaluate.call(checkContext, bannerDiv)); | ||
it('should return true if the banner landmark is not in form landmark', function() { | ||
var params = checkSetup( | ||
'<div><div role="banner" id="target"></div><div role="form"></div></div>' | ||
); | ||
assert.isTrue(check.evaluate.apply(checkContext, params)); | ||
assert.deepEqual(checkContext._data, { role: 'banner' }); | ||
}); | ||
|
||
(shadowSupported ? it : xit)( | ||
'should test if the landmark in shadow DOM is top level', | ||
function() { | ||
var div = document.createElement('div'); | ||
var shadow = div.attachShadow({ mode: 'open' }); | ||
shadow.innerHTML = '<main>Main content</main>'; | ||
var checkArgs = checkSetup(shadow.querySelector('main')); | ||
assert.isTrue(check.evaluate.apply(checkContext, checkArgs)); | ||
var params = shadowCheckSetup( | ||
'<div></div>', | ||
'<main id="target">Main content</main>' | ||
); | ||
assert.isTrue(check.evaluate.apply(checkContext, params)); | ||
assert.deepEqual(checkContext._data, { role: 'main' }); | ||
} | ||
); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<!doctype html> | ||
<html id="violation2"> | ||
<head> | ||
<meta charset="utf8"> | ||
<script src="/axe.js"></script> | ||
</head> | ||
<body> | ||
<p>This iframe should fail, too</p> | ||
<main> | ||
<div role="complementary"> | ||
<p>This complementary landmark is in a main landmark</p> | ||
</div> | ||
</main> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<!doctype html> | ||
<html id="pass2"> | ||
<head> | ||
<meta charset="utf8"> | ||
<script src="/axe.js"></script> | ||
</head> | ||
<body> | ||
<p>This iframe should pass, too</p> | ||
|
||
<div role="navigation"> | ||
<p>This div has role navigation</p> | ||
</div> | ||
<header> | ||
<p>This banner content is not within another landmark</p> | ||
</header> | ||
<div role="complementary"> | ||
<p>This div has role complementary</p> | ||
</div> | ||
<div role="search"> | ||
<p>This div has role search</p> | ||
</div> | ||
<div role="form"> | ||
<p>This div has role form<p> | ||
</div> | ||
<iframe id="frame2" src="level2.html"></iframe> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<!doctype html> | ||
<html id="pass3"> | ||
<head> | ||
<meta charset="utf8"> | ||
<script src="/axe.js"></script> | ||
</head> | ||
<body> | ||
<p>This iframe should pass<p> | ||
<aside> | ||
<p>This aside is top level and should be ignored</p> | ||
</aside> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<!doctype html> | ||
<html lang="en" id="violation1"> | ||
<head> | ||
<title>landmark-complementary-is-top-level test</title> | ||
<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="navigation"> | ||
<div role="complementary"> | ||
<p>This is going to fail</p> | ||
</div> | ||
</div> | ||
<iframe id="frame1" src="frames/level1-fail.html"></iframe> | ||
<div id="mocha"></div> | ||
<script src="landmark-complementary-is-top-level-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,45 @@ | ||
describe('landmark-complementary-is-top-level test fail', function() { | ||
'use strict'; | ||
var results; | ||
before(function(done) { | ||
window.addEventListener('load', function() { | ||
axe.run( | ||
{ | ||
runOnly: { | ||
type: 'rule', | ||
values: ['landmark-complementary-is-top-level'] | ||
} | ||
}, | ||
function(err, r) { | ||
assert.isNull(err); | ||
results = r; | ||
done(); | ||
} | ||
); | ||
}); | ||
}); | ||
|
||
describe('violations', function() { | ||
it('should find 1', function() { | ||
assert.lengthOf(results.violations, 1); | ||
}); | ||
|
||
it('should find 2 nodes', function() { | ||
assert.lengthOf(results.violations[0].nodes, 2); | ||
}); | ||
}); | ||
|
||
describe('passes', function() { | ||
it('should find none', 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,42 @@ | ||
<!doctype html> | ||
<html lang="en" id="pass1"> | ||
<head> | ||
<title>landmark-complementary-is-top-level test</title> | ||
<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="navigation"> | ||
<p>This div has role navigation</p> | ||
</div> | ||
<div role="banner"> | ||
<p>This banner content is not within another landmark</p> | ||
</div> | ||
<aside> | ||
<p>This aside has an implicit role complementary</p> | ||
</aside> | ||
<div role="complementary"> | ||
<p>This div has role complementary</p> | ||
</div> | ||
<div role="search"> | ||
<p>This div has role search</p> | ||
</div> | ||
<div role="form"> | ||
<p>This div has role form<p> | ||
</div> | ||
<iframe id="frame1" src="frames/level1.html"></iframe> | ||
<div id="mocha"></div> | ||
<script src="landmark-complementary-is-top-level-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,41 @@ | ||
describe('landmark-complementary-is-top-level test pass', function() { | ||
'use strict'; | ||
var results; | ||
before(function(done) { | ||
window.addEventListener('load', function() { | ||
axe.run( | ||
{ | ||
runOnly: { | ||
type: 'rule', | ||
values: ['landmark-complementary-is-top-level'] | ||
} | ||
}, | ||
function(err, r) { | ||
assert.isNull(err); | ||
results = r; | ||
done(); | ||
} | ||
); | ||
}); | ||
}); | ||
|
||
describe('violations', function() { | ||
it('should find 0', function() { | ||
assert.lengthOf(results.violations, 0); | ||
}); | ||
}); | ||
|
||
describe('passes', function() { | ||
it('should find 4', function() { | ||
assert.lengthOf(results.passes[0].nodes, 4); | ||
}); | ||
}); | ||
|
||
it('should find 0 inapplicable', function() { | ||
assert.lengthOf(results.inapplicable, 0); | ||
}); | ||
|
||
it('should find 0 incomplete', function() { | ||
assert.lengthOf(results.incomplete, 0); | ||
}); | ||
}); |
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.
Could this be extracted to
"selector": "[role]"
?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.
You mean in the JSON? The only reason this file has edits is it had a typo in it...
Edit: I don't think we actually need this file for asides.
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.
I ended up not using this file, and the only thing I changed in it was a comment typo. Leaving as-is.
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.
I had to look it up too, but yes aside always maps to role= complementary, unlike footer and header which only map to a role if they are scoped to the body.