-
Notifications
You must be signed in to change notification settings - Fork 779
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
fix(object-alt): ignore unloaded objects #3680
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import noExplicitNameRequired from './no-explicit-name-required-matches'; | ||
|
||
export default (node, vNode) => | ||
[noExplicitNameRequired, objectHasLoaded].every(fn => fn(node, vNode)); | ||
|
||
/** | ||
* Test if an object loaded content; assume yes if we can't prove otherwise | ||
* | ||
* @param {Element} node | ||
* @param {VirtualNode} vNode | ||
* @returns {boolean} | ||
*/ | ||
function objectHasLoaded(node) { | ||
if (!node?.ownerDocument?.createRange) { | ||
return true; // Assume it did | ||
} | ||
// There's no ready | ||
const range = node.ownerDocument.createRange(); | ||
range.setStart(node, 0); | ||
range.setEnd(node, node.childNodes.length); | ||
return range.getClientRects().length === 0; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,75 @@ | ||
<object id="pass1" title="This object has text"></object> | ||
<object id="pass2" aria-label="this object has text"></object> | ||
<span id="label1">this object has text</span> | ||
<object id="pass3" aria-labelledby="label1"></object> | ||
<object id="pass4" role="presentation"></object> | ||
<object id="pass5" role="none"></object> | ||
<object | ||
id="pass1" | ||
title="This object has text" | ||
data="data:text/html,Object%20content" | ||
></object> | ||
<object | ||
id="pass2" | ||
aria-label="this object has text" | ||
data="data:text/html,Object%20content" | ||
></object> | ||
<span id="label1" data="data:text/html,Object%20content" | ||
>this object has text</span | ||
> | ||
<object | ||
id="pass3" | ||
aria-labelledby="label1" | ||
data="data:text/html,Object%20content" | ||
></object> | ||
<object | ||
id="pass4" | ||
role="presentation" | ||
data="data:text/html,Object%20content" | ||
></object> | ||
<object id="pass5" role="none" data="data:text/html,Object%20content"></object> | ||
|
||
<object id="violation1"></object> | ||
<object id="violation2"><div></div></object> | ||
<object id="violation3">This object has text.</object> | ||
<object id="violation4" role="none" tabindex="0"></object> | ||
<object id="violation5" role="presentation" tabindex="0"></object> | ||
<object id="violation6" role="none" aria-live="assertive"></object> | ||
<object id="violation7" role="presentation" aria-live="assertive"></object> | ||
<object id="violation1" data="data:text/html,Object%20content"></object> | ||
<object id="violation2" data="data:text/html,Object%20content"> | ||
<div></div> | ||
</object> | ||
<object id="violation3" data="data:text/html,Object%20content"> | ||
This object has text. | ||
</object> | ||
<object | ||
id="violation4" | ||
role="none" | ||
tabindex="0" | ||
data="data:text/html,Object%20content" | ||
></object> | ||
<object | ||
id="violation5" | ||
role="presentation" | ||
tabindex="0" | ||
data="data:text/html,Object%20content" | ||
></object> | ||
<object | ||
id="violation6" | ||
role="none" | ||
aria-live="assertive" | ||
data="data:text/html,Object%20content" | ||
></object> | ||
<object | ||
id="violation7" | ||
role="presentation" | ||
aria-live="assertive" | ||
data="data:text/html,Object%20content" | ||
></object> | ||
<object | ||
id="violation8" | ||
role="img" | ||
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. This fails inapplicable example 1 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. ACT Covers this in a different rule. We don't. |
||
data="data:text/html,Object%20content" | ||
></object> | ||
<object | ||
id="violation9" | ||
role="separator" | ||
tabindex="0" | ||
data="data:text/html,Object%20content" | ||
></object> | ||
|
||
<object id="violation8" role="img"></object> | ||
<object id="violation9" role="separator" tabindex="0"></object> | ||
<object id="inapplicable1" role="separator"></object> | ||
<object id="inapplicable1"><!-- no data attribute --></object> | ||
<object id="inapplicable2" data="">Fallback content</object> | ||
<object | ||
id="inapplicable3" | ||
role="separator" | ||
data="data:text/html,Object%20content" | ||
></object> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
describe('object-is-loaded-matches', () => { | ||
let rule, fixture; | ||
const data = `data:text/html,Object%20content`; | ||
|
||
// Give enough time for the browser to load / decide it cannot load objects | ||
async function delayedQueryFixture(html, delay = 50) { | ||
fixture.innerHTML = html; | ||
await new Promise(r => setTimeout(r, delay)); | ||
const tree = axe.setup(); | ||
return axe.utils.querySelectorAll(tree, '#target')[0]; | ||
} | ||
|
||
before(() => { | ||
fixture = document.querySelector('#fixture'); | ||
rule = axe.utils.getRule('object-alt'); | ||
}); | ||
|
||
afterEach(() => { | ||
fixture.innerHTML = ''; | ||
}); | ||
|
||
it(`returns true objects with hidden fallback content`, async () => { | ||
const vNode = await delayedQueryFixture( | ||
`<object data="${data}" height="30" id="target"> | ||
Fallback content | ||
</object>` | ||
); | ||
assert.isTrue(rule.matches(vNode.actualNode, vNode)); | ||
}); | ||
|
||
it(`returns false if the object shows any content`, async () => { | ||
const vNode = await delayedQueryFixture( | ||
`<object data="invalid" height="30" id="target"> | ||
Fallback content | ||
</object>` | ||
); | ||
assert.isFalse(rule.matches(vNode.actualNode, vNode)); | ||
}); | ||
|
||
it(`returns true if the object shows no content`, async () => { | ||
const vNode = await delayedQueryFixture( | ||
`<object data="invalid" height="30" id="target"></object>` | ||
); | ||
// Ideally, this should be false, don't know it can be done | ||
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. Very helpful comment, thanks. |
||
assert.isTrue(rule.matches(vNode.actualNode, vNode)); | ||
}); | ||
|
||
it(`returns false if the object has a role that doesn't require a name`, async () => { | ||
const vNode = await delayedQueryFixture( | ||
`<object data="${data}" height="30" id="target" role="grid"> | ||
Fallback content | ||
</object>` | ||
); | ||
assert.isFalse(rule.matches(vNode.actualNode, vNode)); | ||
}); | ||
}); |
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 fails inapplicable example 6
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.
ACT doesn't cover this, but this is still an issue.