Skip to content
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(frame-tested): run without respondable #2942

Merged
merged 6 commits into from
Jun 16, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions lib/checks/media/frame-tested-after.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const joinStr = ' > ';

function frameTestedAfter(results) {
straker marked this conversation as resolved.
Show resolved Hide resolved
const iframes = {};

return results.filter(result => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I'll be damned! I did not know you could do a filter in after.

const frameResult =
result.node.ancestry[result.node.ancestry.length - 1] !== 'html';

if (frameResult) {
iframes[result.node.ancestry.join(joinStr)] = result;
return true;
}

// remove the `html` from the path to get the iframe path
const ancestry = result.node.ancestry
.slice(0, result.node.ancestry.length - 1)
.join(joinStr);
straker marked this conversation as resolved.
Show resolved Hide resolved

// pass for each iframe that has an html result
if (iframes[ancestry]) {
iframes[ancestry].result = true;
}

return false;
});
}

export default frameTestedAfter;
26 changes: 2 additions & 24 deletions lib/checks/media/frame-tested-evaluate.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,6 @@
import { respondable } from '../../core/utils';

function frameTestedEvaluate(node, options) {
const resolve = this.async();
const { isViolation, timeout } = Object.assign(
{ isViolation: false, timeout: 500 },
options
);

// give the frame .5s to respond to 'axe.ping', else log failed response
let timer = setTimeout(() => {
// This double timeout is important for allowing iframes to respond
// DO NOT REMOVE
timer = setTimeout(() => {
timer = null;
resolve(isViolation ? false : undefined);
}, 0);
}, timeout);

respondable(node.contentWindow, 'axe.ping', null, undefined, () => {
if (timer !== null) {
clearTimeout(timer);
resolve(true);
}
});
// assume iframe is not tested
return options.isViolation ? false : undefined;
}

export default frameTestedEvaluate;
1 change: 1 addition & 0 deletions lib/checks/media/frame-tested.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"id": "frame-tested",
"evaluate": "frame-tested-evaluate",
"after": "frame-tested-after",
"options": {
"isViolation": false
},
Expand Down
2 changes: 2 additions & 0 deletions lib/core/base/metadata-function-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ import structuredDlitemsEvaluate from '../../checks/lists/structured-dlitems-eva
// media
import captionEvaluate from '../../checks/media/caption-evaluate';
import frameTestedEvaluate from '../../checks/media/frame-tested-evaluate';
import frameTestedAfter from '../../checks/media/frame-tested-after';
import noAutoplayAudioEvaluate from '../../checks/media/no-autoplay-audio-evaluate';

// rule matches
Expand Down Expand Up @@ -299,6 +300,7 @@ const metadataFunctionMap = {
// media
'caption-evaluate': captionEvaluate,
'frame-tested-evaluate': frameTestedEvaluate,
'frame-tested-after': frameTestedAfter,
'no-autoplay-audio-evaluate': noAutoplayAudioEvaluate,

// rule matches
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/frame-tested.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "frame-tested",
"selector": "frame, iframe",
"selector": "html, frame, iframe",
"tags": ["cat.structure", "review-item", "best-practice"],
"metadata": {
"description": "Ensures <iframe> and <frame> elements contain the axe-core script",
Expand Down
116 changes: 78 additions & 38 deletions test/checks/media/frame-tested.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,92 @@
describe('frame-tested', function() {
'use strict';

var checkContext, iframe;
var checkEvaluate = axe.testUtils.getCheckEvaluate('frame-tested');
var fixture = document.querySelector('#fixture');
var frameTestedAfter = checks['frame-tested'].after;

beforeEach(function() {
iframe = document.createElement('iframe');
fixture.appendChild(iframe);
checkContext = axe.testUtils.MockCheckContext();
// Don't throw on async
checkContext._onAsync = function() {};
});
describe('evaluate', function() {
it('returns undefined', function() {
assert.isUndefined(checkEvaluate());
});

after(function() {
fixture = '';
it('returns false if passed isViolation:true', function() {
assert.isFalse(checkEvaluate(null, { isViolation: true }));
});
});

it('passes if the iframe contains axe-core', function(done) {
iframe.src = '/test/mock/frames/test.html';
iframe.addEventListener('load', function() {
checkContext._onAsync = function(result) {
assert.isTrue(result);
done();
};
describe('after', function() {
it('changes result to true if frame has been tested', function() {
var results = [
{
result: undefined,
node: {
ancestry: ['html']
}
},
{
result: undefined,
node: {
ancestry: ['html > body > iframe#1']
}
},
{
result: undefined,
node: {
ancestry: ['html > body > iframe#2']
}
},
{
result: undefined,
node: {
ancestry: ['html > body > iframe#1', 'html']
}
},
{
result: undefined,
node: {
ancestry: ['html > body > iframe#2', 'html']
}
}
];

checkEvaluate.call(checkContext, iframe);
var afterResults = frameTestedAfter(results);
assert.lengthOf(afterResults, 2);
assert.isTrue(afterResults[0].result);
assert.isTrue(afterResults[1].result);
straker marked this conversation as resolved.
Show resolved Hide resolved
});
});

it('fails if the iframe does not contain axe-core, and isViolation is true', function(done) {
checkContext._onAsync = function(result) {
assert.isFalse(result);
done();
};
// Timeout after 10ms
checkEvaluate.call(checkContext, iframe, {
timeout: 10,
isViolation: true
});
});
it('does not change result when iframe has not been tested', function() {
var results = [
{
result: undefined,
node: {
ancestry: ['html']
}
},
{
result: undefined,
node: {
ancestry: ['html > body > iframe#1']
}
},
{
result: undefined,
node: {
ancestry: ['html > body > iframe#2']
}
},
{
result: undefined,
node: {
ancestry: ['html > body > iframe#1', 'html']
}
}
];

it('is incomplete if the iframe does not contain axe-core', function(done) {
checkContext._onAsync = function(result) {
assert.isUndefined(result);
done();
};
// Timeout after 10ms
checkEvaluate.call(checkContext, iframe, { timeout: 10 });
var afterResults = frameTestedAfter(results);
assert.lengthOf(afterResults, 2);
assert.isTrue(afterResults[0].result);
assert.isUndefined(afterResults[1].result);
});
});
});