Skip to content

Commit

Permalink
fix(jsdom): allow axe.setup() without a global window (#4116)
Browse files Browse the repository at this point in the history
* fix(jsdom): allow axe.setup() without a global window

* Caps matter, silly willy
  • Loading branch information
WilcoFiers authored Aug 2, 2023
1 parent 2cc5547 commit 33b0314
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 24 deletions.
37 changes: 19 additions & 18 deletions doc/examples/jsdom/test/a11y.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
/* global describe, it */
const axe = require('axe-core');
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
const assert = require('assert');

describe('axe', () => {
const { window } = new JSDOM(`<!DOCTYPE html>
const { document } = new JSDOM(`<!DOCTYPE html>
<html lang="en">
<head>
<title>JSDOM Example</title>
Expand All @@ -18,30 +19,30 @@ describe('axe', () => {
<p>Not a label</p><input type="text" id="no-label">
</div>
</body>
</html>`);

const axe = require('axe-core');
</html>`).window;
const config = {
rules: {
'color-contrast': { enabled: false }
}
};

it('should report that good HTML is good', function (done) {
var n = window.document.getElementById('working');
axe.run(n, config, function (err, result) {
assert.equal(err, null, 'Error is not null');
assert.equal(result.violations.length, 0, 'Violations is not empty');
done();
});
it('reports that good HTML is good', async () => {
const node = document.getElementById('working');
const result = await axe.run(node, config);
assert.equal(result.violations.length, 0, 'Violations is not empty');
});

it('reports that bad HTML is bad', async () => {
const node = document.getElementById('broken');
const results = await axe.run(node, config);
assert.equal(results.violations.length, 1, 'Violations.length is not 1');
});

it('should report that bad HTML is bad', function (done) {
var n = window.document.getElementById('broken');
axe.run(n, config, function (err, result) {
assert.equal(err, null, 'Error is not null');
assert.equal(result.violations.length, 1, 'Violations.length is not 1');
done();
});
it('allows commons after axe.setup() is called', () => {
axe.setup(document);
const input = document.querySelector('input');
const role = axe.commons.aria.getRole(input);
assert.equal(role, 'textbox');
axe.teardown();
});
});
10 changes: 4 additions & 6 deletions lib/core/public/run.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getReporter } from './reporter';
import normalizeRunParams from './run/normalize-run-params';
import { setupGlobals, resetGlobals } from './run/globals-setup';
import { setupGlobals } from './run/globals-setup';
import { assert } from '../utils';

const noop = () => {};
Expand Down Expand Up @@ -36,10 +36,10 @@ export default function run(...args) {
axe.utils.performanceTimer.start();
}

function handleRunRules(rawResults, cleanup) {
function handleRunRules(rawResults, teardown) {
const respond = results => {
axe._running = false;
cleanup();
teardown();
try {
callback(null, results);
} catch (e) {
Expand All @@ -55,7 +55,7 @@ export default function run(...args) {
createReport(rawResults, options, respond);
} catch (err) {
axe._running = false;
cleanup();
teardown();
callback(err);
reject(err);
}
Expand All @@ -66,7 +66,6 @@ export default function run(...args) {
axe.utils.performanceTimer.end();
}
axe._running = false;
resetGlobals();
callback(err);
reject(err);
}
Expand Down Expand Up @@ -97,7 +96,6 @@ function createReport(rawResults, options, respond) {
}

function handleError(err, callback) {
resetGlobals();
if (typeof callback === 'function' && callback !== noop) {
callback(err.message);
return;
Expand Down
10 changes: 10 additions & 0 deletions lib/core/public/setup.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getFlattenedTree, getSelectorData } from '../utils';
import { setupGlobals } from './run/globals-setup';

/**
* Setup axe-core so axe.common functions can work properly.
Expand All @@ -10,7 +11,16 @@ function setup(node) {
'Axe is already setup. Call `axe.teardown()` before calling `axe.setup` again.'
);
}
// Normalize document
if (
node &&
typeof node.documentElement === 'object' &&
typeof node.defaultView === 'object'
) {
node = node.documentElement;
}

setupGlobals(node);
axe._tree = getFlattenedTree(node);
axe._selectorData = getSelectorData(axe._tree);

Expand Down
5 changes: 5 additions & 0 deletions test/core/public/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ describe('axe.setup', function () {
assert.exists(axe._selectorData);
});

it('takes documentElement when passed the document', () => {
axe.setup(document);
assert.equal(axe._tree[0].actualNode, document.documentElement);
});

it('should throw if called twice in a row', function () {
function fn() {
axe.setup();
Expand Down
33 changes: 33 additions & 0 deletions test/node/jsdom.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,37 @@ describe('jsdom axe-core', () => {
});
});
});

describe('axe.setup()', () => {
afterEach(() => {
axe.teardown();
});

it('sets up the tree', function () {
const { document } = new jsdom.JSDOM(domStr).window;
const tree = axe.setup(document.body);
assert.equal(tree, axe._tree[0]);
assert.equal(tree.actualNode, document.body);
});

it('can use commons after axe.setup()', () => {
const { document } = new jsdom.JSDOM(domStr).window;
axe.setup(document);

const skipLink = document.querySelector('#skip');
assert.equal(axe.commons.aria.getRole(skipLink), 'link');
assert.equal(axe.commons.text.accessibleText(skipLink), 'Skip Link');
});

it('is cleaned up with axe.teardown()', () => {
const { document } = new jsdom.JSDOM(domStr).window;
axe.setup(document);
axe.teardown();
const skipLink = document.querySelector('#skip');

assert.throws(() => {
assert.equal(axe.commons.aria.getRole(skipLink), 'link');
});
});
});
});

0 comments on commit 33b0314

Please sign in to comment.