Skip to content

Commit

Permalink
Merge pull request #3 from dwightjack/master
Browse files Browse the repository at this point in the history
Updates and new features
  • Loading branch information
helen-dikareva authored May 13, 2019
2 parents c46532b + c779a28 commit 973e1bd
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 99 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
.idea
.idea
*.lock
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,26 @@ The TestCafe module that allows you to use the [aXe](https://github.com/dequelab
## Installation

```bash
npm install axe-testcafe
npm install axe-core axe-testcafe --save-dev
```

## How to use

You can write a TestCafe test with automated accessibility checks like this.

```js
import axeCheck from 'axe-testcafe';
import { axeCheck, createReport } from 'axe-testcafe';

fixture `TestCafe tests with Axe`
.page `http://example.com`;

test('Automated accessibility testing', async t => {
await axeCheck(t);
const { error, violations } = await axeCheck(t);
await t.expect(violations.length === 0).ok(createReport(violations));
});
```

If any accessibility issues are found, you will see a detailed report.
If any accessibility issues are found, you will see a detailed report generated by the `createReport` function.

![Accessibility errors](https://github.com/helen-dikareva/axe-testcafe/blob/master/errors.png)

Expand All @@ -32,8 +33,8 @@ The `axe-testcafe` module allows you to define the `context` and `options` [axe.

```js
test('Automated accessibility testing', async () => {
var axeContext = { exclude: [['select']] };
var axeOptions = { rules: { 'html-has-lang': { enabled: false } } };

await axeCheck(t, axeContext, axeOptions);
const axeContext = { exclude: [['select']] };
const axeOptions = { rules: { 'html-has-lang': { enabled: false } } };
const { error, violations } = await axeCheck(t, axeContext, axeOptions);
await t.expect(violations.length === 0).ok(createReport(violations));
});
Binary file modified errors.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
declare module 'axe-testcafe' {
import { ElementContext, RunOnly, AxeResults, Result } from 'axe-core';
import 'testcafe';

export function axeCheck(
t: TestController,
context?: ElementContext,
options?: {
runOnly?: RunOnly;
rules?: Object;
iframes?: Boolean;
elementRef?: Boolean;
selectors?: Boolean;
}
): Promise<AxeResults>;

export function createReport(violations: Result[]): string;
}
103 changes: 44 additions & 59 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,74 +1,59 @@
var fs = require('fs');
var path = require('path');
var testcafe = require('testcafe');
const fs = require('fs');
const path = require('path');
const { ClientFunction } = require('testcafe');
const { red, green, reset } = require('chalk');

var ClientFunction = testcafe.ClientFunction;
const AXE_DIR_PATH = path.dirname(require.resolve('axe-core'));
const AXE_SCRIPT = fs.readFileSync(path.join(AXE_DIR_PATH, 'axe.min.js'), 'utf8');

var AXE_DIR_PATH = path.dirname(require.resolve('axe-core'));
var AXE_SCRIPT = fs.readFileSync(path.join(AXE_DIR_PATH, 'axe.min.js')).toString();
const hasAxe = ClientFunction(() => !!(window.axe && window.axe.run));

function AxeError (message) {
Error.call(this, message);
const injectAxe = ClientFunction(() => eval(AXE_SCRIPT), { dependencies: { AXE_SCRIPT } });

this.name = 'AxeError';
this.message = message;

if (typeof Error.captureStackTrace === 'function')
Error.captureStackTrace(this, AxeError);
else
this.stack = (new Error(message)).stack;
}

AxeError.prototype = Object.create(Error.prototype);

var hasAxe = ClientFunction(function () {
return !!(window.axe && window.axe.run);
const runAxe = ClientFunction((context, options = {}) => {
return new Promise((resolve) => {
axe.run(context || document, options, (error, { violations }) => {
resolve({ error, violations });
});
});
});

var injectAxe = ClientFunction(function () {
eval(AXE_SCRIPT);
}, { dependencies: { AXE_SCRIPT: AXE_SCRIPT } });
const createReport = violations => {
if (!violations.length) {
return green('0 violations found');
}

var runAxe = ClientFunction(function (context, options) {
return new Promise(function (resolve) {
axe.run(context || document, options || {}, function (err, results) {
if (err)
return resolve(err.message);
const report = violations.reduce((acc, { nodes, help }, i) => {

var errors = '';
acc += red(`${i+1}) ${help}\n`);

acc += reset(nodes.reduce((e, { target }) => {
const targetNodes = target.map((t) => `"${t}"`).join(', ');
e += `\t${targetNodes}\n`;
return e;
},''));

if (results.violations.length !== 0) {
results.violations.forEach(function (violation) {
errors += violation.help + '\n\tnodes:\n';
return acc;

violation.nodes.forEach(function (node) {
var targetNodes = node.target.map(function (target) {
return '"' + target + '"';
}).join(', ');
}, red(`${violations.length} violations found:\n`));

errors += '\t\t' + targetNodes + '\n';
});
});
}
return reset(report);

};

return resolve(errors);
});
})
});
const axeCheck = async (t, context, options) => {
const hasScript = await hasAxe.with({ boundTestRun: t })();
if (!hasScript)
await injectAxe.with({ boundTestRun: t })();

module.exports = function axeCheck (t, context, options) {
return hasAxe.with({ boundTestRun: t })()
.then(function (result) {
if (!result)
return injectAxe.with({ boundTestRun: t })();
try {
return await runAxe.with({ boundTestRun: t })(context, options);
} catch (e) {
return { error: e };
}
};

return Promise.resolve();
})
.then(function () {
return runAxe.with({ boundTestRun: t })(context, options);
})
.then(function (error) {
if (error)
throw new AxeError('\n' + error);
});
module.exports = {
axeCheck,
createReport
};
67 changes: 36 additions & 31 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
{
"name": "axe-testcafe",
"version": "1.1.0",
"description": "The TestCafe module that allows you to use the aXe accessibility engine in TestCafe tests",
"main": "index.js",
"repository": {
"type": "git",
"url": "https://github.com/helen-dikareva/axe-testcafe"
},
"keywords": [
"axe",
"testcafe",
"test",
"accessibility"
],
"author": "Helen Dikareva ([email protected])",
"license": "MIT",
"bugs": {
"url": "https://github.com/helen-dikareva/axe-testcafe/issues"
},
"homepage": "https://github.com/helen-dikareva/axe-testcafe",
"files": [
"index.js"
],
"dependencies": {
"axe-core": "^2.2.3 || ^3.0.0"
},
"peerDependencies": {
"testcafe": "*"
}
}
{
"name": "axe-testcafe",
"version": "2.0.0",
"description": "The TestCafe module that allows you to use the aXe accessibility engine in TestCafe tests",
"main": "index.js",
"typings": "index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/helen-dikareva/axe-testcafe"
},
"engines": {
"node": ">=8.9.0"
},
"keywords": [
"axe",
"testcafe",
"test",
"accessibility"
],
"author": "Helen Dikareva ([email protected])",
"license": "MIT",
"bugs": {
"url": "https://github.com/helen-dikareva/axe-testcafe/issues"
},
"homepage": "https://github.com/helen-dikareva/axe-testcafe",
"files": [
"index.js"
],
"peerDependencies": {
"axe-core": ">=2.2.3 <4",
"testcafe": "*"
},
"dependencies": {
"chalk": "^2.4.1"
}
}

0 comments on commit 973e1bd

Please sign in to comment.