From 073d74e803fa3a7433415f3c943b734a653132b7 Mon Sep 17 00:00:00 2001 From: Stephen Mathieson Date: Thu, 14 May 2020 11:05:17 -0400 Subject: [PATCH 01/11] feat: Add `@axe-core/cli` --- .eslintrc.js | 13 +- package.json | 4 +- packages/cli/.eslintrc.js | 14 + packages/cli/CHANGELOG.md | 144 +++++ packages/cli/README.md | 185 ++++++ packages/cli/axe-cli | 3 + packages/cli/index.js | 261 ++++++++ packages/cli/lib/axe-test-urls.js | 121 ++++ packages/cli/lib/save-outcome.js | 26 + packages/cli/lib/utils.js | 80 +++ packages/cli/lib/webdriver.js | 49 ++ packages/cli/package-lock.json | 925 +++++++++++++++++++++++++++++ packages/cli/package.json | 45 ++ packages/cli/test/axe-test-urls.js | 92 +++ packages/cli/test/integrations.js | 73 +++ packages/cli/test/testpage.html | 27 + packages/cli/test/webdriver.js | 108 ++++ 17 files changed, 2166 insertions(+), 4 deletions(-) create mode 100644 packages/cli/.eslintrc.js create mode 100644 packages/cli/CHANGELOG.md create mode 100644 packages/cli/README.md create mode 100755 packages/cli/axe-cli create mode 100755 packages/cli/index.js create mode 100644 packages/cli/lib/axe-test-urls.js create mode 100644 packages/cli/lib/save-outcome.js create mode 100644 packages/cli/lib/utils.js create mode 100644 packages/cli/lib/webdriver.js create mode 100644 packages/cli/package-lock.json create mode 100644 packages/cli/package.json create mode 100644 packages/cli/test/axe-test-urls.js create mode 100644 packages/cli/test/integrations.js create mode 100644 packages/cli/test/testpage.html create mode 100644 packages/cli/test/webdriver.js diff --git a/.eslintrc.js b/.eslintrc.js index 0002dd94..94e679d6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -19,6 +19,15 @@ module.exports = { }, env: { node: true, - browser: true - } + browser: true, + es6: true + }, + overrides: [ + { + files: '*.js', + rules: { + '@typescript-eslint/explicit-function-return-type': 0 + } + } + ] }; diff --git a/package.json b/package.json index 13f44944..83f3fbc5 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "license": "MPL-2.0", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "fmt": "prettier --write *.{md,js,json}", - "lint": "eslint *.js", + "fmt": "prettier --write *.{md,js,json} 'packages/**/*.{js,ts,tsx,html,json,md,css}'", + "lint": "eslint *.js 'packages/**/*.{js,ts,tsx}'", "bootstrap": "lerna bootstrap" }, "devDependencies": { diff --git a/packages/cli/.eslintrc.js b/packages/cli/.eslintrc.js new file mode 100644 index 00000000..2986a5e3 --- /dev/null +++ b/packages/cli/.eslintrc.js @@ -0,0 +1,14 @@ +module.exports = { + rules: { + '@typescript-eslint/no-var-requires': 0, + '@typescript-eslint/no-empty-function': 0 + }, + overrides: [ + { + files: 'test/**/*.js', + env: { + mocha: true + } + } + ] +}; diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md new file mode 100644 index 00000000..ffa0b400 --- /dev/null +++ b/packages/cli/CHANGELOG.md @@ -0,0 +1,144 @@ +# Changelog + +All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. + +## [3.2.0](https://github.com/dequelabs/axe-cli/compare/v3.1.1...v3.2.0) (2019-09-30) + +### Bug Fixes + +- **circleci:** use ([7a8ca66](https://github.com/dequelabs/axe-cli/commit/7a8ca66)) + +### Features + +- **webdriver:** add option ([134b299](https://github.com/dequelabs/axe-cli/commit/134b299)) + +### [3.1.1](https://github.com/dequelabs/axe-cli/compare/v3.1.0...v3.1.1) (2019-08-20) + +### Bug Fixes + +- Update Chromedriver dependency [#103](https://github.com/dequelabs/axe-cli/issues/103) [#104](https://github.com/dequelabs/axe-cli/issues/104) + +## [3.1.0](https://github.com/dequelabs/axe-cli/compare/v3.0.0...v3.1.0) (2019-07-15) + +### Bug Fixes + +- correct scope documentation ([#80](https://github.com/dequelabs/axe-cli/issues/80)) ([81b4312](https://github.com/dequelabs/axe-cli/commit/81b4312)), closes [#75](https://github.com/dequelabs/axe-cli/issues/75) + +### Features + +- add `--chrome-options` flag ([#81](https://github.com/dequelabs/axe-cli/issues/81)) ([6214bcb](https://github.com/dequelabs/axe-cli/commit/6214bcb)), closes [#65](https://github.com/dequelabs/axe-cli/issues/65) +- add `--stdout` flag ([#83](https://github.com/dequelabs/axe-cli/issues/83)) ([06328bf](https://github.com/dequelabs/axe-cli/commit/06328bf)), closes [#15](https://github.com/dequelabs/axe-cli/issues/15) +- add meta data to cli output ([#94](https://github.com/dequelabs/axe-cli/issues/94)) ([7ee59e9](https://github.com/dequelabs/axe-cli/commit/7ee59e9)) + +### Tests + +- make "ready class" test more forgiving ([#74](https://github.com/dequelabs/axe-cli/issues/74)) ([fc2b595](https://github.com/dequelabs/axe-cli/commit/fc2b595)) + + + +# [3.0.0](https://github.com/dequelabs/axe-cli/compare/v2.1.0-alpha.1...v3.0.0) (2018-03-28) + +### Features + +- Update to [axe-core 3.0.0](https://github.com/dequelabs/axe-core/releases/tag/v3.0.0) +- Add --load-delay option to delay audit after page loads ([#53](https://github.com/dequelabs/axe-cli/issues/53)) ([c0659a8](https://github.com/dequelabs/axe-cli/commit/c0659a8)) +- Upgrade chromedriver to support Chrome 65 ([e4d4bd1](https://github.com/dequelabs/axe-cli/commit/e4d4bd1)) + + + +# [2.1.0-alpha.1](https://github.com/dequelabs/axe-cli/compare/v2.1.0-alpha.0...v2.1.0-alpha.1) (2018-02-21) + +### Features + +- Support aXe-core 3.0 Shadow DOM selectors ([#49](https://github.com/dequelabs/axe-cli/issues/49)) ([790b421](https://github.com/dequelabs/axe-cli/commit/790b421)) + + + +# [2.1.0-alpha.0](https://github.com/dequelabs/axe-cli/compare/v2.0.0...v2.1.0-alpha.0) (2018-02-20) + +### Bug Fixes + +- Security vulnerability in hoek package ([#50](https://github.com/dequelabs/axe-cli/issues/50)) ([81695ad](https://github.com/dequelabs/axe-cli/commit/81695ad)) + +### Features + +- Upgrade axe-core to 3.0.0-beta.1 +- Upgrade axe-webdriverjs to 2.0.0-alpha.1 + + + +## [2.0.0](https://github.com/dequelabs/axe-cli/compare/v1.3.1...v2.0.0) (2017-12-19) + +### Features + +- Use chrome-headless as default browser replacing PhantomJS ([1ae8e12](https://github.com/dequelabs/axe-cli/commit/1ae8e12)) + +### BREAKING CHANGES + +- PhantomJS is no longer maintained. We will be + replacing it with headless Chrome + + + +## [1.3.1](https://github.com/dequelabs/axe-cli/compare/v1.3.0...v1.3.1) (2017-12-19) + +### Features + +- Add axe-core 2.6.0 + + + +# [1.3.0](https://github.com/dequelabs/axe-cli/compare/v1.1.1...v1.3.0) (2017-11-17) + +### Bug Fixes + +- package.json & .snyk to reduce vulnerabilities ([#39](https://github.com/dequelabs/axe-cli/issues/39)) ([9b20eef](https://github.com/dequelabs/axe-cli/commit/9b20eef)) + +### Features + +- Add flag that enables supplying a list of rules to be skipped during the analysis ([d22903d](https://github.com/dequelabs/axe-cli/commit/d22903d)) +- Allow running from file:// and ftp(s):// ([#41](https://github.com/dequelabs/axe-cli/issues/41)) ([aa3d937](https://github.com/dequelabs/axe-cli/commit/aa3d937)) +- Link to DeqeuU courses/testingmethods ([#38](https://github.com/dequelabs/axe-cli/issues/38)) ([8c0e661](https://github.com/dequelabs/axe-cli/commit/8c0e661)) + + + +# [1.2.0](https://github.com/dequelabs/axe-cli/compare/1.0.2...1.2.0) (2017-10-31) + +### Features + +- Allow running from file:// and ftp(s):// ([#41](https://github.com/dequelabs/axe-cli/issues/41)) ([aa3d937](https://github.com/dequelabs/axe-cli/commit/aa3d937)) +- Link to DeqeuU courses/testingmethods ([#38](https://github.com/dequelabs/axe-cli/issues/38)) ([8c0e661](https://github.com/dequelabs/axe-cli/commit/8c0e661)) +- support exit codes ([e14e2d5](https://github.com/dequelabs/axe-cli/commit/e14e2d5)), closes [#20](https://github.com/dequelabs/axe-cli/issues/20) [#22](https://github.com/dequelabs/axe-cli/issues/22) + + + +## [1.1.1](https://github.com/dequelabs/axe-cli/compare/1.0.3...1.1.1) (2017-09-20) + +### New Features + +- feat: Add --timeout and --timer options ([6d4d14f](https://github.com/dequelabs/axe-cli/commit/6d4d14f80e63bef2d54b3704a818a8ca8b1bb0e3)) +- chore: upgrade axe-core to 2.4.1, axe-webdriverjs to 1.1.5 ([933f1fd](https://github.com/dequelabs/axe-cli/commit/933f1fdb60b06c6fbbcf6d77763dd334d4df8d73)) + +### Bug Fixes + +- doc: Changed non-working promo url for courses to use a working url ([ca7361e](https://github.com/dequelabs/axe-cli/commit/ca7361e653ccb8f3a0138d0dc5f800ff09136351)) + + + +## [1.0.3](https://github.com/dequelabs/axe-cli/compare/1.0.2...1.0.3) (2017-07-05) + +### New Features + +- chore: update axe/webdriverjs to 2.3.1 ([c16bc2f](https://github.com/dequelabs/axe-cli/commit/c16bc2f48f60fbdc556c983db396794cad083a71)) +- feat: support exit codes ([e14e2d5](https://github.com/dequelabs/axe-cli/commit/e14e2d503fc52e6ca38378dd865f8948ed1f9d88)) + + + +## [1.0.2](https://github.com/dequelabs/axe-cli/compare/043d0a4...1.0.2) (2017-05-06) + +### Bug Fixes + +- add correct Selenium server URL ([043d0a4](https://github.com/dequelabs/axe-cli/commit/043d0a4)) +- add node version restriction ([#14](https://github.com/dequelabs/axe-cli/issues/14)) ([b9ff463](https://github.com/dequelabs/axe-cli/commit/b9ff463)) +- handle phantomjs and selenium without errors ([afedd67](https://github.com/dequelabs/axe-cli/commit/afedd67)) +- remove extraneous driver kill ([870f6de](https://github.com/dequelabs/axe-cli/commit/870f6de)) diff --git a/packages/cli/README.md b/packages/cli/README.md new file mode 100644 index 00000000..be04d8a9 --- /dev/null +++ b/packages/cli/README.md @@ -0,0 +1,185 @@ +# axe-cli + +[![Greenkeeper badge](https://badges.greenkeeper.io/dequelabs/axe-cli.svg)](https://greenkeeper.io/) + +[![Join the chat at https://gitter.im/dequelabs/axe-core](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dequelabs/axe-core?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Version](https://img.shields.io/npm/v/axe-cli.svg)](https://www.npmjs.com/package/axe-cli) +[![License](https://img.shields.io/npm/l/axe-cli.svg)](LICENSE) + +Provides a command line interface for [aXe](https://github.com/dequelabs/axe-core) to run quick accessibility tests. + +## Getting Started + +Install [Node.js](https://docs.npmjs.com/getting-started/installing-node) if you haven't already. This project requires Node 6+. By default, axe-cli runs Chrome in headless mode, which requires Chrome 59 or up. + +Install axe-cli globally: `npm install axe-cli -g` + +Lastly, install the webdrivers of the browsers you wish to use. A webdriver is a driver for your web browsers. It allows other programs on your machine to open a browser and operate it. Current information about available webdrivers can be found at [selenium-webdriver project](https://www.npmjs.com/package/selenium-webdriver). Alternatively, you could use [Webdriver manager](https://www.npmjs.com/package/webdriver-manager) + +## Usage + +After installing, you can now run the `axe` command in your CLI, followed by the URL of the page you wish to test: + +``` +axe https://www.deque.com +``` + +You can run multiple pages at once, simply add more URLs to the command. Keep in mind that axe-cli is not a crawler, so if you find yourself testing dozens of pages at once, you may want to consider switching over to something like [axe-webdriverjs](https://www.npmjs.com/package/axe-webdriverjs). If you do not specify the protocol, http will be used by default: + +``` +axe www.deque.com, dequeuniversity.com +``` + +**Note:** If you are having difficulty with the color scheme, use `--no-color` to disable text styles. + +## Running specific rules + +You can use the `--rules` flag to set which rules you wish to run, or you can use `--tags` to tell axe to run all rules that have that specific tag. For example: + +``` +axe www.deque.com --rules color-contrast,html-has-lang +``` + +Or, to run all wcag2a rules: + +``` +axe www.deque.com --tags wcag2a +``` + +In case you want to disable some rules, you can use `--disable` followed by a list of rules. These will be skipped when analyzing the site: + +``` +axe www.deque.com --disable color-contrast +``` + +This option can be combined with either `--tags` or `--rules`. + +A list of rules and what tags they have is available at: https://dequeuniversity.com/rules/worldspace/3.0/. + +## Saving the results + +Results can be saved as JSON data, using the `--save` and `--dir` flags. By passing a filename to `--save` you indicate how the file should be called. If no filename is passed, a default will be used. For example: + +``` +axe www.deque.com --save deque-site.json +``` + +Or: + +``` +axe www.deque.com --dir ./axe-results/ +``` + +## Sending results to STDOUT + +To output the test results to STDOUT, provide the `--stdout` flag. This flag has the side-effect of silencing all other logs/output (other than errors, which are written to STDERR). + +To print the entire result object to your terminal, do: + +``` +axe --stdout www.deque.com +``` + +To pipe the results to a file, do: + +``` +axe --stdout www.deque.com > your_file.json +``` + +To pipe the results to a JSON-parsing program for further processing, do: + +``` +axe --stdout www.deque.com | jq ".[0].violations" +``` + +## Defining the scope of a test + +If you want to only test a specific area of a page, or wish to exclude some part of a page you can do so using the `--include` and `--exclude` flags and pass it a CSS selector: + +``` +axe www.deque.com --include "#main" --exclude "#aside" +``` + +You may pass multiple selectors with a comma-delimited string. For example: + +``` +axe www.deque.com --include "#div1,#div2,#div3" +``` + +## Custom axe-core versions + +Axe-cli will look for locally available versions of axe-core. If the directory from where you start axe-cli has an `axe.js` file, or has a `node_modules` directory with axe-core installed in it. Axe-cli will use this version of axe-core instead of the default version installed globally. + +To specify the exact file axe-core file axe-cli should use, you can use the `--axe-source` flag (`-a` for short), with a relative or absolute path to the file. + +``` +axe www.deque.com --axe-source ./axe.nl.js +``` + +## Different browsers + +Axe-cli can run in a variety of web browsers. By default axe-cli uses Chrome in headless mode. But axe-cli is equally capable of testing pages using other web browsers. **Running in another browser requires that browser's webdriver to be available on your PATH**. You can find a list of available webdrivers and how to install them at: https://seleniumhq.github.io/docs/wd.html + +To run axe-cli using another browser, pass it in as the `--browser` option: + +``` +axe www.deque.com --browser chrome +``` + +Or for short: + +``` +axe www.deque.com -b c +``` + +## Custom Chrome Flags + +When using the Headless Chrome browser, you may provide any number of [flags to configure how the browser functions](https://peter.sh/experiments/chromium-command-line-switches/). + +Options are passed by name, without their leading `--` prefix. For example, to provide the `--no-sandbox --disable-setuid-sandbox --disable-dev-shm-usage` flags to the Chrome binary, you'd do: + +``` +axe --chrome-options="no-sandbox,disable-setuid-sandbox,disable-dev-shm-usage" www.deque.com +``` + +## CI integration + +Axe-cli can be ran within the CI tooling for your project. Many tools are automatically configured to halt/fail builds when a process exits with a code of `1`. + +Use the `--exit` flag, `-q` for short, to have the axe-cli process exit with a failure code `1` when any rule fails to pass. + +``` +axe www.deque.com --exit +``` + +## Timing and timeout + +For debugging and managing timeouts, there are two options available. With `--timer` set, axe-cli will log how long it takes to load the page, and how long it takes to run axe-core. If you find the execution of axe-core takes too long, which can happen on very large pages, use `--timeout` to increase the time axe has to test that page: + +``` +axe www.cnn.com --timeout=120 +``` + +## Delay audit to ensure page is loaded + +If you find your page is not ready after axe has determined it has loaded, you can use `--load-delay` followed by a number in milliseconds. This will make axe wait that time before running the audit after the page has loaded. + +``` +axe www.deque.com --load-delay=2000 +``` + +## Verbose output + +To see additional information like test tool name, version and environment details, use the `--verbose` flag, `-v` for short. + +``` +axe www.deque.com --verbose +``` + +## ChromeDriver Path + +If you need to test your page using an older version of Chrome, you can use `--chromedriver-path` followed by the absolute path to the desired version of the ChromeDriver executable. + +``` +axe www.deque.com --chromedriver-path="absolute/path/to/chromedriver" +``` diff --git a/packages/cli/axe-cli b/packages/cli/axe-cli new file mode 100755 index 00000000..111ce84a --- /dev/null +++ b/packages/cli/axe-cli @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +require('./index') \ No newline at end of file diff --git a/packages/cli/index.js b/packages/cli/index.js new file mode 100755 index 00000000..2ee3ea9b --- /dev/null +++ b/packages/cli/index.js @@ -0,0 +1,261 @@ +'use strict'; + +const program = require('commander'); +const colors = require('colors'); +const link = colors.underline.blue; +const error = colors.red.bold; +const version = require('./package.json').version; +const axeTestUrls = require('./lib/axe-test-urls'); +const saveOutcome = require('./lib/save-outcome'); +const utils = require('./lib/utils'); + +program + .version(version) + .usage(' [options]') + .option( + '-i, --include ', + 'CSS selector of included elements, comma separated', + utils.splitList + ) + .option( + '-e, --exclude ', + 'CSS selector of included elements, comma separated', + utils.splitList + ) + .option( + '-r, --rules ', + 'IDs of rules to run, comma separated', + utils.splitList + ) + .option( + '-t, --tags ', + 'Tags of rules to run, comma separated', + utils.splitList + ) + .option( + '-l, --disable ', + 'IDs of rules to disable, comma separated', + utils.splitList + ) + .option( + '-b, --browser [browser-name]', + 'Which browser to run (Webdriver required)' + ) + .option( + '-s, --save [filename]', + 'Save the output as a JSON file. Filename is optional' + ) + .option( + '-j, --stdout', + 'Output results to STDOUT and silence all other output' + ) + .option('-d, --dir ', 'Output directory') + .option('-a, --axe-source ', 'Path to axe.js file') + .option('-q, --exit', 'Exit with `1` failure code if any a11y tests fail') + .option( + '--load-delay ', + 'Set how much time (milliseconds) axe will wait after page load before running the audit (default: 0)', + 0 + ) + .option( + '--timeout ', + 'Set how much time (seconds) axe has to run (default: 90)', + 90 + ) + .option('--timer', 'Log the time it takes to run') + .option('--show-errors', 'Display the full error stack') + // TODO: Replace this with a reporter option, this required adding + // a reporter option to axe-webdriverjs + .option('--no-reporter', 'Turn the CLI reporter off') + .option( + '--chrome-options [options]', + 'Options to provide to headless Chrome', + utils.splitList + ) + .option( + '-v, --verbose', + 'Output metadata like test tool name, version and environment' + ) + .option( + '--chromedriver-path ', + 'Absolute path to the desired chromedriver executable' + ) + // .option('-c, --config ', 'Path to custom axe configuration') + .parse(process.argv); + +const silentMode = !!program.stdout; + +program.browser = utils.parseBrowser(program.browser); +program.axeSource = utils.getAxeSource(program.axeSource); + +if (!program.axeSource) { + console.error(error('Unable to find the axe-core source file.')); + return; +} + +if (program.chromeOptions) { + if (program.browser !== 'chrome-headless') { + console.error( + error('You may only provide --chrome-options when using headless chrome') + ); + process.exit(2); + } + + program.chromeOptions = program.chromeOptions.map(option => `--${option}`); +} + +let cliReporter; +if (program.reporter === false || silentMode) { + cliReporter = function () {}; +} else { + cliReporter = function (...args) { + console.log(...args); + }; +} + +// Try to match the version of axe that's used +const axeVersion = utils.getAxeVersion(program.axeSource); + +if (!silentMode) { + // Setup axe with the appropriate config + console.log( + colors.bold('Running axe-core ' + axeVersion + ' in ' + program.browser) + ); +} + +// Make valid URLs of all pages +const urls = program.args.map(utils.parseUrl); + +if (urls.length === 0) { + console.error(error('No url was specified. Check `axe -h` for help\n')); + process.exitCode = 1; + return; +} + +// Run axe inside the pages +axeTestUrls(urls, program, { + /** + * Inform the user what page is tested + */ + onTestStart: function (url) { + if (silentMode) { + return; + } + + console.log( + colors.bold('\nTesting ' + link(url)) + + ' ... please wait, this may take a minute.' + ); + if (program.timer) { + console.time('Total test time'); + } + }, + + /** + * Put the result in the console + */ + onTestComplete: function logResults(results) { + const { violations, testEngine, testEnvironment, testRunner } = results; + + if (violations.length === 0) { + cliReporter(colors.green(' 0 violations found!')); + return; + } + + const issueCount = violations.reduce((count, violation) => { + cliReporter( + '\n' + + error(' Violation of %j with %d occurrences!\n') + + ' %s. Correct invalid elements at:\n' + + violation.nodes + .map(node => ' - ' + utils.selectorToString(node.target) + '\n') + .join('') + + ' For details, see: %s', + violation.id, + violation.nodes.length, + violation.description, + link(violation.helpUrl.split('?')[0]) + ); + return count + violation.nodes.length; + }, 0); + + cliReporter(error('\n%d Accessibility issues detected.'), issueCount); + + if (program.verbose) { + const metadata = { + 'Test Runner': testRunner, + 'Test Engine': testEngine, + 'Test Environment': testEnvironment + }; + cliReporter(`\n${JSON.stringify(metadata, null, 2)}`); + } + + if (program.exit) { + process.exitCode = 1; + } + } +}) + .then(function (outcome) { + if (silentMode) { + process.stdout.write(JSON.stringify(outcome, null, 2)); + return; + } + + console.log(''); + if (program.timer) { + console.timeEnd('Total test time'); + } + // All results are in, quit the browser, and give a final report + if (outcome.length > 1) { + console.log( + colors.bold.underline('Testing complete of %d pages\n'), + outcome.length + ); + } else if (program.timer) { + console.log(''); + } + + // Save the outcome + if (program.save || program.dir) { + return saveOutcome(outcome, program.save, program.dir) + .then(fileName => { + console.log('Saved file at', fileName, '\n'); + }) + .catch(err => { + console.error(error('Unable to save file!\n') + err); + process.exitCode = 1; + return Promise.resolve(); + }); + } else { + return Promise.resolve(); + } + }) + .then(() => { + if (silentMode) { + return; + } + // Give a notification that 0 issues in axe doesn't mean perfect a11y + console.log( + colors.italic( + 'Please note that only 20% to 50% of all accessibility ' + + 'issues can automatically be detected. \nManual testing is ' + + 'always required. For more information see:\n%s\n' + ), + link('https://dequeuniversity.com/curriculum/courses/testingmethods') + ); + }) + .catch(e => { + console.error(' '); + if (!program['show-errors']) { + console.error(error('An error occurred while testing this page.')); + } else { + console.error(error('Error: %j \n $s'), e.message, e.stack); + } + + console.error( + 'Please report the problem to: ' + + link('https://github.com/dequelabs/axe-cli/issues/') + + '\n' + ); + process.exit(1); + }); diff --git a/packages/cli/lib/axe-test-urls.js b/packages/cli/lib/axe-test-urls.js new file mode 100644 index 00000000..2b4a8dd9 --- /dev/null +++ b/packages/cli/lib/axe-test-urls.js @@ -0,0 +1,121 @@ +'use strict'; + +const WebDriver = require('selenium-webdriver'); +const AxeBuilder = require('axe-webdriverjs'); +const { startDriver, stopDriver } = require('./webdriver'); + +function testPages(urls, config, events) { + const driver = config.driver; + // Setup webdriver + if (!driver) { + return startDriver(config).then(function (config) { + return testPages(urls, config, events); + }); + } + + // End of the line, no more page left + if (urls.length === 0) { + stopDriver(config); + return Promise.resolve([]); + } + + return new Promise((resolve, reject) => { + // Grab the first item on the URL list + const currentUrl = urls[0].replace(/[,;]$/, ''); + + if (events.onTestStart) { + events.onTestStart(currentUrl); + } + if (config.timer) { + console.log(' '); + console.time('page load time'); + } + + driver + .get(currentUrl) + .then(function () { + // Wait for the page to be loaded + return driver.executeAsyncScript(function (callback) { + const script = document.createElement('script'); + script.innerHTML = + 'document.documentElement.classList.add("deque-axe-is-ready");'; + document.documentElement.appendChild(script); + callback(); + }); + }) + .then(function () { + return driver.wait( + WebDriver.until.elementsLocated( + WebDriver.By.css('.deque-axe-is-ready') + ) + ); + }) + .then(() => { + if (config.timer) { + console.timeEnd('page load time'); + } + + if (config.loadDelay > 0) { + console.log( + 'Waiting for ' + + config.loadDelay + + ' milliseconds after page load...' + ); + } + return new Promise(function (resolve) { + setTimeout(resolve, config.loadDelay); + }); + }) + .then(() => { + // Set everything up + const axe = AxeBuilder(driver, config.axeSource); + + if (Array.isArray(config.include)) { + config.include.forEach(include => axe.include(include)); + } + if (Array.isArray(config.exclude)) { + config.exclude.forEach(exclude => axe.exclude(exclude)); + } + + // Can not use withTags and withRules together + if (config.tags) { + axe.withTags(config.tags); + } else if (config.rules) { + axe.withRules(config.rules); + } + if (config.disable) { + axe.disableRules(config.disable); + } + if (config.timer) { + console.time('axe-core execution time'); + } + + // Run axe + axe.analyze(function (err, results) { + if (config.timer) { + console.timeEnd('axe-core execution time'); + } + + if (err) { + return reject(err); + } + + // Notify about the update + if (events.onTestComplete) { + events.onTestComplete(results); + } + + // Move to the next item + testPages(urls.slice(1), config, events).then(out => { + resolve([results].concat(out)); + }); + }); + }) + .catch(e => { + driver.quit(); + reject(e); + }); + }); +} + +module.exports = testPages; diff --git a/packages/cli/lib/save-outcome.js b/packages/cli/lib/save-outcome.js new file mode 100644 index 00000000..28207434 --- /dev/null +++ b/packages/cli/lib/save-outcome.js @@ -0,0 +1,26 @@ +'use strict'; +const fs = require('fs'); +const path = require('path'); + +module.exports = function saveOutcome(outcome, fileName, dir) { + return new Promise((resolve, reject) => { + if (typeof fileName !== 'string') { + fileName = 'axe-results-' + Date.now() + '.json'; + } + + if (typeof dir !== 'string') { + dir = process.cwd(); + } else if (!path.isAbsolute(dir)) { + dir = path.join(process.cwd(), dir); + } + + const filePath = path.join(dir, fileName); + fs.writeFile(filePath, JSON.stringify(outcome, null, ' '), 'utf8', err => { + if (err) { + reject(err); + } else { + resolve(filePath); + } + }); + }); +}; diff --git a/packages/cli/lib/utils.js b/packages/cli/lib/utils.js new file mode 100644 index 00000000..65c629bc --- /dev/null +++ b/packages/cli/lib/utils.js @@ -0,0 +1,80 @@ +module.exports.parseUrl = function parseUrl(url) { + if (!/[a-z]+:\/\//.test(url)) { + return 'http://' + url; + } + return url; +}; + +module.exports.parseBrowser = function parseBrowser(browser) { + if (!browser) { + return 'chrome-headless'; + } + + const l = browser.length; + switch (browser.toLowerCase()) { + case 'ff': + case 'firefox'.substr(0, l): + case 'gecko'.substr(0, l): + case 'marionette'.substr(0, l): + return 'firefox'; + + case 'chrome'.substr(0, l): + return 'chrome'; + + case 'ie': + case 'explorer'.substr(0, l): + case 'internetexplorer'.substr(0, l): + case 'internet_explorer'.substr(0, l): + case 'internet-explorer'.substr(0, l): + return 'ie'; + + case 'safari'.substr(0, l): + return 'safari'; + + case 'edge'.substr(0, l): + case 'microsoftedge'.substr(0, l): + return 'MicrosoftEdge'; + + default: + throw new Error('Unknown browser ' + browser); + } +}; + +module.exports.getAxeSource = function getAxeSource(axePath) { + const path = require('path'); + const fs = require('fs'); + // Abort if axePath should exist, and it isn't + if (axePath && !fs.existsSync(axePath)) { + return; + // Look for axe in CWD + } else if (!axePath) { + axePath = path.join(process.cwd(), './axe.js'); + } + + if (!fs.existsSync(axePath)) { + // Look for axe in CDW ./node_modules + axePath = path.join(process.cwd(), './node_modules/axe-core/axe.js'); + } + if (!fs.existsSync(axePath)) { + // if all else fails, use the locally installed axe + axePath = path.join(__dirname, '../node_modules/axe-core/axe.js'); + } + + return fs.readFileSync(axePath, 'utf8'); +}; + +module.exports.getAxeVersion = function getAxeVersion(source) { + const match = source.match(/\.version\s*=\s'([^']+)'/); + return match ? match[1] : 'unknown version'; +}; + +module.exports.splitList = function (val) { + return val.split(/[,;]/).map(str => str.trim()); +}; + +module.exports.selectorToString = function (selectors, separator) { + separator = separator || ' '; + return selectors + .reduce((prev, curr) => prev.concat(curr), []) + .join(separator); +}; diff --git a/packages/cli/lib/webdriver.js b/packages/cli/lib/webdriver.js new file mode 100644 index 00000000..6618558b --- /dev/null +++ b/packages/cli/lib/webdriver.js @@ -0,0 +1,49 @@ +const chromedriver = require('chromedriver'); +const { Builder, Capabilities } = require('selenium-webdriver'); +const chrome = require('selenium-webdriver/chrome'); + +function startDriver(config) { + let builder; + const scriptTimeout = (config.timeout || 20) * 1000.0; + + if (config.browser === 'chrome-headless') { + // Tell selenium use the driver in node_modules + const service = new chrome.ServiceBuilder( + config.chromedriverPath || chromedriver.path + ).build(); + chrome.setDefaultService(service); + + const args = ['--headless']; + if (config.chromeOptions) { + args.push(...config.chromeOptions); + } + + const chromeCapabilities = Capabilities.chrome(); + chromeCapabilities.set('chromeOptions', { args }); + + builder = new Builder() + .forBrowser('chrome') + .withCapabilities(chromeCapabilities); + } else { + builder = new Builder().forBrowser(config.browser); + } + + // Launch a browser + config.driver = builder.build(); + config.builder = builder; + + return config.driver + .manage() + .timeouts() + .setScriptTimeout(scriptTimeout) + .then(() => config); +} + +function stopDriver(config) { + config.driver.quit(); +} + +module.exports = { + startDriver, + stopDriver +}; diff --git a/packages/cli/package-lock.json b/packages/cli/package-lock.json new file mode 100644 index 00000000..01cd55f1 --- /dev/null +++ b/packages/cli/package-lock.json @@ -0,0 +1,925 @@ +{ + "name": "@axe-core/cli", + "version": "4.0.0-pre.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@nodelib/fs.scandir": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", + "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "requires": { + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==" + }, + "@nodelib/fs.walk": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", + "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "requires": { + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" + } + }, + "@testim/chrome-version": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@testim/chrome-version/-/chrome-version-1.0.7.tgz", + "integrity": "sha512-8UT/J+xqCYfn3fKtOznAibsHpiuDshCb0fwgWxRazTT19Igp9ovoXMPhXyLD6m3CKQGTMHgqoxaFfMWaL40Rnw==" + }, + "@types/events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", + "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==" + }, + "@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "requires": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" + }, + "@types/node": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.1.tgz", + "integrity": "sha512-FAYBGwC+W6F9+huFIDtn43cpy7+SzG+atzRiTfdp3inUKL2hXnd4rG8hylJLIh4+hqrQy1P17kvJByE/z825hA==" + }, + "@types/yauzl": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", + "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", + "optional": true, + "requires": { + "@types/node": "*" + } + }, + "aggregate-error": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "dependencies": { + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + } + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "axe-core": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-3.2.2.tgz", + "integrity": "sha512-gAy4kMSPpuRJV3mwictJqlg5LhE84Vw2CydKdC4tvrLhR6+G3KW51zbL/vYujcLA2jvWOq3HMHrVeNuw+mrLVA==" + }, + "axe-webdriverjs": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axe-webdriverjs/-/axe-webdriverjs-2.2.0.tgz", + "integrity": "sha512-ckGIk3VaYkpXeESKSYYnPArSajlCVr+18wNFmBvms0H12thS5WaKK8nwtOzeN93SZB+EFn2M19VsGTy07XKxCw==", + "requires": { + "axe-core": "^3.1.2", + "babel-runtime": "^6.26.0", + "depd": "^2.0.0" + } + }, + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "requires": { + "follow-redirects": "1.5.10" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "chromedriver": { + "version": "81.0.0", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-81.0.0.tgz", + "integrity": "sha512-BA++IQ7O1FzHmNpzMlOfLiSBvPZ946uuhtJjZHEIr/Gb+Ha9jiuGbHiT45l6O3XGbQ8BAwvbmdisjl4rTxro4A==", + "requires": { + "@testim/chrome-version": "^1.0.7", + "axios": "^0.19.2", + "del": "^5.1.0", + "extract-zip": "^2.0.0", + "mkdirp": "^1.0.4", + "tcp-port-used": "^1.0.1" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + } + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" + }, + "colors": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", + "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==" + }, + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "core-js": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", + "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, + "del": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/del/-/del-5.1.0.tgz", + "integrity": "sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA==", + "requires": { + "globby": "^10.0.1", + "graceful-fs": "^4.2.2", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.1", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==" + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + } + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "requires": { + "path-type": "^4.0.0" + }, + "dependencies": { + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + } + } + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "^1.4.0" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "extract-zip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.0.tgz", + "integrity": "sha512-i42GQ498yibjdvIhivUsRslx608whtGoFIhF26Z7O4MYncBxp8CwalOs1lnHy21A9sIohWO2+uiE4SRtC9JXDg==", + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "requires": { + "pump": "^3.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "fast-glob": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.2.tgz", + "integrity": "sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "fastq": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", + "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==", + "requires": { + "reusify": "^1.0.4" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "requires": { + "pend": "~1.2.0" + } + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", + "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==" + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + } + } + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==" + }, + "is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" + }, + "is2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is2/-/is2-2.0.1.tgz", + "integrity": "sha512-+WaJvnaA7aJySz2q/8sLjMb2Mw14KTplHmSwcSpZ/fWJPkUmqw3YTzSWbPJ7OAwRvdYTWF2Wg+yYJ1AdP5Z8CA==", + "requires": { + "deep-is": "^0.1.3", + "ip-regex": "^2.1.0", + "is-url": "^1.2.2" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "jszip": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.2.1.tgz", + "integrity": "sha512-iCMBbo4eE5rb1VCpm5qXOAaUiRKRUKiItn8ah2YQQx9qymmSAY98eyQfioChEYcVQLh0zxJ3wS4A0mh90AVPvw==", + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" + } + }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "requires": { + "immediate": "~3.0.5" + } + }, + "merge2": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", + "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.15.1", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.5", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "5.4.0" + }, + "dependencies": { + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node-static": { + "version": "0.7.11", + "resolved": "https://registry.npmjs.org/node-static/-/node-static-0.7.11.tgz", + "integrity": "sha512-zfWC/gICcqb74D9ndyvxZWaI1jzcoHmf4UTHWQchBNuNMxdBLJMDiUgZ1tjGLEIe/BMhj2DxKD8HOuc2062pDQ==", + "dev": true, + "requires": { + "colors": ">=0.6.0", + "mime": "^1.2.9", + "optimist": ">=0.3.4" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "pako": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + } + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "selenium-webdriver": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", + "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", + "requires": { + "jszip": "^3.1.3", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" + } + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "tcp-port-used": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.1.tgz", + "integrity": "sha512-rwi5xJeU6utXoEIiMvVBMc9eJ2/ofzB+7nLOdnZuFTmNCLqRiQh2sMG9MqCxHU/69VC/Fwp5dV9306Qd54ll1Q==", + "requires": { + "debug": "4.1.0", + "is2": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz", + "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "tmp": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", + "requires": { + "os-tmpdir": "~1.0.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/packages/cli/package.json b/packages/cli/package.json new file mode 100644 index 00000000..8bbf210d --- /dev/null +++ b/packages/cli/package.json @@ -0,0 +1,45 @@ +{ + "name": "@axe-core/cli", + "version": "4.0.0-pre.0", + "description": "A CLI for accessibility testing using axe-core", + "main": "index.js", + "bin": { + "axe": "axe-cli" + }, + "engines": { + "node": ">=8" + }, + "scripts": { + "test": "mocha test/*.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/dequelabs/axe-core-npm.git" + }, + "keywords": [ + "axe-core", + "accessibility", + "a11y", + "wcag", + "cli", + "testing" + ], + "author": { + "name": "Wilco Fiers", + "organization": "Deque Systems, Inc.", + "url": "http://github.com/wilcofiers/" + }, + "dependencies": { + "axe-core": "^3.2.2", + "axe-webdriverjs": "^2.2.0", + "chromedriver": "latest", + "colors": "^1.3.3", + "commander": "^2.19.0", + "selenium-webdriver": "^3.6.0" + }, + "devDependencies": { + "chai": "^4.2.0", + "mocha": "^5.1.0", + "node-static": "^0.7.11" + } +} diff --git a/packages/cli/test/axe-test-urls.js b/packages/cli/test/axe-test-urls.js new file mode 100644 index 00000000..d7ded396 --- /dev/null +++ b/packages/cli/test/axe-test-urls.js @@ -0,0 +1,92 @@ +'use strict'; + +const assert = require('chai').assert; +const testPages = require('../lib/axe-test-urls'); + +describe('testPages', function () { + let config, mockDriver; + + beforeEach(() => { + mockDriver = { + get: async arg => arg, + executeAsyncScript: async arg => arg, + executeScript: async arg => arg, + wait: async arg => arg, + switchTo: () => ({ defaultContent: () => {} }), + findElements: async () => [], + quit: async arg => arg + }; + config = { driver: mockDriver }; + }); + + it('return a promise', () => { + assert.instanceOf(testPages([], config, {}), Promise); + }); + + it('calls driver.get() for each URL', async () => { + const urlsCalled = []; + const urls = ['http://foo', 'http://bar', 'http://baz']; + + mockDriver.get = async url => { + urlsCalled.push(url); + return url; + }; + + await testPages(urls, config, {}); + + assert.deepEqual(urlsCalled, urls); + }); + + it('waits until the document is ready to have a className added', async () => { + const asyncScripts = []; + let waitCalls = 0; + + mockDriver.executeAsyncScript = async script => { + asyncScripts.push(script); + return script; + }; + mockDriver.wait = async script => { + waitCalls++; + return script; + }; + + await testPages(['http://foo'], config, {}); + + assert.equal(asyncScripts.length, 2); + const [script] = asyncScripts; + assert.match( + script, + /script\.innerHTML\s*=[\s\S]*['"]document\.documentElement\.classList\.add\(['"]deque-axe-is-ready/ + ); + + assert.equal(waitCalls, 1); + }); + + it('injects axe into the page', async () => { + const scripts = []; + config.axeSource = 'axe="hi, I am axe"'; + mockDriver.executeScript = async script => { + scripts.push(script); + return script; + }; + + await testPages(['http://foo'], config, {}); + assert.include(scripts[0].toString(), config.axeSource); + }); + + it('runs axe once the page is loaded', async () => { + const asyncScripts = []; + mockDriver.executeAsyncScript = async script => { + asyncScripts.push(script); + return script; + }; + + await testPages(['http://foo'], config, {}); + + assert.isDefined( + asyncScripts + .map(script => script.toString()) + .find(script => script.match(/(axe\.run)|(axe\.a11yCheck)/)) + ); + }); +}); diff --git a/packages/cli/test/integrations.js b/packages/cli/test/integrations.js new file mode 100644 index 00000000..8ddd0d65 --- /dev/null +++ b/packages/cli/test/integrations.js @@ -0,0 +1,73 @@ +'use strict'; + +const assert = require('chai').assert; +const chrome = require('selenium-webdriver/chrome'); +const http = require('http'); +const nodeStatic = require('node-static'); +const axeTestUrls = require('../lib/axe-test-urls'); +const { startDriver, stopDriver } = require('../lib/webdriver'); + +describe('integrations', function () { + let program, urls, server; + + before(function () { + // Start a server + const file = new nodeStatic.Server('.'); + server = http.createServer(function (request, response) { + request + .addListener('end', function () { + file.serve(request, response); + }) + .resume(); + }); + server.listen(8182); + }); + + after(function () { + server.close(); + }); + + beforeEach(async function () { + program = { + browser: 'chrome-headless' + }; + await startDriver(program); + urls = ['http://localhost:8182/test/testpage.html']; + }); + + afterEach(async () => { + stopDriver(program); + + const service = chrome.getDefaultService(); + if (service.isRunning()) { + await service.stop(); + + // An unfortunately hacky way to clean up + // the service. Stop will shut it down, + // but it doesn't reset the local state + service.address_ = null; + chrome.setDefaultService(null); + } + }); + + it('finds results in light and shadow DOM', async () => { + let listResult; + await axeTestUrls(urls, program, { + onTestComplete: function (results) { + assert.containsAllKeys(results, [ + 'testEngine', + 'testEnvironment', + 'testRunner' + ]); + listResult = results.violations.find(result => result.id === 'list'); + assert.lengthOf(listResult.nodes, 2); + assert.deepEqual(listResult.nodes[0].target, ['#list']); + assert.deepEqual(listResult.nodes[1].target, [ + ['#shadow-root', '#shadow-list'] + ]); + } + }); + + assert.isDefined(listResult); + }); +}); diff --git a/packages/cli/test/testpage.html b/packages/cli/test/testpage.html new file mode 100644 index 00000000..dfe4b4c0 --- /dev/null +++ b/packages/cli/test/testpage.html @@ -0,0 +1,27 @@ + + + + + List Item Test + + +
+

Page heading

+
    +

    Item One

    +

    Item Two

    +
+ +
+
+ + + diff --git a/packages/cli/test/webdriver.js b/packages/cli/test/webdriver.js new file mode 100644 index 00000000..badb7778 --- /dev/null +++ b/packages/cli/test/webdriver.js @@ -0,0 +1,108 @@ +'use strict'; + +const assert = require('chai').assert; +const { startDriver, stopDriver } = require('../lib/webdriver'); +const chromedriver = require('chromedriver'); +const chrome = require('selenium-webdriver/chrome'); +const path = require('path'); + +describe('startDriver', () => { + let config, browser; + beforeEach(() => { + browser = 'chrome-headless'; + config = { + get browser() { + return browser; + } + }; + }); + + afterEach(async () => { + stopDriver(config); + const service = chrome.getDefaultService(); + if (service.isRunning()) { + await service.stop(); + + // An unfortunately hacky way to clean up + // the service. Stop will shut it down, + // but it doesn't reset the local state + service.address_ = null; + chrome.setDefaultService(null); + } + }); + + it('creates a driver', async () => { + await startDriver(config); + + assert.isObject(config.driver); + assert.isFunction(config.driver.manage); + }); + + xit('sets the config.browser as the browser', done => { + browser = 'chrome'; + startDriver(config) + .then(config => config.driver.getCapabilities()) + .then(capabilities => { + assert.equal(capabilities.get('browserName'), browser); + }) + .then(done, done); + }); + + it('sets the browser as chrome with chrome-headless', async () => { + browser = 'chrome-headless'; + await startDriver(config); + const capabilities = await config.driver.getCapabilities(); + + assert.equal(capabilities.get('browserName'), 'chrome'); + }); + + it('uses the chromedriver path with chrome-headless', async () => { + browser = 'chrome-headless'; + await startDriver(config); + const service = chrome.getDefaultService(); + + assert.equal(service.executable_, chromedriver.path); + }); + + it('uses the passed in chromedriver path with chrome-headless', async () => { + browser = 'chrome-headless'; + config.chromedriverPath = path.relative(process.cwd(), chromedriver.path); + await startDriver(config); + const service = chrome.getDefaultService(); + + assert.notEqual(config.chromedriverPath, chromedriver.path); + assert.equal(service.executable_, config.chromedriverPath); + }); + + it('sets the --headless flag with chrome-headless', async () => { + browser = 'chrome-headless'; + const { builder } = await startDriver(config); + const capabilities = await builder.getCapabilities(); + const chromeOptions = capabilities.get('chromeOptions'); + + assert.isObject(chromeOptions); + assert.deepEqual(chromeOptions.args, ['--headless']); + }); + + it('sets the --chrome-options flag with no-sandbox', async () => { + browser = 'chrome-headless'; + config.chromeOptions = ['--no-sandbox']; + const { builder } = await startDriver(config); + const capabilities = await builder.getCapabilities(); + const chromeOptions = capabilities.get('chromeOptions'); + + assert.isArray(chromeOptions.args); + assert.deepEqual(chromeOptions.args, ['--headless', '--no-sandbox']); + }); +}); + +describe('stopDriver', () => { + it('calls browser.quit', () => { + let called = 0; + stopDriver({ + browser: 'chrome-headless', + driver: { quit: () => called++ } + }); + assert.equal(called, 1); + }); +}); From d6612cbb4ee8df47929ec1bf63fc3bc413581c96 Mon Sep 17 00:00:00 2001 From: Stephen Mathieson Date: Thu, 14 May 2020 11:07:45 -0400 Subject: [PATCH 02/11] add note pointing at old repo --- packages/cli/README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/cli/README.md b/packages/cli/README.md index be04d8a9..9052e078 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -1,12 +1,8 @@ # axe-cli -[![Greenkeeper badge](https://badges.greenkeeper.io/dequelabs/axe-cli.svg)](https://greenkeeper.io/) +Provides a command line interface for [axe](https://github.com/dequelabs/axe-core) to run quick accessibility tests. -[![Join the chat at https://gitter.im/dequelabs/axe-core](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dequelabs/axe-core?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Version](https://img.shields.io/npm/v/axe-cli.svg)](https://www.npmjs.com/package/axe-cli) -[![License](https://img.shields.io/npm/l/axe-cli.svg)](LICENSE) - -Provides a command line interface for [aXe](https://github.com/dequelabs/axe-core) to run quick accessibility tests. +Previous versions of this program were maintained at [dequelabs/axe-cli](https://github.com/dequelabs/axe-cli). ## Getting Started From 35c32009e4915495b8b291658628e7be81ccbebc Mon Sep 17 00:00:00 2001 From: Stephen Mathieson Date: Thu, 14 May 2020 11:08:56 -0400 Subject: [PATCH 03/11] prevent axe-linter from linting generated changelogs --- .github/axe-linter.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .github/axe-linter.yml diff --git a/.github/axe-linter.yml b/.github/axe-linter.yml new file mode 100644 index 00000000..bbc90633 --- /dev/null +++ b/.github/axe-linter.yml @@ -0,0 +1,3 @@ +exclude: + # Prevent axe-linter from linting changelogs, as they're generated files. + - '**/*/CHANGELOG.md' From b77425df27c8408b078d064137133cc68253aedd Mon Sep 17 00:00:00 2001 From: Stephen Mathieson Date: Thu, 14 May 2020 11:10:50 -0400 Subject: [PATCH 04/11] Add cli tests to CI --- .circleci/config.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 58b9dae5..a9db3bd9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -46,6 +46,13 @@ jobs: - restore_dependency_cache - run: npm run lint + cli: + <<: *defaults + steps: + - checkout + - restore_dependency_cache + - run: npm run test --prefix=packages/cli + workflows: version: 2 build_and_test: @@ -54,3 +61,6 @@ workflows: - lint: requires: - dependencies + - cli: + requires: + - lint From 35ff3ea6eb27a8acedc5a11cf90ade4562cc38ef Mon Sep 17 00:00:00 2001 From: Stephen Mathieson Date: Thu, 14 May 2020 11:17:07 -0400 Subject: [PATCH 05/11] add cli deps to cache --- .circleci/config.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a9db3bd9..746536ae 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -27,6 +27,9 @@ jobs: - restore_cache: keys: - v1-root-npm-{{ checksum "package-lock.json" }} + - restore_cache: + keys: + - v1-cli-npm-{{ checksum "packages/cli/package-lock.json" }} - run: npm ci - save_cache: key: v1-root-npm-{{ checksum "package-lock.json" }} @@ -37,8 +40,12 @@ jobs: key: v1-npm-cache-{{ .Environment.CIRCLE_SHA1 }} paths: - node_modules - # TODO: once packages are added, include their `node_modules` too: - # - packages/*/node_modules + - packages/cli/node_modules + - save_cache: + key: v1-cli-npm-{{ checksum "packages/cli/package-lock.json" }} + paths: + - packages/cli/node_modules + lint: <<: *defaults steps: From 07da69da006e21fe6476e2641a8efeea64146673 Mon Sep 17 00:00:00 2001 From: Stephen Mathieson Date: Thu, 14 May 2020 11:30:12 -0400 Subject: [PATCH 06/11] remove old readme --- packages/cli/README.md | 181 ----------------------------------------- 1 file changed, 181 deletions(-) delete mode 100644 packages/cli/README.md diff --git a/packages/cli/README.md b/packages/cli/README.md deleted file mode 100644 index 9052e078..00000000 --- a/packages/cli/README.md +++ /dev/null @@ -1,181 +0,0 @@ -# axe-cli - -Provides a command line interface for [axe](https://github.com/dequelabs/axe-core) to run quick accessibility tests. - -Previous versions of this program were maintained at [dequelabs/axe-cli](https://github.com/dequelabs/axe-cli). - -## Getting Started - -Install [Node.js](https://docs.npmjs.com/getting-started/installing-node) if you haven't already. This project requires Node 6+. By default, axe-cli runs Chrome in headless mode, which requires Chrome 59 or up. - -Install axe-cli globally: `npm install axe-cli -g` - -Lastly, install the webdrivers of the browsers you wish to use. A webdriver is a driver for your web browsers. It allows other programs on your machine to open a browser and operate it. Current information about available webdrivers can be found at [selenium-webdriver project](https://www.npmjs.com/package/selenium-webdriver). Alternatively, you could use [Webdriver manager](https://www.npmjs.com/package/webdriver-manager) - -## Usage - -After installing, you can now run the `axe` command in your CLI, followed by the URL of the page you wish to test: - -``` -axe https://www.deque.com -``` - -You can run multiple pages at once, simply add more URLs to the command. Keep in mind that axe-cli is not a crawler, so if you find yourself testing dozens of pages at once, you may want to consider switching over to something like [axe-webdriverjs](https://www.npmjs.com/package/axe-webdriverjs). If you do not specify the protocol, http will be used by default: - -``` -axe www.deque.com, dequeuniversity.com -``` - -**Note:** If you are having difficulty with the color scheme, use `--no-color` to disable text styles. - -## Running specific rules - -You can use the `--rules` flag to set which rules you wish to run, or you can use `--tags` to tell axe to run all rules that have that specific tag. For example: - -``` -axe www.deque.com --rules color-contrast,html-has-lang -``` - -Or, to run all wcag2a rules: - -``` -axe www.deque.com --tags wcag2a -``` - -In case you want to disable some rules, you can use `--disable` followed by a list of rules. These will be skipped when analyzing the site: - -``` -axe www.deque.com --disable color-contrast -``` - -This option can be combined with either `--tags` or `--rules`. - -A list of rules and what tags they have is available at: https://dequeuniversity.com/rules/worldspace/3.0/. - -## Saving the results - -Results can be saved as JSON data, using the `--save` and `--dir` flags. By passing a filename to `--save` you indicate how the file should be called. If no filename is passed, a default will be used. For example: - -``` -axe www.deque.com --save deque-site.json -``` - -Or: - -``` -axe www.deque.com --dir ./axe-results/ -``` - -## Sending results to STDOUT - -To output the test results to STDOUT, provide the `--stdout` flag. This flag has the side-effect of silencing all other logs/output (other than errors, which are written to STDERR). - -To print the entire result object to your terminal, do: - -``` -axe --stdout www.deque.com -``` - -To pipe the results to a file, do: - -``` -axe --stdout www.deque.com > your_file.json -``` - -To pipe the results to a JSON-parsing program for further processing, do: - -``` -axe --stdout www.deque.com | jq ".[0].violations" -``` - -## Defining the scope of a test - -If you want to only test a specific area of a page, or wish to exclude some part of a page you can do so using the `--include` and `--exclude` flags and pass it a CSS selector: - -``` -axe www.deque.com --include "#main" --exclude "#aside" -``` - -You may pass multiple selectors with a comma-delimited string. For example: - -``` -axe www.deque.com --include "#div1,#div2,#div3" -``` - -## Custom axe-core versions - -Axe-cli will look for locally available versions of axe-core. If the directory from where you start axe-cli has an `axe.js` file, or has a `node_modules` directory with axe-core installed in it. Axe-cli will use this version of axe-core instead of the default version installed globally. - -To specify the exact file axe-core file axe-cli should use, you can use the `--axe-source` flag (`-a` for short), with a relative or absolute path to the file. - -``` -axe www.deque.com --axe-source ./axe.nl.js -``` - -## Different browsers - -Axe-cli can run in a variety of web browsers. By default axe-cli uses Chrome in headless mode. But axe-cli is equally capable of testing pages using other web browsers. **Running in another browser requires that browser's webdriver to be available on your PATH**. You can find a list of available webdrivers and how to install them at: https://seleniumhq.github.io/docs/wd.html - -To run axe-cli using another browser, pass it in as the `--browser` option: - -``` -axe www.deque.com --browser chrome -``` - -Or for short: - -``` -axe www.deque.com -b c -``` - -## Custom Chrome Flags - -When using the Headless Chrome browser, you may provide any number of [flags to configure how the browser functions](https://peter.sh/experiments/chromium-command-line-switches/). - -Options are passed by name, without their leading `--` prefix. For example, to provide the `--no-sandbox --disable-setuid-sandbox --disable-dev-shm-usage` flags to the Chrome binary, you'd do: - -``` -axe --chrome-options="no-sandbox,disable-setuid-sandbox,disable-dev-shm-usage" www.deque.com -``` - -## CI integration - -Axe-cli can be ran within the CI tooling for your project. Many tools are automatically configured to halt/fail builds when a process exits with a code of `1`. - -Use the `--exit` flag, `-q` for short, to have the axe-cli process exit with a failure code `1` when any rule fails to pass. - -``` -axe www.deque.com --exit -``` - -## Timing and timeout - -For debugging and managing timeouts, there are two options available. With `--timer` set, axe-cli will log how long it takes to load the page, and how long it takes to run axe-core. If you find the execution of axe-core takes too long, which can happen on very large pages, use `--timeout` to increase the time axe has to test that page: - -``` -axe www.cnn.com --timeout=120 -``` - -## Delay audit to ensure page is loaded - -If you find your page is not ready after axe has determined it has loaded, you can use `--load-delay` followed by a number in milliseconds. This will make axe wait that time before running the audit after the page has loaded. - -``` -axe www.deque.com --load-delay=2000 -``` - -## Verbose output - -To see additional information like test tool name, version and environment details, use the `--verbose` flag, `-v` for short. - -``` -axe www.deque.com --verbose -``` - -## ChromeDriver Path - -If you need to test your page using an older version of Chrome, you can use `--chromedriver-path` followed by the absolute path to the desired version of the ChromeDriver executable. - -``` -axe www.deque.com --chromedriver-path="absolute/path/to/chromedriver" -``` From f5bb33d01cd8c27c9d3c899502d92693fe8630a6 Mon Sep 17 00:00:00 2001 From: Stephen Mathieson Date: Thu, 14 May 2020 11:31:13 -0400 Subject: [PATCH 07/11] Delete axe-linter.yml --- .github/axe-linter.yml | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .github/axe-linter.yml diff --git a/.github/axe-linter.yml b/.github/axe-linter.yml deleted file mode 100644 index bbc90633..00000000 --- a/.github/axe-linter.yml +++ /dev/null @@ -1,3 +0,0 @@ -exclude: - # Prevent axe-linter from linting changelogs, as they're generated files. - - '**/*/CHANGELOG.md' From 88bfc43fe074c21204f29b0ba95c8eb679162313 Mon Sep 17 00:00:00 2001 From: Stephen Mathieson Date: Thu, 14 May 2020 11:32:15 -0400 Subject: [PATCH 08/11] Revert "remove old readme" This reverts commit 07da69da006e21fe6476e2641a8efeea64146673. --- packages/cli/README.md | 181 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 packages/cli/README.md diff --git a/packages/cli/README.md b/packages/cli/README.md new file mode 100644 index 00000000..9052e078 --- /dev/null +++ b/packages/cli/README.md @@ -0,0 +1,181 @@ +# axe-cli + +Provides a command line interface for [axe](https://github.com/dequelabs/axe-core) to run quick accessibility tests. + +Previous versions of this program were maintained at [dequelabs/axe-cli](https://github.com/dequelabs/axe-cli). + +## Getting Started + +Install [Node.js](https://docs.npmjs.com/getting-started/installing-node) if you haven't already. This project requires Node 6+. By default, axe-cli runs Chrome in headless mode, which requires Chrome 59 or up. + +Install axe-cli globally: `npm install axe-cli -g` + +Lastly, install the webdrivers of the browsers you wish to use. A webdriver is a driver for your web browsers. It allows other programs on your machine to open a browser and operate it. Current information about available webdrivers can be found at [selenium-webdriver project](https://www.npmjs.com/package/selenium-webdriver). Alternatively, you could use [Webdriver manager](https://www.npmjs.com/package/webdriver-manager) + +## Usage + +After installing, you can now run the `axe` command in your CLI, followed by the URL of the page you wish to test: + +``` +axe https://www.deque.com +``` + +You can run multiple pages at once, simply add more URLs to the command. Keep in mind that axe-cli is not a crawler, so if you find yourself testing dozens of pages at once, you may want to consider switching over to something like [axe-webdriverjs](https://www.npmjs.com/package/axe-webdriverjs). If you do not specify the protocol, http will be used by default: + +``` +axe www.deque.com, dequeuniversity.com +``` + +**Note:** If you are having difficulty with the color scheme, use `--no-color` to disable text styles. + +## Running specific rules + +You can use the `--rules` flag to set which rules you wish to run, or you can use `--tags` to tell axe to run all rules that have that specific tag. For example: + +``` +axe www.deque.com --rules color-contrast,html-has-lang +``` + +Or, to run all wcag2a rules: + +``` +axe www.deque.com --tags wcag2a +``` + +In case you want to disable some rules, you can use `--disable` followed by a list of rules. These will be skipped when analyzing the site: + +``` +axe www.deque.com --disable color-contrast +``` + +This option can be combined with either `--tags` or `--rules`. + +A list of rules and what tags they have is available at: https://dequeuniversity.com/rules/worldspace/3.0/. + +## Saving the results + +Results can be saved as JSON data, using the `--save` and `--dir` flags. By passing a filename to `--save` you indicate how the file should be called. If no filename is passed, a default will be used. For example: + +``` +axe www.deque.com --save deque-site.json +``` + +Or: + +``` +axe www.deque.com --dir ./axe-results/ +``` + +## Sending results to STDOUT + +To output the test results to STDOUT, provide the `--stdout` flag. This flag has the side-effect of silencing all other logs/output (other than errors, which are written to STDERR). + +To print the entire result object to your terminal, do: + +``` +axe --stdout www.deque.com +``` + +To pipe the results to a file, do: + +``` +axe --stdout www.deque.com > your_file.json +``` + +To pipe the results to a JSON-parsing program for further processing, do: + +``` +axe --stdout www.deque.com | jq ".[0].violations" +``` + +## Defining the scope of a test + +If you want to only test a specific area of a page, or wish to exclude some part of a page you can do so using the `--include` and `--exclude` flags and pass it a CSS selector: + +``` +axe www.deque.com --include "#main" --exclude "#aside" +``` + +You may pass multiple selectors with a comma-delimited string. For example: + +``` +axe www.deque.com --include "#div1,#div2,#div3" +``` + +## Custom axe-core versions + +Axe-cli will look for locally available versions of axe-core. If the directory from where you start axe-cli has an `axe.js` file, or has a `node_modules` directory with axe-core installed in it. Axe-cli will use this version of axe-core instead of the default version installed globally. + +To specify the exact file axe-core file axe-cli should use, you can use the `--axe-source` flag (`-a` for short), with a relative or absolute path to the file. + +``` +axe www.deque.com --axe-source ./axe.nl.js +``` + +## Different browsers + +Axe-cli can run in a variety of web browsers. By default axe-cli uses Chrome in headless mode. But axe-cli is equally capable of testing pages using other web browsers. **Running in another browser requires that browser's webdriver to be available on your PATH**. You can find a list of available webdrivers and how to install them at: https://seleniumhq.github.io/docs/wd.html + +To run axe-cli using another browser, pass it in as the `--browser` option: + +``` +axe www.deque.com --browser chrome +``` + +Or for short: + +``` +axe www.deque.com -b c +``` + +## Custom Chrome Flags + +When using the Headless Chrome browser, you may provide any number of [flags to configure how the browser functions](https://peter.sh/experiments/chromium-command-line-switches/). + +Options are passed by name, without their leading `--` prefix. For example, to provide the `--no-sandbox --disable-setuid-sandbox --disable-dev-shm-usage` flags to the Chrome binary, you'd do: + +``` +axe --chrome-options="no-sandbox,disable-setuid-sandbox,disable-dev-shm-usage" www.deque.com +``` + +## CI integration + +Axe-cli can be ran within the CI tooling for your project. Many tools are automatically configured to halt/fail builds when a process exits with a code of `1`. + +Use the `--exit` flag, `-q` for short, to have the axe-cli process exit with a failure code `1` when any rule fails to pass. + +``` +axe www.deque.com --exit +``` + +## Timing and timeout + +For debugging and managing timeouts, there are two options available. With `--timer` set, axe-cli will log how long it takes to load the page, and how long it takes to run axe-core. If you find the execution of axe-core takes too long, which can happen on very large pages, use `--timeout` to increase the time axe has to test that page: + +``` +axe www.cnn.com --timeout=120 +``` + +## Delay audit to ensure page is loaded + +If you find your page is not ready after axe has determined it has loaded, you can use `--load-delay` followed by a number in milliseconds. This will make axe wait that time before running the audit after the page has loaded. + +``` +axe www.deque.com --load-delay=2000 +``` + +## Verbose output + +To see additional information like test tool name, version and environment details, use the `--verbose` flag, `-v` for short. + +``` +axe www.deque.com --verbose +``` + +## ChromeDriver Path + +If you need to test your page using an older version of Chrome, you can use `--chromedriver-path` followed by the absolute path to the desired version of the ChromeDriver executable. + +``` +axe www.deque.com --chromedriver-path="absolute/path/to/chromedriver" +``` From 4c65c510112030327c6a03345fc2b34618b9f7af Mon Sep 17 00:00:00 2001 From: Stephen Mathieson Date: Thu, 14 May 2020 11:32:36 -0400 Subject: [PATCH 09/11] remove changelog, not readme --- packages/cli/CHANGELOG.md | 144 -------------------------------------- 1 file changed, 144 deletions(-) delete mode 100644 packages/cli/CHANGELOG.md diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md deleted file mode 100644 index ffa0b400..00000000 --- a/packages/cli/CHANGELOG.md +++ /dev/null @@ -1,144 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. - -## [3.2.0](https://github.com/dequelabs/axe-cli/compare/v3.1.1...v3.2.0) (2019-09-30) - -### Bug Fixes - -- **circleci:** use ([7a8ca66](https://github.com/dequelabs/axe-cli/commit/7a8ca66)) - -### Features - -- **webdriver:** add option ([134b299](https://github.com/dequelabs/axe-cli/commit/134b299)) - -### [3.1.1](https://github.com/dequelabs/axe-cli/compare/v3.1.0...v3.1.1) (2019-08-20) - -### Bug Fixes - -- Update Chromedriver dependency [#103](https://github.com/dequelabs/axe-cli/issues/103) [#104](https://github.com/dequelabs/axe-cli/issues/104) - -## [3.1.0](https://github.com/dequelabs/axe-cli/compare/v3.0.0...v3.1.0) (2019-07-15) - -### Bug Fixes - -- correct scope documentation ([#80](https://github.com/dequelabs/axe-cli/issues/80)) ([81b4312](https://github.com/dequelabs/axe-cli/commit/81b4312)), closes [#75](https://github.com/dequelabs/axe-cli/issues/75) - -### Features - -- add `--chrome-options` flag ([#81](https://github.com/dequelabs/axe-cli/issues/81)) ([6214bcb](https://github.com/dequelabs/axe-cli/commit/6214bcb)), closes [#65](https://github.com/dequelabs/axe-cli/issues/65) -- add `--stdout` flag ([#83](https://github.com/dequelabs/axe-cli/issues/83)) ([06328bf](https://github.com/dequelabs/axe-cli/commit/06328bf)), closes [#15](https://github.com/dequelabs/axe-cli/issues/15) -- add meta data to cli output ([#94](https://github.com/dequelabs/axe-cli/issues/94)) ([7ee59e9](https://github.com/dequelabs/axe-cli/commit/7ee59e9)) - -### Tests - -- make "ready class" test more forgiving ([#74](https://github.com/dequelabs/axe-cli/issues/74)) ([fc2b595](https://github.com/dequelabs/axe-cli/commit/fc2b595)) - - - -# [3.0.0](https://github.com/dequelabs/axe-cli/compare/v2.1.0-alpha.1...v3.0.0) (2018-03-28) - -### Features - -- Update to [axe-core 3.0.0](https://github.com/dequelabs/axe-core/releases/tag/v3.0.0) -- Add --load-delay option to delay audit after page loads ([#53](https://github.com/dequelabs/axe-cli/issues/53)) ([c0659a8](https://github.com/dequelabs/axe-cli/commit/c0659a8)) -- Upgrade chromedriver to support Chrome 65 ([e4d4bd1](https://github.com/dequelabs/axe-cli/commit/e4d4bd1)) - - - -# [2.1.0-alpha.1](https://github.com/dequelabs/axe-cli/compare/v2.1.0-alpha.0...v2.1.0-alpha.1) (2018-02-21) - -### Features - -- Support aXe-core 3.0 Shadow DOM selectors ([#49](https://github.com/dequelabs/axe-cli/issues/49)) ([790b421](https://github.com/dequelabs/axe-cli/commit/790b421)) - - - -# [2.1.0-alpha.0](https://github.com/dequelabs/axe-cli/compare/v2.0.0...v2.1.0-alpha.0) (2018-02-20) - -### Bug Fixes - -- Security vulnerability in hoek package ([#50](https://github.com/dequelabs/axe-cli/issues/50)) ([81695ad](https://github.com/dequelabs/axe-cli/commit/81695ad)) - -### Features - -- Upgrade axe-core to 3.0.0-beta.1 -- Upgrade axe-webdriverjs to 2.0.0-alpha.1 - - - -## [2.0.0](https://github.com/dequelabs/axe-cli/compare/v1.3.1...v2.0.0) (2017-12-19) - -### Features - -- Use chrome-headless as default browser replacing PhantomJS ([1ae8e12](https://github.com/dequelabs/axe-cli/commit/1ae8e12)) - -### BREAKING CHANGES - -- PhantomJS is no longer maintained. We will be - replacing it with headless Chrome - - - -## [1.3.1](https://github.com/dequelabs/axe-cli/compare/v1.3.0...v1.3.1) (2017-12-19) - -### Features - -- Add axe-core 2.6.0 - - - -# [1.3.0](https://github.com/dequelabs/axe-cli/compare/v1.1.1...v1.3.0) (2017-11-17) - -### Bug Fixes - -- package.json & .snyk to reduce vulnerabilities ([#39](https://github.com/dequelabs/axe-cli/issues/39)) ([9b20eef](https://github.com/dequelabs/axe-cli/commit/9b20eef)) - -### Features - -- Add flag that enables supplying a list of rules to be skipped during the analysis ([d22903d](https://github.com/dequelabs/axe-cli/commit/d22903d)) -- Allow running from file:// and ftp(s):// ([#41](https://github.com/dequelabs/axe-cli/issues/41)) ([aa3d937](https://github.com/dequelabs/axe-cli/commit/aa3d937)) -- Link to DeqeuU courses/testingmethods ([#38](https://github.com/dequelabs/axe-cli/issues/38)) ([8c0e661](https://github.com/dequelabs/axe-cli/commit/8c0e661)) - - - -# [1.2.0](https://github.com/dequelabs/axe-cli/compare/1.0.2...1.2.0) (2017-10-31) - -### Features - -- Allow running from file:// and ftp(s):// ([#41](https://github.com/dequelabs/axe-cli/issues/41)) ([aa3d937](https://github.com/dequelabs/axe-cli/commit/aa3d937)) -- Link to DeqeuU courses/testingmethods ([#38](https://github.com/dequelabs/axe-cli/issues/38)) ([8c0e661](https://github.com/dequelabs/axe-cli/commit/8c0e661)) -- support exit codes ([e14e2d5](https://github.com/dequelabs/axe-cli/commit/e14e2d5)), closes [#20](https://github.com/dequelabs/axe-cli/issues/20) [#22](https://github.com/dequelabs/axe-cli/issues/22) - - - -## [1.1.1](https://github.com/dequelabs/axe-cli/compare/1.0.3...1.1.1) (2017-09-20) - -### New Features - -- feat: Add --timeout and --timer options ([6d4d14f](https://github.com/dequelabs/axe-cli/commit/6d4d14f80e63bef2d54b3704a818a8ca8b1bb0e3)) -- chore: upgrade axe-core to 2.4.1, axe-webdriverjs to 1.1.5 ([933f1fd](https://github.com/dequelabs/axe-cli/commit/933f1fdb60b06c6fbbcf6d77763dd334d4df8d73)) - -### Bug Fixes - -- doc: Changed non-working promo url for courses to use a working url ([ca7361e](https://github.com/dequelabs/axe-cli/commit/ca7361e653ccb8f3a0138d0dc5f800ff09136351)) - - - -## [1.0.3](https://github.com/dequelabs/axe-cli/compare/1.0.2...1.0.3) (2017-07-05) - -### New Features - -- chore: update axe/webdriverjs to 2.3.1 ([c16bc2f](https://github.com/dequelabs/axe-cli/commit/c16bc2f48f60fbdc556c983db396794cad083a71)) -- feat: support exit codes ([e14e2d5](https://github.com/dequelabs/axe-cli/commit/e14e2d503fc52e6ca38378dd865f8948ed1f9d88)) - - - -## [1.0.2](https://github.com/dequelabs/axe-cli/compare/043d0a4...1.0.2) (2017-05-06) - -### Bug Fixes - -- add correct Selenium server URL ([043d0a4](https://github.com/dequelabs/axe-cli/commit/043d0a4)) -- add node version restriction ([#14](https://github.com/dequelabs/axe-cli/issues/14)) ([b9ff463](https://github.com/dequelabs/axe-cli/commit/b9ff463)) -- handle phantomjs and selenium without errors ([afedd67](https://github.com/dequelabs/axe-cli/commit/afedd67)) -- remove extraneous driver kill ([870f6de](https://github.com/dequelabs/axe-cli/commit/870f6de)) From 337963d709736a35a96ecd98d3273da2e7c66901 Mon Sep 17 00:00:00 2001 From: Stephen Mathieson Date: Thu, 14 May 2020 11:33:35 -0400 Subject: [PATCH 10/11] stuff --- packages/cli/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/README.md b/packages/cli/README.md index 9052e078..49c8d6c8 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -1,4 +1,4 @@ -# axe-cli +# @axe-core/cli Provides a command line interface for [axe](https://github.com/dequelabs/axe-core) to run quick accessibility tests. @@ -8,7 +8,7 @@ Previous versions of this program were maintained at [dequelabs/axe-cli](https:/ Install [Node.js](https://docs.npmjs.com/getting-started/installing-node) if you haven't already. This project requires Node 6+. By default, axe-cli runs Chrome in headless mode, which requires Chrome 59 or up. -Install axe-cli globally: `npm install axe-cli -g` +Install axe CLI globally: `npm install @axe-devtools/cli -g` Lastly, install the webdrivers of the browsers you wish to use. A webdriver is a driver for your web browsers. It allows other programs on your machine to open a browser and operate it. Current information about available webdrivers can be found at [selenium-webdriver project](https://www.npmjs.com/package/selenium-webdriver). Alternatively, you could use [Webdriver manager](https://www.npmjs.com/package/webdriver-manager) From 8122faab29b297c896cff1f2502212f7a76cb500 Mon Sep 17 00:00:00 2001 From: Stephen Mathieson Date: Thu, 14 May 2020 14:39:45 -0400 Subject: [PATCH 11/11] Update packages/cli/README.md Co-authored-by: Michael <45568605+michael-siek@users.noreply.github.com> --- packages/cli/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/README.md b/packages/cli/README.md index 49c8d6c8..109a0d55 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -8,7 +8,7 @@ Previous versions of this program were maintained at [dequelabs/axe-cli](https:/ Install [Node.js](https://docs.npmjs.com/getting-started/installing-node) if you haven't already. This project requires Node 6+. By default, axe-cli runs Chrome in headless mode, which requires Chrome 59 or up. -Install axe CLI globally: `npm install @axe-devtools/cli -g` +Install axe CLI globally: `npm install @axe-core/cli -g` Lastly, install the webdrivers of the browsers you wish to use. A webdriver is a driver for your web browsers. It allows other programs on your machine to open a browser and operate it. Current information about available webdrivers can be found at [selenium-webdriver project](https://www.npmjs.com/package/selenium-webdriver). Alternatively, you could use [Webdriver manager](https://www.npmjs.com/package/webdriver-manager)