-
Notifications
You must be signed in to change notification settings - Fork 780
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(landmark-one-main): add rule ensuring one main landmark in document #498
feat(landmark-one-main): add rule ensuring one main landmark in document #498
Conversation
This pull request combined the at least one main landmark rule and more than one main landmark rule and is ready for your review. |
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.
This is really starting to shape up! Excellent! I've added a few more comments. Also, make sure you run the tests next time before creating the PR. Unfortunately those don't run with CircleCI for PRs from forks. You would've caught a few issues.
@@ -0,0 +1,3 @@ | |||
var main = document.querySelector('main,[role=main]'); | |||
this.data(!!main); |
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.
What is the purpose of this line?
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.
This tells the after function whether or not the main exists; if there's a main in any one document, all of the documents will pass. This will probably make more sense when I add the file for the after function.
@@ -0,0 +1,3 @@ | |||
var main = document.querySelector('main,[role=main]'); |
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.
Please do the following instead. That way you'll query shadow DOM trees too.
const main = axe.utils.querySelectorAll(virtualNode, 'main,[role=main')
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.
What purpose does adding 'virtualnode' serve? I am getting errors concerning an undefined object.
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.
The change to use "const main = axe.utils.querySelectorAll(virtualNode, 'main,[role=main')" seems to break all our test cases and we get the following error:
"TypeError: Cannot read property 'length' of undefined"
Is there something we need to change in the test cases?
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.
@jongund When you call the check in a test, and that test uses virtualNode, you need to pass a virtualNode object in. We've got a test utility to help set that up on axe.testUtils.checkSetup
. You can see an example here:
https://github.com/dequelabs/axe-core/blob/develop/test/checks/aria/required-children.js#L22
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.
The test utilities are also documented here: https://github.com/dequelabs/axe-core/blob/master/doc/developer-guide.md#test-utilities
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.
It doesn't appear to be possible to test the rule on an 'html' node using checkSetup. Do you have any suggestions on how to go about doing this?
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.
Forgive me for trying to catch up on this PR...but why do you need to test the HTML node for a main
rule? Tests are scoped to the #fixture
element, which is already present in the test environment, to avoid auditing the test harness UI itself.
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.
The selector of the rule itself is 'html' so that the rule runs even if there are no main landmarks. My thought was that it wouldn't really be a proper test if it didn't begin from the specified node.
@@ -0,0 +1,2 @@ | |||
var mains = document.querySelectorAll('main,[role=main]'); |
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.
As above, use axe.utils.querySelectorAll
instead, so you can query shadow DOM trees.
lib/rules/landmark-one-main.json
Outdated
}, | ||
"all": [], | ||
"any": [ | ||
"has-at-least-one-main", |
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.
These should be on all
. Putting them on any
just requires one of them to pass, which it always will.
'use strict'; | ||
|
||
var fixture = document.getElementById('fixture'); | ||
var checkContext = { |
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 recommend using var checkContext = new axe.testUtils.MockCheckContext();
var results = [{data: false, result: false}, {data: false, result: false}]; | ||
assert.isFalse(checks['has-at-least-one-main'].after(results)[0].result && checks['has-at-least-one-main'].after(results)[1].result); | ||
}); | ||
|
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.
Please add a shadow DOM test too. Have a look at the develop docs https://github.com/dequelabs/axe-core/blob/develop/doc/developer-guide.md#test-utilities about testUtils.shadowSupport
var node = document.querySelector('html'); | ||
var mainLandmark = document.createElement('div'); | ||
mainLandmark.setAttribute('role','main'); | ||
node.appendChild(mainLandmark); |
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.
It's better to append these to the fixture instead. Saves you a few lines of code + it's guaranteed to be cleaned up with afterEach.
@@ -0,0 +1,10 @@ | |||
<!doctype html> |
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.
This dir, and some of the files in it, should be renamed from landmark-at-least-one-main
to landmark-one-main
so that it matches the rule name.
@@ -0,0 +1,24 @@ | |||
<!doctype html> |
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.
We need integration tests for when there are no main landmarks too.
Any additional questions on this pull request? |
@ssanaul @jongund Several of my comments haven't been addressed yet, including the one that's causing the tests not to run. There isn't much I can review if the tests aren't passing, let alone not running at all. Please resolve these issues. |
@ssanaul @WilcoFiers Wilco we have a question about how the "target" attribute is used in a unit test, since we are looking at page level rules. Also we just wanted to verify that all of the files we need to run test are included. |
Upon preparing remarks for whatwg/html#100, it occurred to me that we might need to account for inert subtrees. What about this scenario: A view of a web application opens in a new modal layer, with the background rendered If that scenario was acceptable, we'd want to check to make sure a Just wanted to throw that out there for your input before I respond more fully to WHATWG. A real-life example of this scenario can be found in the Blackboard Learn Application: https://www.youtube.com/watch?v=6tmBd9USe6g&t=1s |
@ssanaul @jongund What's the status of this PR? Tests still aren't passing so I'm assuming you're still working on it? |
Even though the unit tests are passing, the integration tests for the rule are only returning results for has-at-least-one-main, which itself is only checking the main DOM, not any iframes. |
|
||
describe('violations', function () { | ||
it('should find 1', function () { | ||
assert.lengthOf(results.violations, 1); |
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.
Check the number of nodes. I think that should be 2. You're not testing the second node here either.
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.
@WilcoFiers @jongund
It appears as if results.violations only has one node even though it should be observing two violations. We couldn't find any implementation of a test for more than one violation.
However, the testing results.passes in landmark-one-main-pass.js does return 4 nodes.
Any thoughts on why violations only seems to contain one node, for our test as well as the other tests in the aXe library?
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.
@ssanaul Looks like another timing issue. You're calling axe.run before the frames are loaded. Have a look at how that was solved in landmark-main-is-top-level in the before.
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.
@WilcoFiers @jongund
Both nodes are tested now.
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.
Excellent. I think that will do it!! Thanks @ssanaul!
* @param String Target for the check, CSS selector (default: '#target') | ||
* @return Array | ||
*/ | ||
testUtils.shadowCheckSetup = function (content, shadowContent, options, target) { |
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.
Why is this here?
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 created it for them in a separate branch, there was a lot of boilerplate for shadow DOM tests that I wanted to smooth out.
@@ -0,0 +1,8 @@ | |||
var hasMain = false; | |||
for (var i = 0; i < results.length && !hasMain; i++) { | |||
hasMain = results[i].data; |
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.
This doesn't look like it would work. Basically, you're taking the last item on results
and assigning that as the result for everything. That doesn't seem right. What was this supposed to do? Please comment the code, as these double loops are confusing.
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.
var hasMain = false;
//iterate through results from each document
//stops if any document contains a main landmark
for (var i = 0; i < results.length && !hasMain; i++) {
hasMain = results[i].data;
}
//if any document contains a main landmark, set all documents to pass the check
//otherwise, fail all documents
//since this is a page level rule, all documents either pass or fail the requirement
for (var i = 0; i < results.length; i++) {
results[i].result = hasMain;
}
return results;
Do these comments clear up the behavior of the function?
Since this is a page level rule all documents either fail or pass this rule.
We only need one main in any of the documents (e.g. main document or an iframe) to pass this rule,
If some documents fail this rule (e.g. iframe), people will think they need to have a main landmark in every iframe.
Does this make sense?
Are we getting the logic right here, do we need to update the individual document results?
|
||
//iterate through results from each document | ||
//stops if any document contains a main landmark | ||
for (var i = 0; i < results.length && !hasMain; i++) { |
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.
Tip for future work. For-loops generally make code harder to read. JS has a bunch of very excellent array methods which let you write more expressive code. If you had written your code like this, you wouldn't have needed the comment.
const hasMain = results.some(result => result.data === true);
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.
Great, I'll keep that in mind. Thanks for the tip!
…ent (#498) * feat(landmark-one-main): add rule ensuring one main landmark in document * fix: rename integration tests * fix: move checks from 'any' to 'all' * fix: add missing 'after' check * fix: update to pass virtualnode in to check * fix: change incorrect rule name in integration tests * fix: remove line for debugging * fix: correct faulty check tests * test: add shadowCheckSetup util * test: fix main tests for shadowdom * fix: resolve timeout issues * fix: add event listener * fix: change where to check for passes * style: comment code for comprehension * test: add test for second violation node
This is the rule that incorporates the checks from the previous PR's as requested.