Skip to content

Commit

Permalink
feat(frame-tested): Use this new rule to test if all frames are avail…
Browse files Browse the repository at this point in the history
…able, instead of axe.log
  • Loading branch information
WilcoFiers committed Mar 7, 2018
1 parent 3e2acb1 commit 83cd17d
Show file tree
Hide file tree
Showing 16 changed files with 353 additions and 3 deletions.
1 change: 1 addition & 0 deletions doc/rule-descriptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
| duplicate-id | Ensures every id attribute value is unique | cat.parsing, wcag2a, wcag411 | true |
| empty-heading | Ensures headings have discernible text | cat.name-role-value, best-practice | true |
| focus-order-semantics | Ensures elements in the focus order have an appropriate role | cat.keyboard, best-practice, experimental | true |
| frame-tested | Ensures <iframe> and <frame> elements contain the axe-core script | wcag2a, wcag2aa, best-practice | true |
| frame-title-unique | Ensures <iframe> and <frame> elements contain a unique title attribute | cat.text-alternatives, best-practice | true |
| frame-title | Ensures <iframe> and <frame> elements contain a non-empty title attribute | cat.text-alternatives, wcag2a, wcag241, section508, section508.22.i | true |
| heading-order | Ensures the order of headings is semantically correct | cat.semantics, best-practice | true |
Expand Down
19 changes: 19 additions & 0 deletions lib/checks/media/frame-tested.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
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(function() {
// This double timeout is important for allowing iframes to respond
// DO NOT REMOVE
timer = setTimeout(function() {
timer = null;
resolve(isViolation ? false : undefined);
}, 0);
}, timeout);

axe.utils.respondable(node.contentWindow, 'axe.ping', null, undefined, function () {
if (timer !== null) {
clearTimeout(timer);
resolve(true);
}
});
15 changes: 15 additions & 0 deletions lib/checks/media/frame-tested.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"id": "frame-tested",
"evaluate": "frame-tested.js",
"options": {
"isViolation": false
},
"metadata": {
"impact": "critical",
"messages": {
"pass": "The iframe was tested with axe-core",
"fail": "The iframe could not be tested with axe-core",
"incomplete": "The iframe still has to be tested with axe-core"
}
}
}
4 changes: 1 addition & 3 deletions lib/core/utils/collect-results-from-frames.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,10 @@ axe.utils.sendCommandToFrame = function(node, parameters, resolve, reject) {
// This double timeout is important for allowing iframes to respond
// DO NOT REMOVE
timeout = setTimeout(function() {
var errMsg = err('No response from frame', node);
if (!parameters.debug) {
axe.log(errMsg);
resolve(null);
} else {
reject(errMsg);
reject(err('No response from frame', node));
}
}, 0);
}, 500);
Expand Down
18 changes: 18 additions & 0 deletions lib/rules/frame-tested.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"id": "frame-tested",
"selector": "frame, iframe",
"tags": [
"wcag2a",
"wcag2aa",
"best-practice"
],
"metadata": {
"description": "Ensures <iframe> and <frame> elements contain the axe-core script",
"help": "Frames must be tested with axe-core"
},
"all": [
"frame-tested"
],
"any": [],
"none": []
}
64 changes: 64 additions & 0 deletions test/checks/media/frame-tested.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
describe('frame-tested', function () {
'use strict';

var checkContext = axe.testUtils.MockCheckContext();
var __respondable;
var respondableCalls = [];
var checkEvaluate = checks['frame-tested'].evaluate.bind(checkContext);
var iframe;

before(function () {
__respondable = axe.utils.respondable;
axe.utils.respondable = function () {
respondableCalls.push(Array.from(arguments));
}
iframe = document.createElement('iframe');
document.querySelector('#fixture').appendChild(iframe);
});

afterEach(function () {
respondableCalls = [];
checkContext.reset();
})

after(function () {
axe.utils.respondable = __respondable;
});

it('correctly calls axe.utils.respondable', function () {
checkEvaluate(iframe);

assert.lengthOf(respondableCalls, 1);
assert.deepEqual(respondableCalls[0].slice(0,4),
[iframe.contentWindow, 'axe.ping', null, undefined]);
assert.isFunction(respondableCalls[0][4]);
});

it('passes if the iframe contains axe-core', function (done) {
checkEvaluate(iframe, { timeout: 20 });
checkContext._onAsync = function (result) {
assert.isTrue(result);
done();
}
// Respond to the ping
respondableCalls[0][4]();
});

it('fails if the iframe does not contain axe-core, and isViolation is true', function (done) {
checkEvaluate(iframe, { timeout: 10, isViolation: true });
// Timeout after 10ms
checkContext._onAsync = function (result) {
assert.isFalse(result);
done();
}
});

it('is incomplete if the iframe does not contain axe-core', function (done) {
checkEvaluate(iframe, { timeout: 10 });
// Timeout after 10ms
checkContext._onAsync = function (result) {
assert.isUndefined(result);
done();
}
});
});
25 changes: 25 additions & 0 deletions test/integration/full/frame-tested/frame-tested-fail.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!doctype html>
<html lang="en">
<head>
<title>frame-tested 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 src="/test/testutils.js"></script>
<script>
mocha.setup({
timeout: 10000,
ui: 'bdd'
});
var assert = chai.assert;
</script>
</head>
<body>
<iframe id="frame" src="frames/nested-no-axe.html"></iframe>
<div id="mocha"></div>
<script src="frame-tested-fail.js"></script>
<script src="/test/integration/adapter.js"></script>
</body>
</html>
44 changes: 44 additions & 0 deletions test/integration/full/frame-tested/frame-tested-fail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
describe('frame-tested-fail test', function () {
'use strict';

var results;
before(function (done) {
axe.testUtils.awaitNestedLoad(function () {
axe.run({
runOnly: { type: 'rule', values: ['frame-tested'] },
checks: {
'frame-tested': { options: { isViolation: true } }
}
}, function (err, r) {
assert.isNull(err);
results = r;
done();
});
});
});

describe('violations', function () {
it('should find 1', function () {
assert.lengthOf(results.violations[0].nodes, 1);
});
it('should find the failing iframe', function () {
assert.deepEqual(results.violations[0].nodes[0].target, ['#frame', '#fail']);
});
});

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

describe('passes', function () {
it('should find 2', function () {
assert.lengthOf(results.passes, 1);
assert.lengthOf(results.passes[0].nodes, 2);

assert.deepEqual(results.passes[0].nodes[0].target, ['#frame']);
assert.deepEqual(results.passes[0].nodes[1].target, ['#frame', '#pass']);
});
});
});
25 changes: 25 additions & 0 deletions test/integration/full/frame-tested/frame-tested-incomplete.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!doctype html>
<html lang="en">
<head>
<title>frame-tested 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 src="/test/testutils.js"></script>
<script>
mocha.setup({
timeout: 10000,
ui: 'bdd'
});
var assert = chai.assert;
</script>
</head>
<body>
<iframe id="incomplete" src="frames/no-axe.html"></iframe>
<div id="mocha"></div>
<script src="frame-tested-incomplete.js"></script>
<script src="/test/integration/adapter.js"></script>
</body>
</html>
36 changes: 36 additions & 0 deletions test/integration/full/frame-tested/frame-tested-incomplete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
describe('frame-tested-incomplete test', function () {
'use strict';

var results;
before(function (done) {
axe.testUtils.awaitNestedLoad(function () {
axe.run({ runOnly: { type: 'rule', values: ['frame-tested'] } }, function (err, r) {
assert.isNull(err);
results = r;
console.log(results);
done();
});
});
});

describe('incomplete', function () {
it('should find 1', function () {
assert.lengthOf(results.incomplete[0].nodes, 1);
});
it('should find first iframe', function () {
assert.deepEqual(results.incomplete[0].nodes[0].target, ['#incomplete']);
});
});

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

describe('passes', function () {
it('should find 0', function () {
assert.lengthOf(results.passes, 0);
});
});
});
25 changes: 25 additions & 0 deletions test/integration/full/frame-tested/frame-tested-pass.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!doctype html>
<html lang="en">
<head>
<title>frame-tested 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 src="/test/testutils.js"></script>
<script>
mocha.setup({
timeout: 10000,
ui: 'bdd'
});
var assert = chai.assert;
</script>
</head>
<body>
<iframe id="pass" src="frames/with-axe.html"></iframe>
<div id="mocha"></div>
<script src="frame-tested-pass.js"></script>
<script src="/test/integration/adapter.js"></script>
</body>
</html>
36 changes: 36 additions & 0 deletions test/integration/full/frame-tested/frame-tested-pass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
describe('frame-tested-pass test', function () {
'use strict';

var results;
before(function (done) {
axe.testUtils.awaitNestedLoad(function () {
axe.run({ runOnly: { type: 'rule', values: ['frame-tested'] } }, function (err, r) {
assert.isNull(err);
results = r;
console.log(results);
done();
});
});
});

describe('passes', function () {
it('should find 1', function () {
assert.lengthOf(results.passes[0].nodes, 1);
});
it('should find first iframe', function () {
assert.deepEqual(results.passes[0].nodes[0].target, ['#pass']);
});
});

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

describe('incomplete', function () {
it('should find 0', function () {
assert.lengthOf(results.incomplete, 0);
});
});
});
12 changes: 12 additions & 0 deletions test/integration/full/frame-tested/frames/nested-no-axe.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html id="pass1">
<head>
<title>Frame with axe-core</title>
<meta charset="utf8">
<script src="/axe.js"></script>
</head>
<body>
<iframe id="pass" src="with-axe.html"></iframe>
<iframe id="fail" src="no-axe.html"></iframe>
</body>
</html>
10 changes: 10 additions & 0 deletions test/integration/full/frame-tested/frames/no-axe.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!doctype html>
<html>
<head>
<title>Frame without axe-core</title>
<meta charset="utf8">
</head>
<body>
<h1>Frame without axe-core</h1>
</body>
</html>
11 changes: 11 additions & 0 deletions test/integration/full/frame-tested/frames/with-axe.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!doctype html>
<html>
<head>
<title>Frame with axe-core</title>
<meta charset="utf8">
<script src="/axe.js"></script>
</head>
<body>
<h1>Frame with axe-core</h1>
</body>
</html>
11 changes: 11 additions & 0 deletions test/testutils.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ testUtils.MockCheckContext = function () {
return {
_relatedNodes: [],
_data: null,
// When using this.async() in a check, assign a function to _onAsync
// to catch the response.
_onAsync: null,
async: function () {
var self = this;
return function (result) {
// throws if _onAsync isn't set
self._onAsync(result, self);
}
},
data: function (d) {
this._data = d;
},
Expand All @@ -20,6 +30,7 @@ testUtils.MockCheckContext = function () {
reset: function () {
this._data = null;
this._relatedNodes = [];
this._onAsync = null;
}
};
};
Expand Down

0 comments on commit 83cd17d

Please sign in to comment.