axe-core
Version:
Accessibility engine for automated Web UI testing
494 lines (426 loc) • 12.1 kB
JavaScript
describe('axe.run', function () {
'use strict';
var fixture = document.getElementById('fixture');
var noop = function () {};
var origRunRules = axe._runRules;
beforeEach(function () {
axe._load({
rules: [{
id: 'test',
selector: '*',
none: ['fred']
}],
checks: [{
id: 'fred',
evaluate: function (node) {
this.relatedNodes([node]);
return true;
}
}]
});
});
afterEach(function () {
fixture.innerHTML = '';
axe._audit = null;
axe._runRules = origRunRules;
});
it('takes context, options and callback as parameters', function (done) {
fixture.innerHTML = '<div id="t1"></div>';
var options = {
runOnly: {
type: 'rule',
values: ['test']
}
};
axe.run(['#t1'], options, function () {
assert.ok(true, 'test completed');
done();
});
});
it('uses document as content if it is not specified', function (done) {
axe._runRules = function (ctxt) {
assert.equal(ctxt, document);
done();
};
axe.run({ someOption: true }, noop);
});
it('uses an object as options if it is not specified', function (done) {
axe._runRules = function (ctxt, opt) {
assert.isObject(opt);
done();
};
axe.run(document, noop);
});
it('works with performance logging enabled', function (done) {
axe.run(document, {performanceTimer: true}, function (err, result) {
assert.isObject(result);
done();
});
});
it('treats objects with include or exclude as the context object', function (done) {
axe._runRules = function (ctxt) {
assert.deepEqual(ctxt, {include: '#BoggyB'});
done();
};
axe.run({include: '#BoggyB'}, noop);
});
it('treats objects with neither include or exclude as the option object', function (done) {
axe._runRules = function (ctxt, opt) {
assert.deepEqual(opt.HHG, 'hallelujah');
done();
};
axe.run({HHG: 'hallelujah'}, noop);
});
it('does not fail if no callback is specified', function (done) {
assert.doesNotThrow(function () {
axe.run(done);
});
});
it('should clear axe._tree', function (done) {
var getFlattenedTree = axe.utils.getFlattenedTree;
var thing = 'honey badger';
axe.utils.getFlattenedTree = function () {
return thing;
};
axe._runRules = function () {
assert.isTrue(typeof axe._tree === 'undefined');
axe.utils.getFlattenedTree = getFlattenedTree;
done();
};
axe.run({ someOption: true }, noop);
});
describe('callback', function () {
it('gives errors to the first argument on the callback', function (done) {
axe._runRules = function (ctxt, opt, resolve, reject) {
axe._runRules = origRunRules;
reject('Ninja rope!');
};
axe.run({ reporter: 'raw' }, function (err) {
assert.equal(err, 'Ninja rope!');
done();
});
});
it('gives results to the second argument on the callback', function (done) {
axe._runRules = function (ctxt, opt, resolve) {
axe._runRules = origRunRules;
resolve('MB Bomb', noop);
};
axe.run({ reporter: 'raw' }, function (err, result) {
assert.equal(err, null);
assert.equal(result, 'MB Bomb');
done();
});
});
it('does not run the callback twice if it throws', function (done) {
var calls = 0;
axe._runRules = function (ctxt, opt, resolve) {
resolve([], noop);
};
var log = axe.log;
axe.log = function (e) {
assert.equal(e.message, 'err');
axe.log = log;
};
axe.run(function () {
calls += 1;
if (calls === 1) {
setTimeout(function () {
assert.equal(calls, 1);
axe.log = log;
done();
}, 20);
}
throw new Error('err');
});
});
it('is called after cleanup', function (done) {
var isClean = false;
axe._runRules = function (ctxt, opt, resolve) {
axe._runRules = origRunRules;
// Check that cleanup is called before the callback is executed
resolve('MB Bomb', function cleanup() {
isClean = true;
});
};
axe.run({ reporter: 'raw' }, function () {
assert.isTrue(isClean, 'cleanup must be called first');
done();
});
});
});
describe('promise result', function () {
/*eslint indent: 0*/
var promiseIt = window.Promise ? it : it.skip;
promiseIt('returns an error to catch if axe fails', function (done) {
axe._runRules = function (ctxt, opt, resolve, reject) {
axe._runRules = origRunRules;
reject('I surrender!');
};
var p = axe.run({ reporter: 'raw' });
p.then(noop)
.catch(function (err) {
assert.equal(err, 'I surrender!');
done();
});
assert.instanceOf(p, window.Promise);
});
promiseIt('returns a promise if no callback was given', function (done) {
axe._runRules = function (ctxt, opt, resolve) {
axe._runRules = origRunRules;
resolve('World party', noop);
};
var p = axe.run({ reporter: 'raw' });
p.then(function (result) {
assert.equal(result, 'World party');
done();
});
assert.instanceOf(p, window.Promise);
});
promiseIt('does not error if then() throws', function (done) {
axe._runRules = function (ctxt, opt, resolve) {
resolve([], noop);
};
axe.run()
.then(function () {
throw new Error('err');
}, function (e) {
assert.isNotOk(e, 'Caught callback error in the wrong place');
done();
}).catch(function (e) {
assert.equal(e.message, 'err');
done();
});
});
promiseIt('is called after cleanup', function (done) {
var isClean = false;
axe._runRules = function (ctxt, opt, resolve) {
axe._runRules = origRunRules;
// Check that cleanup is called before the callback is executed
resolve('MB Bomb', function cleanup () {
isClean = true;
});
};
axe.run({ reporter: 'raw' })
.then(function () {
assert(isClean, 'cleanup must be called first');
done();
})
.catch(done);
});
});
describe('option reporter', function () {
it('sets v1 as the default reporter if audit.reporter is null', function (done) {
axe._runRules = function (ctxt, opt) {
assert.equal(opt.reporter, 'v1');
axe._runRules = origRunRules;
done();
};
axe._audit.reporter = null;
axe.run(document, noop);
});
it('uses the audit.reporter if no reporter is set in options', function (done) {
axe._runRules = function (ctxt, opt) {
assert.equal(opt.reporter, 'raw');
axe._runRules = origRunRules;
done();
};
axe._audit.reporter = 'raw';
axe.run(document, noop);
});
it('does not override if another reporter is set', function (done) {
axe._runRules = function (ctxt, opt) {
assert.equal(opt.reporter, 'raw');
axe._runRules = origRunRules;
done();
};
axe._audit.reporter = null;
axe.run(document, {reporter: 'raw'}, noop);
});
});
describe('option xpath', function () {
it('returns no xpath if the xpath option is not set', function (done) {
axe.run('#fixture', function (err, result) {
assert.isUndefined(result.violations[0].nodes[0].xpath);
done();
});
});
it('returns the xpath if the xpath option is true', function (done) {
axe.run('#fixture', {
xpath: true
}, function (err, result) {
assert.deepEqual(
result.violations[0].nodes[0].xpath,
['/div[@id=\'fixture\']']
);
done();
});
});
it('returns xpath on related nodes', function (done) {
axe.run('#fixture', {
xpath: true
}, function (err, result) {
assert.deepEqual(
result.violations[0].nodes[0].none[0].relatedNodes[0].xpath,
['/div[@id=\'fixture\']']
);
done();
});
});
it('returns the xpath on any reporter', function (done) {
axe.run('#fixture', {
xpath: true,
reporter: 'no-passes'
}, function (err, result) {
assert.deepEqual(
result.violations[0].nodes[0].xpath,
['/div[@id=\'fixture\']']
);
done();
});
});
});
describe('option absolutePaths', function () {
it('returns relative paths when falsy', function (done) {
axe.run('#fixture', {
absolutePaths: 0
}, function (err, result) {
assert.deepEqual(
result.violations[0].nodes[0].target,
['#fixture']
);
done();
});
});
it('returns absolute paths when truthy', function (done) {
axe.run('#fixture', {
absolutePaths: 'yes please'
}, function (err, result) {
assert.deepEqual(
result.violations[0].nodes[0].target,
['html > body > #fixture']
);
done();
});
});
it('returns absolute paths on related nodes', function (done) {
axe.run('#fixture', {
absolutePaths: true
}, function (err, result) {
assert.deepEqual(
result.violations[0].nodes[0].none[0].relatedNodes[0].target,
['html > body > #fixture']
);
done();
});
});
});
describe('option restoreScroll', function () {
it('does not change scroll when restoreScroll is not set', function (done) {
var calls = 0;
var _getSS = axe.utils.getScrollState;
var _setSS = axe.utils.setScrollState;
axe.utils.setScrollState = function () {
calls++;
};
axe.utils.getScrollState = axe.utils.setScrollState;
axe.run('#fixture', {}, function () {
assert.equal(calls, 0);
axe.utils.getScrollState = _getSS;
axe.utils.setScrollState = _setSS;
done();
});
});
it('resets scrolLState after running the audit', function (done) {
var scrollState = {};
var calls = 0;
var _getSS = axe.utils.getScrollState;
var _setSS = axe.utils.setScrollState;
axe.utils.setScrollState = function (arg) {
assert.equal(scrollState, arg);
calls++;
};
axe.utils.getScrollState = function () {
return scrollState;
};
axe.run('#fixture', {
restoreScroll: true
}, function () {
assert.equal(calls, 1);
axe.utils.getScrollState = _getSS;
axe.utils.setScrollState = _setSS;
done();
});
});
});
});
describe('axe.run iframes', function () {
'use strict';
var fixture = document.getElementById('fixture');
var origRunRules = axe._runRules;
beforeEach(function () {
fixture.innerHTML = '<div id="target">Target in top frame</div>';
axe._load({
rules: [{
id: 'html',
selector: '#target',
none: ['fred']
}],
checks: [{
id: 'fred',
evaluate: function () {
return true;
}
}]
});
});
afterEach(function () {
fixture.innerHTML = '';
axe._audit = null;
axe._runRules = origRunRules;
});
it('includes iframes by default', function (done) {
var frame = document.createElement('iframe');
frame.addEventListener('load', function () {
var safetyTimeout = window.setTimeout(function () {
done();
}, 1000);
axe.run('#fixture', {}, function (err, result) {
assert.equal(result.violations.length, 1);
var violation = result.violations[0];
assert.equal(violation.nodes.length, 2,
'one node for top frame, one for iframe');
assert.isTrue(violation.nodes.some(function(node) {
return node.target.length === 1 && node.target[0] === '#target';
}), 'one result from top frame');
assert.isTrue(violation.nodes.some(function(node) {
return node.target.length === 2 &&
node.target[0] === '#fixture > iframe';
}), 'one result from iframe');
window.clearTimeout(safetyTimeout);
done();
});
});
frame.src = '../mock/frames/test.html';
fixture.appendChild(frame);
});
it('excludes iframes if iframes is false', function (done) {
var frame = document.createElement('iframe');
frame.addEventListener('load', function () {
var safetyTimeout = setTimeout(function () {
done();
}, 1000);
axe.run('#fixture', { iframes: false }, function (err, result) {
assert.equal(result.violations.length, 1);
var violation = result.violations[0];
assert.equal(violation.nodes.length, 1,
'only top frame');
assert.equal(violation.nodes[0].target.length, 1);
assert.equal(violation.nodes[0].target[0], '#target');
window.clearTimeout(safetyTimeout);
done();
});
});
frame.src = '../mock/frames/test.html';
fixture.appendChild(frame);
});
});