diff --git a/.babelrc b/.babelrc index f91ebace9b..9ef4d1948b 100644 --- a/.babelrc +++ b/.babelrc @@ -1,8 +1,15 @@ { "presets": [ - "env" + [ + "@babel/preset-env" + ] ], "plugins": [ - "transform-object-rest-spread" + [ + "@babel/plugin-proposal-object-rest-spread", + { + "loose": true + } + ] ] -} +} \ No newline at end of file diff --git a/.circleci/config.yml b/.circleci/config.yml index 89f251332e..56b4978cbf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -57,6 +57,14 @@ jobs: - phantomjs - run: npm run test + # Run examples under `doc/examples` + test_examples: + <<: *defaults + steps: + - checkout + - <<: *restore_dependency_cache + - run: npm run test:examples + # Release a "next" version next_release: <<: *defaults @@ -68,7 +76,7 @@ jobs: - run: npm publish --tag=next # Release a "production" version - production_release: + release: <<: *defaults steps: - checkout @@ -77,6 +85,20 @@ jobs: - run: npm run build - run: npm publish + # Create a GitHub release. + github_release: + docker: + - image: circleci/golang:1.8 + steps: + - checkout + - run: go get gopkg.in/aktau/github-release.v0 + - run: + name: Download and run GitHub release script + command: | + curl https://raw.githubusercontent.com/dequelabs/attest-release-scripts/develop/src/node-github-release.sh -s -o ./node-github-release.sh + chmod +x ./node-github-release.sh + ./node-github-release.sh + workflows: version: 2 build: @@ -92,11 +114,15 @@ workflows: requires: - dependencies - lint + - test_examples: + requires: + - test # Hold for approval - hold: type: approval requires: - test + - test_examples filters: # We only want to hold on these two branches, as PR review acts as approval for PRs branches: @@ -108,16 +134,20 @@ workflows: requires: - dependencies - test - - hold + - test_examples filters: branches: only: develop # Run a production release on "master" commits, but only after the tests pass and dependencies are installed - - production_release: + - release: requires: - dependencies - test + - test_examples - hold filters: branches: only: master + - github_release: + requires: + - release diff --git a/.eslintrc b/.eslintrc index 93cde8712f..1f18745554 100644 --- a/.eslintrc +++ b/.eslintrc @@ -42,14 +42,6 @@ 2, 5 ], - "max-statements": [ - 2, - 15 - ], - "complexity": [ - 1, - 12 - ], "no-cond-assign": 0, "no-debugger": 0, "no-eq-null": 0, @@ -70,6 +62,12 @@ { "selector": "MemberExpression[property.name=tagName]", "message": "Don't use node.tagName, use node.nodeName instead." + }, + { + // node.attributes can be clobbered so is unsafe to use + // @see https://github.com/dequelabs/axe-core/pull/1432 + "selector": "MemberExpression[object.name=node][property.name=attributes]", + "message": "Don't use node.attributes, use node.hasAttributes() or axe.utils.getNodeAttributes(node) instead." } ] } diff --git a/.prettierignore b/.prettierignore index c2658d7d1b..5ae2c1f2b3 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,2 @@ node_modules/ +doc/api \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 63587ce5a5..b398f23ef2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -707,7 +707,7 @@ All notable changes to this project will be documented in this file. See [standa - Security improvements - Build includes Babel/ES6 - Improvements to table rules -- aXe can be loaded in Node +- axe can be loaded in Node diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9e17b4c1b8..b2090d3770 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -110,13 +110,13 @@ To build the package: grunt build ``` -## Using aXe with TypeScript +## Using axe with TypeScript -### aXe Development +### Axe Development The TypeScript definition file for axe-core is distributed with this module and can be found in [axe.d.ts](./axe.d.ts). It currently supports TypeScript 2.0+. -To maintain aXe support for TypeScript you must first install it (globally recommended): +To maintain axe support for TypeScript you must first install it (globally recommended): ```console sudo npm -g install typescript @@ -128,9 +128,9 @@ Once that's installed, you can run TypeScript definition tests (with the optiona tsc --noImplicitAny typings/axe-core/axe-core-tests.ts ``` -## Including aXe's type definition in tests +## Including axe's type definition in tests -Installing aXe to run accessibility tests in your TypeScript project should be as simple as importing the module: +Installing axe to run accessibility tests in your TypeScript project should be as simple as importing the module: ```js import * as axe from 'axe-core'; diff --git a/Gruntfile.js b/Gruntfile.js index 9c11dd6ea7..f6df5e8266 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,6 +1,4 @@ /*eslint -complexity: ["error",12], -max-statements: ["error", 35], camelcase: ["error", {"properties": "never"}] */ var testConfig = require('./build/test/config'); @@ -73,7 +71,7 @@ module.exports = function(grunt) { clean: ['dist', 'tmp', 'axe.js', 'axe.*.js'], babel: { options: { - compact: 'false' + compact: false }, core: { files: [ @@ -209,7 +207,7 @@ module.exports = function(grunt) { quote_style: 1 }, output: { - comments: /^\/*! aXe/ + comments: /^\/*! axe/ } } }, @@ -222,7 +220,7 @@ module.exports = function(grunt) { }), options: { output: { - comments: /^\/*! aXe/ + comments: /^\/*! axe/ }, mangle: { reserved: ['commons', 'utils', 'axe', 'window', 'document'] @@ -243,7 +241,18 @@ module.exports = function(grunt) { }, testconfig: { test: { - src: ['test/integration/rules/**/*.json'], + src: ['test/integration/rules/**/*.json'].concat( + process.env.APPVEYOR + ? [ + // These tests are causing PhantomJS to timeout on Appveyor + // Warning: PhantomJS timed out, possibly due to a missing Mocha run() call. Use --force to continue. + '!test/integration/rules/td-has-header/*.json', + '!test/integration/rules/label-content-name-mismatch/*.json', + '!test/integration/rules/label/*.json', + '!test/integration/rules/th-has-data-cells/*.json' + ] + : [] + ), dest: 'tmp/integration-tests.js' } }, @@ -255,7 +264,7 @@ module.exports = function(grunt) { fixture: 'test/runner.tmpl', testCwd: 'test/core', data: { - title: 'aXe Core Tests' + title: 'Axe Core Tests' } } }, @@ -270,7 +279,7 @@ module.exports = function(grunt) { fixture: 'test/runner.tmpl', testCwd: 'test/checks', data: { - title: 'aXe Check Tests' + title: 'Axe Check Tests' } } }, @@ -285,7 +294,7 @@ module.exports = function(grunt) { fixture: 'test/runner.tmpl', testCwd: 'test/commons', data: { - title: 'aXe Commons Tests' + title: 'Axe Commons Tests' } } }, @@ -300,7 +309,7 @@ module.exports = function(grunt) { fixture: 'test/runner.tmpl', testCwd: 'test/rule-matches', data: { - title: 'aXe Rule Matches Tests' + title: 'Axe Rule Matches Tests' } } }, @@ -312,7 +321,7 @@ module.exports = function(grunt) { testCwd: 'test/integration/rules', tests: ['../../../tmp/integration-tests.js', 'runner.js'], data: { - title: 'aXe Integration Tests' + title: 'Axe Integration Tests' } } } diff --git a/README.md b/README.md index 52828470ec..db80d5cd9f 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,8 @@ Support means that we will fix bugs and attempt to test each browser regularly. There is limited support for JSDOM. We will attempt to make all rules compatible with JSDOM but where this is not possible, we recommend turning those rules off. Currently the `color-contrast` rule is known not to work with JSDOM. +We can only support environments where features are either natively supported or polyfilled correctly. + ## The Accessibility Rules The complete list of rules run by axe-core can be found in [doc/rule-descriptions.md](./doc/rule-descriptions.md). diff --git a/appveyor.yml b/appveyor.yml index 5cf1f0ebc9..e56047f7f6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,21 +1,24 @@ -# Test against the latest version of this Node.js version +version: 1.0.{build}-{branch} + + # Test against this version of node. environment: - nodejs_version: "6" + nodejs_version: "10" +skip_tags: true -# Install scripts. (runs after repo cloning) + # Install scripts. (runs after repo cloning) install: - # Get the latest stable version of Node.js or io.js - ps: Install-Product node $env:nodejs_version - # install modules - npm install -# Post-install test scripts. + # Post-install test scripts. test_script: - # Output useful info for debugging. - node --version - npm --version - # run tests - - npm test + - npm run build + # A few tests are flaky, causing occasional random failures + # When we have time, we should investigate to see if we can eliminate these flakes + # - npm run test + - npm run test:examples -# Don't actually build. -build: off + # Don't actually build. +build: off \ No newline at end of file diff --git a/axe.d.ts b/axe.d.ts index 3f615c56c7..08edbe323c 100644 --- a/axe.d.ts +++ b/axe.d.ts @@ -1,4 +1,4 @@ -// Type definitions for axe-core 3.0.2 +// Type definitions for axe-core // Project: https://github.com/dequelabs/axe-core // Definitions by: Marcy Sutton @@ -65,13 +65,13 @@ declare namespace axe { help: string; helpUrl: string; id: string; - impact: ImpactValue; + impact?: ImpactValue; tags: TagValue[]; nodes: NodeResult[]; } interface NodeResult { html: string; - impact: ImpactValue; + impact?: ImpactValue; target: string[]; any: CheckResult[]; all: CheckResult[]; @@ -155,7 +155,7 @@ declare namespace axe { let source: string; /** - * Object for aXe Results + * Object for axe Results */ var AxeResults: AxeResults; @@ -165,7 +165,7 @@ declare namespace axe { * @param {ElementContext} context Optional The `Context` specification object @see Context * @param {RunOptions} options Optional Options passed into rules or checks, temporarily modifying them. * @param {RunCallback} callback Optional The function to invoke when analysis is complete. - * @returns {Promise|void} If the callback was not defined, aXe will return a Promise. + * @returns {Promise|void} If the callback was not defined, axe will return a Promise. */ function run(context?: ElementContext): Promise; function run(options: RunOptions): Promise; @@ -183,7 +183,7 @@ declare namespace axe { ): void; /** - * Method for configuring the data format used by aXe. Helpful for adding new + * Method for configuring the data format used by axe. Helpful for adding new * rules, which must be registered with the library to execute. * @param {Spec} Spec Object with valid `branding`, `reporter`, `checks` and `rules` data */ diff --git a/build/configure.js b/build/configure.js index f4d2c0e417..be6438c971 100644 --- a/build/configure.js +++ b/build/configure.js @@ -1,5 +1,5 @@ /*eslint-env node */ -/*eslint max-statements: ["error", 20], max-len: off */ +/*eslint max-len: off */ 'use strict'; var clone = require('clone'); diff --git a/build/tasks/langs.js b/build/tasks/langs.js index 32b55980bb..e4b2890723 100644 --- a/build/tasks/langs.js +++ b/build/tasks/langs.js @@ -1,7 +1,6 @@ /*eslint-env node */ 'use strict'; var http = require('http'); -var Promise = require('promise'); module.exports = function(grunt) { function getLine(data, start) { var len = data.length; diff --git a/build/tasks/test-webdriver.js b/build/tasks/test-webdriver.js index fb15fde36e..ccc009b300 100644 --- a/build/tasks/test-webdriver.js +++ b/build/tasks/test-webdriver.js @@ -1,10 +1,6 @@ /*global window */ -/*eslint -max-statements: ["error", 20], -*/ 'use strict'; -var Promise = require('promise'); var WebDriver = require('selenium-webdriver'); module.exports = function(grunt) { @@ -45,16 +41,20 @@ module.exports = function(grunt) { .get(url) // Get results .then(function() { - return collectTestResults(driver); + let driverBrowser = driver + .getCapabilities() + .then(capabilities => capabilities.get('browserName')); + return Promise.all([driverBrowser, collectTestResults(driver)]); }) // And process them - .then(function(result) { - grunt.log.writeln(url); + .then(function([browser, result]) { + grunt.log.writeln(url + ' [' + browser + ']'); // Remember the errors (result.reports || []).forEach(function(err) { grunt.log.error(err.message); err.url = url; + err.browser = browser; errors.push(err); }); @@ -189,6 +189,7 @@ module.exports = function(grunt) { testErrors.forEach(function(err) { grunt.log.writeln(); grunt.log.error('URL: ' + err.url); + grunt.log.error('Browser: ' + err.browser); grunt.log.error('Describe: ' + err.titles.join(' > ')); grunt.log.error('it ' + err.name); grunt.log.error(err.stack); diff --git a/build/test/config.js b/build/test/config.js index 9d36b805e8..1bd8e7d893 100644 --- a/build/test/config.js +++ b/build/test/config.js @@ -42,7 +42,12 @@ exports = module.exports = function(grunt, options) { [ 'test/integration/full/**/*.html', '!test/integration/full/**/frames/**/*.html' - ], + ].concat([ + // These tests can be flaky on AppVeyor in Chrome and frequently fail + process.env.APPVEYOR + ? ['!test/integration/full/preload-cssom/preload-cssom.html'] + : [] + ]), '<%= connect.test.options.port %>' ), run: true, diff --git a/doc/API.md b/doc/API.md index 543fa7041d..9c292f754a 100644 --- a/doc/API.md +++ b/doc/API.md @@ -1,4 +1,4 @@ -# aXe Javascript Accessibility API +# Axe Javascript Accessibility API ## Table of Contents @@ -28,7 +28,7 @@ ## Section 1: Introduction -The aXe API is designed to be an improvement over the previous generation of accessibility APIs. It provides the following benefits: +The axe API is designed to be an improvement over the previous generation of accessibility APIs. It provides the following benefits: - Runs in any modern browser - Designed to work with existing testing infrastructure @@ -39,9 +39,9 @@ The aXe API is designed to be an improvement over the previous generation of acc ### Getting Started -This section gives a quick description of how to use the aXe APIs to analyze web page content and return a JSON object that lists any accessibility violations found. +This section gives a quick description of how to use the axe APIs to analyze web page content and return a JSON object that lists any accessibility violations found. -The aXe API can be used as part of a broader process that is performed on many, if not all, pages of a website. The API is used to analyze web page content and return a JSON object that lists any accessibility violations found. Here is how to get started: +The axe API can be used as part of a broader process that is performed on many, if not all, pages of a website. The API is used to analyze web page content and return a JSON object that lists any accessibility violations found. Here is how to get started: 1. Load page in testing system 2. Optionally, set configuration options for the javascript API (`axe.configure`) @@ -53,17 +53,17 @@ The aXe API can be used as part of a broader process that is performed on many, ### Overview -The aXe APIs are provided in the javascript file axe.js. It must be included in the web page under test. Parameters are sent as javascript function parameters. Results are returned in JSON format. +The axe APIs are provided in the javascript file axe.js. It must be included in the web page under test. Parameters are sent as javascript function parameters. Results are returned in JSON format. ### Full API Reference for Developers -For a full listing of API offered by aXe, clone the repository and run `npm run api-docs`. This generates `jsdoc` documentation under `doc/api` which can be viewed using the browser. +For a full listing of API offered by axe, clone the repository and run `npm run api-docs`. This generates `jsdoc` documentation under `doc/api` which can be viewed using the browser. ### API Notes - A Rule test is made up of sub-tests. Each sub-test is returned in an array of 'checks' - The `"helpUrl"` in the results object is a link to a broader description of the accessibility issue and suggested remediation. These links point to Deque University help pages, which do not require a login. -- aXe does not test hidden regions, such as inactive menus or modal windows. To test those for accessibility, write tests that activate or render the regions visible and run the analysis again. +- Axe does not test hidden regions, such as inactive menus or modal windows. To test those for accessibility, write tests that activate or render the regions visible and run the analysis again. ### API Name: axe.getRules @@ -134,7 +134,7 @@ In this example, we pass in the WCAG 2 A and AA tags into `axe.getRules` to retr #### Purpose -To configure the format of the data used by aXe. This can be used to add new rules, which must be registered with the library to execute. +To configure the format of the data used by axe. This can be used to add new rules, which must be registered with the library to execute. #### Description @@ -176,7 +176,7 @@ axe.configure({ - The rules attribute is an Array of rule objects - each rule object can contain the following attributes - `id` - string(required). This uniquely identifies the rule. If the rule already exists, it will be overridden with any of the attributes supplied. The attributes below that are marked required, are only required for new rules. - - `selector` - string(optional, default `*`). A CSS selector used to identify the elements that are passed into the rule for evaluation. + - `selector` - string(optional, default `*`). A [CSS selector](./developer-guide.md#supported-css-selectors) used to identify the elements that are passed into the rule for evaluation. - `excludeHidden` - boolean(optional, default `true`). This indicates whether elements that are hidden from all users are to be passed into the rule for evaluation. - `enabled` - boolean(optional, default `true`). Whether the rule is turned on. This is a common attribute for overriding. - `pageLevel` - boolean(optional, default `false`). When set to true, this rule is only applied when the entire page is tested. Results from nodes on different frames are combined into a single result. See [page level rules](#page-level-rules). @@ -184,7 +184,7 @@ axe.configure({ - `all` - array(optional, default `[]`). This is a list of checks that, if any "fails", will generate a violation. - `none` - array(optional, default `[]`). This is a list of checks that, if any "pass", will generate a violation. - `tags` - array(optional, default `[]`). A list if the tags that "classify" the rule. In practice, you must supply some valid tags or the default evaluation will not invoke the rule. The convention is to include the standard (WCAG 2 and/or section 508), the WCAG 2 level, Section 508 paragraph, and the WCAG 2 success criteria. Tags are constructed by converting all letters to lower case, removing spaces and periods and concatinating the result. E.g. WCAG 2 A success criteria 1.1.1 would become ["wcag2a", "wcag111"] - - `matches` - string(optional, default `*`). A filtering CSS selector that will exclude elements that do not match the CSS selector. + - `matches` - string(optional, default `*`). A filtering [CSS selector](./developer-guide.md#supported-css-selectors) that will exclude elements that do not match the CSS selector. - `disableOtherRules` - Disables all rules not included in the `rules` property. - `locale` - A locale object to apply (at runtime) to all rules and checks, in the same shape as `/locales/*.json`. @@ -251,11 +251,7 @@ By default, `axe.run` will test the entire document. The context object is an op - Example: To limit analysis to the `
` element: `document.getElementById("content")` 2. A NodeList such as returned by `document.querySelectorAll`. -3. A CSS selector that selects the portion(s) of the document that must be analyzed. This includes: - -- A CSS selector as a class name (e.g. `.classname`) -- A CSS selector as a node name (e.g. `div`) -- A CSS selector of an element id (e.g. `#tag`) +3. A [CSS selector](./developer-guide.md#supported-css-selectors) that selects the portion(s) of the document that must be analyzed. 4. An include-exclude object (see below) @@ -264,7 +260,7 @@ By default, `axe.run` will test the entire document. The context object is an op The include exclude object is a JSON object with two attributes: include and exclude. Either include or exclude is required. If only `exclude` is specified; include will default to the entire `document`. - A node, or -- An array of arrays of CSS selectors +- An array of arrays of [CSS selectors](./developer-guide.md#supported-css-selectors) - If the nested array contains a single string, that string is the CSS selector - If the nested array contains multiple strings - The last string is the final CSS selector @@ -338,19 +334,20 @@ The options parameter is flexible way to configure how `axe.run` operates. The d Additionally, there are a number or properties that allow configuration of different options: -| Property | Default | Description | -| --------------- | :------ | :-------------------------------------------------------------------------------------------------------------------------------------- | -| `runOnly` | n/a | Limit which rules are executed, based on names or tags | -| `rules` | n/a | Allow customizing a rule's properties (including { enable: false }) | -| `reporter` | `v1` | Which reporter to use (see [Configuration](#api-name-axeconfigure)) | -| `resultTypes` | n/a | Limit which result types are processed and aggregated | -| `xpath` | `false` | Return xpath selectors for elements | -| `absolutePaths` | `false` | Use absolute paths when creating element selectors | -| `iframes` | `true` | Tell axe to run inside iframes | -| `elementRef` | `false` | Return element references in addition to the target | -| `restoreScroll` | `false` | Scrolls elements back to before axe started | -| `frameWaitTime` | `60000` | How long (in milliseconds) axe waits for a response from embedded frames before timing out | -| `preload` | `false` | Any additional assets (eg: cssom) to preload before running rules. [See here for configuration details](#preload-configuration-details) | +| Property | Default | Description | +| ------------------ | :------ | :-------------------------------------------------------------------------------------------------------------------------------------- | +| `runOnly` | n/a | Limit which rules are executed, based on names or tags | +| `rules` | n/a | Allow customizing a rule's properties (including { enable: false }) | +| `reporter` | `v1` | Which reporter to use (see [Configuration](#api-name-axeconfigure)) | +| `resultTypes` | n/a | Limit which result types are processed and aggregated | +| `xpath` | `false` | Return xpath selectors for elements | +| `absolutePaths` | `false` | Use absolute paths when creating element selectors | +| `iframes` | `true` | Tell axe to run inside iframes | +| `elementRef` | `false` | Return element references in addition to the target | +| `restoreScroll` | `false` | Scrolls elements back to before axe started | +| `frameWaitTime` | `60000` | How long (in milliseconds) axe waits for a response from embedded frames before timing out | +| `preload` | `false` | Any additional assets (eg: cssom) to preload before running rules. [See here for configuration details](#preload-configuration-details) | +| `performanceTimer` | `false` | Log rule performance metrics to the console | ###### Options Parameter Examples @@ -464,7 +461,7 @@ This example first includes all `wcag2a` and `wcag2aa` rules. All rules that are 6. Only process certain types of results -The `resultTypes` option can be used to limit the result types that aXe will process, aggregate, and send to the reporter. This can be useful for improving performance on very large or complicated pages when you are only interested in certain types of results. +The `resultTypes` option can be used to limit the result types that axe will process, aggregate, and send to the reporter. This can be useful for improving performance on very large or complicated pages when you are only interested in certain types of results. Types listed in this option are processed normally and report all of their results. Types _not_ listed process a maximum of one result. The caller can use this information to inform the user of the existence of that type of result if appropriate. @@ -502,11 +499,11 @@ The `timeout` attribute in the object configuration is `optional` and has a fall ##### Callback Parameter -The callback parameter is a function that will be called when the asynchronous `axe.run` function completes. The callback function is passed two parameters. The first parameter will be an error thrown inside of aXe if axe.run could not complete. If axe completed correctly the first parameter will be null, and the second parameter will be the results object. +The callback parameter is a function that will be called when the asynchronous `axe.run` function completes. The callback function is passed two parameters. The first parameter will be an error thrown inside of axe if axe.run could not complete. If axe completed correctly the first parameter will be null, and the second parameter will be the results object. #### Return Promise -If the callback was not defined, aXe will return a Promise instead. Axe does not polyfill a Promise library however. So on systems without support for Promises this feature is not available. If you are unsure if the systems you will need aXe on has Promise support we suggest you use the callback provided by axe.run instead. +If the callback was not defined, axe will return a Promise instead. Axe does not polyfill a Promise library however. So on systems without support for Promises this feature is not available. If you are unsure if the systems you will need axe on has Promise support we suggest you use the callback provided by axe.run instead. #### Error Result @@ -663,7 +660,7 @@ As you can see the `target` array contains one item that is an array. This array ### API Name: axe.registerPlugin -Register a plugin with the aXe plugin system. See [implementing a plugin](plugins.md) for more information on the plugin system +Register a plugin with the axe plugin system. See [implementing a plugin](plugins.md) for more information on the plugin system ### API Name: axe.cleanup @@ -702,7 +699,7 @@ axe.utils.querySelectorAll(virtualNode, 'a[href]'); ##### Parameters - `virtualNode` – object, the flattened DOM tree to query against. `axe._tree` is available for this purpose during an audit; see below. -- `selector` – string, the CSS selector to use as a filter. For the most part, this should work seamlessly with `document.querySelectorAll`. +- `selector` – string, the [CSS selector](./developer-guide.md#supported-css-selectors) to use as a filter. For the most part, this should work seamlessly with `document.querySelectorAll`. ##### Returns diff --git a/doc/code-submission-guidelines.md b/doc/code-submission-guidelines.md index bea68d9a4e..8219620cc6 100644 --- a/doc/code-submission-guidelines.md +++ b/doc/code-submission-guidelines.md @@ -13,7 +13,7 @@ will kindly ask you to resubmit it in the correct format. We follow Angular's code contribution style with precise rules for formatting git commit messages. This leads to more readable messages that are easy to follow when looking through the project -history. We will also use commit messages to generate the aXe Changelog document. +history. We will also use commit messages to generate the axe Changelog document. A detailed explanation of Angular's guidelines and conventions can be found [on Google Docs](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#). diff --git a/doc/developer-guide.md b/doc/developer-guide.md index bbc0686f28..8fbc22ffea 100644 --- a/doc/developer-guide.md +++ b/doc/developer-guide.md @@ -1,10 +1,15 @@ -# aXe Developer Guide +# Axe Developer Guide -aXe runs a series of tests to check for accessibility of content and functionality on a website. A test is made up of a series of Rules which are, themselves, made up of Checks. aXe executes these Rules asynchronously and, when the Rules are finished running, runs a callback function which is passed a Result structure. Since some Rules run on the page level while others do not, tests will also run in one of two ways. If a document is specified, the page level rules will run, otherwise they will not. +Axe runs a series of tests to check for accessibility of content and functionality on a website. A test is made up of a series of Rules which are, themselves, made up of Checks. Axe executes these Rules asynchronously and, when the Rules are finished running, runs a callback function which is passed a Result structure. Since some Rules run on the page level while others do not, tests will also run in one of two ways. If a document is specified, the page level rules will run, otherwise they will not. -aXe 3.0 supports open Shadow DOM: see our virtual DOM APIs and test utilities for developing axe-core moving forward. Note: we do not and cannot support closed Shadow DOM. +Axe 3.0 supports open Shadow DOM: see our virtual DOM APIs and test utilities for developing axe-core moving forward. Note: we do not and cannot support closed Shadow DOM. 1. [Getting Started](#getting-started) + 1. [Environment Pre-requisites](#environment-pre-requisites) + 1. [Building axe.js](#building-axejs) + 1. [Running Tests](#running-tests) + 1. [API Reference](#api-reference) + 1. [Supported CSS Selectors](#supported-css-selectors) 1. [Architecture Overview](#architecture-overview) 1. [Rules](#rules) 1. [Checks](#checks) @@ -47,11 +52,20 @@ You can also load tests in any supported browser, which is helpful for debugging ### API Reference -[See API exposed on aXe](./API.md#section-2-api-reference) +[See API exposed on axe](./API.md#section-2-api-reference) + +### Supported CSS Selectors + +Axe supports the following CSS selectors: + +- Type, Class, ID, and Universal selectors. E.g `div.main, #main` +- Pseudo selector `not`. E.g `th:not([scope])` +- Descendant and Child combinators. E.g. `table td`, `ul > li` +- Attribute selectors `=`, `^=`, `$=`, `*=`. E.g `a[href^="#"]` ## Architecture Overview -aXe tests for accessibility using objects called Rules. Each Rule tests for a high-level aspect of accessibility, such as color contrast, button labels, and alternate text for images. Each rule is made up of a series of Checks. Depending on the rule; all, some, or none of these checks must pass in order for the rule to pass. +Axe tests for accessibility using objects called Rules. Each Rule tests for a high-level aspect of accessibility, such as color contrast, button labels, and alternate text for images. Each rule is made up of a series of Checks. Depending on the rule; all, some, or none of these checks must pass in order for the rule to pass. Upon execution, a Rule runs each of its Checks against all relevant nodes. Which nodes are relevant is determined by the Rule's `selector` property and `matches` function. If a Rule has no Checks that apply to a given node, the Rule will result in an inapplicable result. @@ -62,7 +76,7 @@ After execution, a Check will return `true` or `false` depending on whether or n Rules are defined by JSON files in the [lib/rules directory](../lib/rules). The JSON object is used to seed the [Rule object](../lib/core/base/rule.js#L30). A valid Rule JSON consists of the following: - `id` - `String` A unique name of the Rule. -- `selector` - **optional** `String` which is a CSS selector that specifies the elements of the page on which the Rule runs. aXe-core will look inside of the light DOM and _open_ [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM) trees for elements matching the provided selector. If omitted, the rule will run against every node. +- `selector` - **optional** `String` which is a [CSS selector](#supported-css-selectors) that specifies the elements of the page on which the Rule runs. axe-core will look inside of the light DOM and _open_ [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM) trees for elements matching the provided selector. If omitted, the rule will run against every node. - `excludeHidden` - **optional** `Boolean` Whether the rule should exclude hidden elements. Defaults to `true`. - `enabled` - **optional** `Boolean` Whether the rule is enabled by default. Defaults to `true`. - `pageLevel` - **optional** `Boolean` Whether the rule is page level. Page level rules will only run if given an entire `document` as context. @@ -162,7 +176,7 @@ on writing rules and checks, including incomplete results. #### CheckResult -When a Check is executed, its result is then added to a [CheckResult object](../lib/core/base/check-result.js). Much like the RuleResult object, the CheckResult object only contains information that is required to determine whether a Check, and its parent Rule passed or failed. `metadata` from the originating Check is combined later and will not be available until aXe reaches the reporting stage. +When a Check is executed, its result is then added to a [CheckResult object](../lib/core/base/check-result.js). Much like the RuleResult object, the CheckResult object only contains information that is required to determine whether a Check, and its parent Rule passed or failed. `metadata` from the originating Check is combined later and will not be available until axe reaches the reporting stage. A CheckResult has the following properties: @@ -178,7 +192,7 @@ Common functions are an internal library used by the rules and checks. If you ha #### Commons and Shadow DOM To support open Shadow DOM while maintaining backwards compatibility, commons functions that -query DOM nodes must operate on an in-memory representation of the DOM using aXe-core’s +query DOM nodes must operate on an in-memory representation of the DOM using axe-core’s built-in [API methods and utility functions](./API.md#virtual-dom-utilities). Commons functions should do the virtual tree lookup and call a `virtual` function @@ -211,7 +225,7 @@ which passes on a virtual DOM node with both the light DOM and Shadow DOM (if ap ### Virtual Nodes -To support open Shadow DOM, aXe-core has the ability to handle virtual nodes in [rule matches](#matches-function) +To support open Shadow DOM, axe-core has the ability to handle virtual nodes in [rule matches](#matches-function) and [check evaluate](#check-evaluate) functions. The full set of API methods for Shadow DOM can be found in the [API documentation](./API.md#virtual-dom-utilities), but the general structure for a virtualNode is as follows: @@ -230,7 +244,7 @@ structure for a virtualNode is as follows: ### Core Utilities -Core Utilities are an internal library that provides aXe with functionality used throughout its core processes. Most notably among these are the queue function and the DqElement constructor. +Core Utilities are an internal library that provides axe with functionality used throughout its core processes. Most notably among these are the queue function and the DqElement constructor. #### ARIA Lookup Tables diff --git a/doc/examples/chrome-debugging-protocol/package.json b/doc/examples/chrome-debugging-protocol/package.json index 95f5b2b25e..7d2080bcc9 100644 --- a/doc/examples/chrome-debugging-protocol/package.json +++ b/doc/examples/chrome-debugging-protocol/package.json @@ -1,8 +1,11 @@ { "name": "axe-cdp", "private": true, + "scripts": { + "test": "echo 'No test specified.'" + }, "dependencies": { - "axe-core": "^3.1.1", - "chrome-remote-interface": "^0.26.0" + "axe-core": "^3.2.2", + "chrome-remote-interface": "^0.27.1" } } diff --git a/doc/examples/jasmine/Gruntfile.js b/doc/examples/jasmine/Gruntfile.js deleted file mode 100644 index 6871ee18e9..0000000000 --- a/doc/examples/jasmine/Gruntfile.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = function(grunt) { - 'use strict'; - - grunt.loadNpmTasks('grunt-contrib-jasmine'); - - grunt.initConfig({ - jasmine: { - test: { - src: ['node_modules/axe-core/axe.js'], - options: { - specs: 'spec/**/*.js' - } - } - } - }); -}; diff --git a/doc/examples/jasmine/README.md b/doc/examples/jasmine/README.md index d4c8386eb1..4b05b6394a 100644 --- a/doc/examples/jasmine/README.md +++ b/doc/examples/jasmine/README.md @@ -1,6 +1,6 @@ # Jasmine README -This example demonstrates how to use aXe with the Jasmine unit testing framework. +This example demonstrates how to use axe with the Jasmine unit testing framework. The unit test is in `spec/a11y.js`, and has two test cases: One that shows the expected results from HTML with no errors, and one that shows the expected @@ -32,6 +32,6 @@ to `axe.run`. The third argument to the `axe.run` call should be the function to test the results. The example is simply looking at the count of violations, but much -more detailed information is available if desired. The aXe documentation +more detailed information is available if desired. The axe documentation should be consulted for more details on customizing and analyzing calls to `axe.run`. diff --git a/doc/examples/jasmine/karma.conf.js b/doc/examples/jasmine/karma.conf.js new file mode 100644 index 0000000000..e8cf2f0630 --- /dev/null +++ b/doc/examples/jasmine/karma.conf.js @@ -0,0 +1,29 @@ +module.exports = function(config) { + config.set({ + basePath: '', + + frameworks: ['jasmine'], + + files: ['spec/**/*.js', 'node_modules/axe-core/axe.js'], + + exclude: [], + + preprocessors: {}, + + reporters: ['progress'], + + port: 9876, + + colors: true, + + logLevel: config.LOG_INFO, + + autoWatch: true, + + browsers: ['ChromeHeadless'], + + singleRun: true, + + concurrency: Infinity + }); +}; diff --git a/doc/examples/jasmine/package.json b/doc/examples/jasmine/package.json index 5b74fa8ea8..4e4df4bbc2 100644 --- a/doc/examples/jasmine/package.json +++ b/doc/examples/jasmine/package.json @@ -1,6 +1,6 @@ { "name": "axe-jasmine-example", - "description": "aXe Jasmine Example", + "description": "Axe Jasmine Example", "version": "0.0.1", "private": true, "author": { @@ -10,11 +10,12 @@ }, "dependencies": {}, "scripts": { - "test": "grunt jasmine" + "test": "karma start karma.conf.js" }, "devDependencies": { - "axe-core": "^3.1.1", - "grunt": "~1.0.2", - "grunt-contrib-jasmine": "~2.0.0" + "axe-core": "^3.2.2", + "karma": "^4.1.0", + "karma-chrome-launcher": "^2.2.0", + "karma-jasmine": "^2.0.1" } } diff --git a/doc/examples/jest_react/README.md b/doc/examples/jest_react/README.md index 7736710227..d59e254548 100644 --- a/doc/examples/jest_react/README.md +++ b/doc/examples/jest_react/README.md @@ -1,6 +1,6 @@ # Jest + React README -This example demonstrates how to use aXe to test React components using the +This example demonstrates how to use axe to test React components using the Jest unit testing framework. The unit test is in `link.test.js`, and has one test cases, showing how to run diff --git a/doc/examples/jest_react/link.test.js b/doc/examples/jest_react/link.test.js index 2d67b64f35..cbfd393cef 100644 --- a/doc/examples/jest_react/link.test.js +++ b/doc/examples/jest_react/link.test.js @@ -2,11 +2,11 @@ import React from 'react'; import axe from 'axe-core'; import { mountToDoc } from './test-helpers'; -import Link from './Link'; +import Link from './link'; -test('Link has no aXe violations', done => { +test('Link has no axe violations', done => { const linkComponent = mountToDoc( - aXe website + axe website ); const linkNode = linkComponent.getDOMNode(); diff --git a/doc/examples/jest_react/package.json b/doc/examples/jest_react/package.json index 14f669842d..98dd6e2ff3 100644 --- a/doc/examples/jest_react/package.json +++ b/doc/examples/jest_react/package.json @@ -1,6 +1,6 @@ { "name": "axe-jest-react-example", - "description": "aXe Jest + React Example", + "description": "Axe Jest + React Example", "version": "0.0.1", "private": true, "author": { @@ -13,12 +13,12 @@ "test": "jest" }, "devDependencies": { - "axe-core": "^3.1.1", + "axe-core": "^3.2.2", "babel-jest": "^23.0.0", "babel-preset-es2015": "^6.24.1", "babel-preset-react": "^6.24.1", - "enzyme": "^3.5.0", - "enzyme-adapter-react-16": "^1.3.0", + "enzyme": "^3.9.0", + "enzyme-adapter-react-16": "^1.12.1", "jest": "^22.4.0", "jest-cli": "^23.0.1", "react": "^16.4.0", diff --git a/doc/examples/jsdom/package.json b/doc/examples/jsdom/package.json new file mode 100644 index 0000000000..38d8e325fe --- /dev/null +++ b/doc/examples/jsdom/package.json @@ -0,0 +1,15 @@ +{ + "name": "axe-jsdom-example", + "description": "Axe JSDOM Example", + "version": "0.0.1", + "private": true, + "dependencies": {}, + "scripts": { + "test": "mocha" + }, + "devDependencies": { + "axe-core": "^3.2.2", + "jsdom": "^15.0.0", + "mocha": "^6.1.4" + } +} diff --git a/doc/examples/jsdom/test/a11y.js b/doc/examples/jsdom/test/a11y.js new file mode 100644 index 0000000000..cfa4688a07 --- /dev/null +++ b/doc/examples/jsdom/test/a11y.js @@ -0,0 +1,61 @@ +/* global describe, it */ +const jsdom = require('jsdom'); +const { JSDOM } = jsdom; +const assert = require('assert'); + +describe('axe', () => { + const { window } = new JSDOM(` + + + JSDOM Example + + +
+ + +
+
+

Not a label

+
+ + `); + + global.document = window.document; + global.window = window; + + // needed by axios lib/helpers/isURLSameOrigin.js + global.navigator = window.navigator; + + // needed by axe /lib/core/public/run.js + global.Node = window.Node; + global.NodeList = window.NodeList; + + // needed by axe /lib/core/base/context.js + global.Element = window.Element; + global.Document = window.Document; + + const axe = require('axe-core'); + const config = { + rules: { + 'color-contrast': { enabled: false } + } + }; + + it('should report that good HTML is good', function(done) { + var n = window.document.getElementById('working'); + axe.run(n, config, function(err, result) { + assert.equal(err, null, 'Error is not null'); + assert.equal(result.violations.length, 0, 'Violations is not empty'); + done(); + }); + }); + + it('should report that bad HTML is bad', function(done) { + var n = window.document.getElementById('broken'); + axe.run(n, config, function(err, result) { + assert.equal(err, null, 'Error is not null'); + assert.equal(result.violations.length, 1, 'Violations.length is not 1'); + done(); + }); + }); +}); diff --git a/doc/examples/mocha/README.md b/doc/examples/mocha/README.md index 2fcaa00dc6..b2fd0447f9 100644 --- a/doc/examples/mocha/README.md +++ b/doc/examples/mocha/README.md @@ -1,6 +1,6 @@ # Mocha README -This example demonstrates how to use aXe with the Mocha unit testing framework. +This example demonstrates how to use axe with the Mocha unit testing framework. The unit test is in `test/a11y.js`, and has two test cases: One that shows the expected results from HTML with no errors, and one that shows the expected @@ -32,6 +32,6 @@ to `axe.run`. The third argument to the `axe.run` call should be the function to test the results. The example is simply looking at the count of violations, but much -more detailed information is available if desired. The aXe documentation +more detailed information is available if desired. The axe documentation should be consulted for more details on customizing and analyzing calls to `axe.run`. diff --git a/doc/examples/mocha/package.json b/doc/examples/mocha/package.json index 8096f7eb1d..cbc39b39de 100644 --- a/doc/examples/mocha/package.json +++ b/doc/examples/mocha/package.json @@ -1,6 +1,6 @@ { "name": "axe-mocha-example", - "description": "aXe Mocha Example", + "description": "Axe Mocha Example", "version": "0.0.1", "private": true, "author": { @@ -13,7 +13,7 @@ "test": "grunt mocha" }, "devDependencies": { - "axe-core": "^3.1.1", + "axe-core": "^3.2.2", "chai": "~4.1.2", "grunt": "~1.0.2", "grunt-mocha": "1.2.0" diff --git a/doc/examples/mocha/test/test.html b/doc/examples/mocha/test/test.html index 057614cd35..b45598d27c 100644 --- a/doc/examples/mocha/test/test.html +++ b/doc/examples/mocha/test/test.html @@ -1,33 +1,50 @@ - - - Example Mocha Test - - - - -
+ + + Example Mocha Test + + + + +
- - - - + + + + - + - - -
- - -
-
-

Label for this text field.

- -
- + + +
+ + +
+
+

Label for this text field.

+ +
+ diff --git a/doc/examples/phantomjs/README.md b/doc/examples/phantomjs/README.md index 3c76bbb757..e27ccd6104 100644 --- a/doc/examples/phantomjs/README.md +++ b/doc/examples/phantomjs/README.md @@ -1,6 +1,6 @@ # PhantomJS README -This example demonstrates how to use aXe with PhantomJS. +This example demonstrates how to use axe with PhantomJS. ## To configure the example @@ -11,7 +11,7 @@ This example demonstrates how to use aXe with PhantomJS. ## To run the example - Move to the `doc/examples/phantomjs` directory -- `phantomjs axe-phantom.js http://www.deque.com` to run aXe in PhantomJS +- `phantomjs axe-phantom.js http://www.deque.com` to run axe in PhantomJS against http://www.deque.com and output results to the terminal -- `phantomjs axe-phantom.js http://www.deque.com results.json` to run aXe in PhantomJS +- `phantomjs axe-phantom.js http://www.deque.com results.json` to run axe in PhantomJS against http://www.deque.com and save results to `results.json` diff --git a/doc/examples/phantomjs/axe-phantom.js b/doc/examples/phantomjs/axe-phantom.js index d873d4d33c..7039ec8e18 100644 --- a/doc/examples/phantomjs/axe-phantom.js +++ b/doc/examples/phantomjs/axe-phantom.js @@ -27,7 +27,7 @@ page.open(args[1], function(status) { page.switchToMainFrame(); page.evaluateAsync(function() { /*global axe */ - axe.run(function(err, results) { + axe.run({ include: ['#page'] }, function(err, results) { if (err) { throw err; } diff --git a/doc/examples/phantomjs/package.json b/doc/examples/phantomjs/package.json index ee967c1902..829a09a08b 100644 --- a/doc/examples/phantomjs/package.json +++ b/doc/examples/phantomjs/package.json @@ -1,6 +1,6 @@ { "name": "axe-phantomjs-example", - "description": "aXe Mocha Example", + "description": "Axe PhantomJS Example", "version": "0.0.1", "private": true, "author": { @@ -10,10 +10,10 @@ }, "dependencies": {}, "scripts": { - "test": "phantomjs axe-phantom.js http://www.deque.com" + "test": "phantomjs axe-phantom.js https://www.deque.com" }, "devDependencies": { - "axe-core": "^3.1.1", + "axe-core": "^3.2.2", "phantomjs": "^2.1.7" } } diff --git a/doc/examples/puppeteer/package.json b/doc/examples/puppeteer/package.json index ae316c6414..a0bf27a863 100644 --- a/doc/examples/puppeteer/package.json +++ b/doc/examples/puppeteer/package.json @@ -3,8 +3,11 @@ "version": "0.0.0", "private": true, "main": "axe-puppeteer.js", + "scripts": { + "test": "node axe-puppeteer.js https://deque.com" + }, "dependencies": { - "axe-core": "^3.1.1", + "axe-core": "^3.2.2", "puppeteer": "^1.6.0" } } diff --git a/doc/examples/qunit/README.md b/doc/examples/qunit/README.md index e8795e3ee6..4dd08a3ca4 100644 --- a/doc/examples/qunit/README.md +++ b/doc/examples/qunit/README.md @@ -1,6 +1,6 @@ # QUnit README -This example demonstrates how to use aXe with the QUnit unit testing framework. +This example demonstrates how to use axe with the QUnit unit testing framework. The unit test is in `test/a11y.js`, and has two test cases: One that shows the expected results from HTML with no errors, and one that shows the expected @@ -32,6 +32,6 @@ to `axe.run`. The third argument to the `axe.run` call should be the function to test the results. The example is simply looking at the count of violations, but much -more detailed information is available if desired. The aXe documentation +more detailed information is available if desired. The axe documentation should be consulted for more details on customizing and analyzing calls to `axe.run`. diff --git a/doc/examples/qunit/package.json b/doc/examples/qunit/package.json index c067db2ae2..482ba4a52d 100644 --- a/doc/examples/qunit/package.json +++ b/doc/examples/qunit/package.json @@ -1,6 +1,6 @@ { "name": "axe-qunit-example", - "description": "aXe QUnit Example", + "description": "Axe QUnit Example", "version": "0.0.1", "private": true, "author": { @@ -13,7 +13,7 @@ "test": "grunt qunit" }, "devDependencies": { - "axe-core": "^3.1.1", + "axe-core": "^3.2.2", "grunt": "~1.0.2", "grunt-contrib-qunit": "~3.1.0", "qunitjs": "~2.4.1" diff --git a/doc/examples/qunit/test/test.html b/doc/examples/qunit/test/test.html index e3820e1976..697d0619e8 100644 --- a/doc/examples/qunit/test/test.html +++ b/doc/examples/qunit/test/test.html @@ -1,27 +1,29 @@ - - - Basic Test Suite - - - - - - - - -
-
-
-
- - -
-
-

Label for this text field.

- -
- - + + + Basic Test Suite + + + + + + + + +
+
+
+ + +
+
+

Label for this text field.

+ +
+ diff --git a/doc/examples/test-examples.js b/doc/examples/test-examples.js new file mode 100644 index 0000000000..6d41a2323e --- /dev/null +++ b/doc/examples/test-examples.js @@ -0,0 +1,22 @@ +const { readdirSync, statSync } = require('fs'); +const { join } = require('path'); +const execa = require('execa'); +const exampleDirs = readdirSync(__dirname) + .map(dir => join(__dirname, dir)) + .filter(dir => statSync(dir).isDirectory()); + +async function runner(dir) { + const config = { cwd: dir, stdio: 'inherit' }; + await execa.shell('npm install', config); + return execa.shell('npm test', config); +} + +Promise.all(exampleDirs.map(runner)) + .then(() => { + // Return successful exit + process.exit(); + }) + .catch(err => { + console.error(err); + process.exit(1); + }); diff --git a/doc/plugins.md b/doc/plugins.md index 2c67f9db4b..8d074b7367 100644 --- a/doc/plugins.md +++ b/doc/plugins.md @@ -1,12 +1,12 @@ # Plugins -aXe implements a general purpose plugin system that takes advantage of the cross-domain iframe capabilities of aXe and allows for adding functionality that extends the aXe library outside of its core automated accessibility auditing realm. +Axe implements a general purpose plugin system that takes advantage of the cross-domain iframe capabilities of axe and allows for adding functionality that extends the axe library outside of its core automated accessibility auditing realm. The plugin system was initially designed to support functionality like highlighting of elements but has also been utilized for a variety of tasks including implementing functionality that aids with manual accessibility auditing. ## What is a plugin? -Plugins can be viewed as a registry of tools. The plugins themselves are registered with aXe and then allow themselves for the registration of plugin instances. +Plugins can be viewed as a registry of tools. The plugins themselves are registered with axe and then allow themselves for the registration of plugin instances. Lets walk through a plugin implementation as an example to illustrate how plugins and plugin instances work. @@ -14,9 +14,9 @@ Lets walk through a plugin implementation as an example to illustrate how plugin The act plugin will simply perform an action of some sort inside every iframe on the page. An example of how such a plugin might be used is to implement an instance of this plugin that performs highlighting of all of the elements of a particular type on the page. -Plugins currently support two functions, a "run" function and a "collect" function. Together these functions can be combined to implement complex behaviors on top of the aXe system. +Plugins currently support two functions, a "run" function and a "collect" function. Together these functions can be combined to implement complex behaviors on top of the axe system. -In order to create such a plugin, we need to implement the "run" function for the plugin, and the command that registers and executes the "run" function within each iframe on the page that contains aXe. Lets look at what a noop implementation of this run function would look like: +In order to create such a plugin, we need to implement the "run" function for the plugin, and the command that registers and executes the "run" function within each iframe on the page that contains axe. Lets look at what a noop implementation of this run function would look like: #### Basic plugin @@ -77,16 +77,16 @@ axe.registerPlugin({ Looking at the code, you will see the following things: 1. The plugin contains an id. This id is then used to access the plugin and its implementations. -2. The plugin is registered with aXe (in each iframe) using the `axe.registerPlugin()` function. -3. The plugin registers the "run" function and the "commands" with the aXe system. This allows plugin implementations to be registered with the plugin, and to be executed. It also registers handlers for each of the commands within each of the iframes, so that the plugin can coordinate with itself across the iframe boundaries. +2. The plugin is registered with axe (in each iframe) using the `axe.registerPlugin()` function. +3. The plugin registers the "run" function and the "commands" with the axe system. This allows plugin implementations to be registered with the plugin, and to be executed. It also registers handlers for each of the commands within each of the iframes, so that the plugin can coordinate with itself across the iframe boundaries. When the caller wants to call a plugin instance, it does so by calling the plugin's "run" function in the top level document and passing the id of the plugin instance it would like to call, which plugin instance action it would like to call, the options and a callback function. -The plugin takes this information and sends the same instructions to its implementation in each iframe by communicating to its own command(s) using the aXe utility function `axe.utils.sendCommandToFrame()`. +The plugin takes this information and sends the same instructions to its implementation in each iframe by communicating to its own command(s) using the axe utility function `axe.utils.sendCommandToFrame()`. The plugin waits for the commands in the iframes to complete and then executes its instances' action function within the current document. -In the above implementation, the aXe promise utility `axe.utils.queue()` is used to coordinate the asynchronous handling of communication accross iframes. +In the above implementation, the axe promise utility `axe.utils.queue()` is used to coordinate the asynchronous handling of communication accross iframes. The command handler callback runs the plugin's run function within each iframe. This essentially operates like a recursive call to the run function for the plugin within each iframe. @@ -118,6 +118,6 @@ var highlight = { axe.plugins.doStuff.add(highlight); ``` -Above you can see the implementation of a `doStuff` "highlight" instance (the actual highlighting code is not included so as to simplify the example and is left as an exercise for the reader). Plugin instances have an id (which is used to address them), a cleanup function and any number of private or action members. The doStuff `add()` function is called to register this instance with the plugin (notice that we did not have to implement this add function, aXe did that for us). In this case, the action is called "run", so after registration, this instance can be called by calling `axe.plugins.doStuff.run('highlight', 'run', options, callback);` in the top-level iframe on the page. +Above you can see the implementation of a `doStuff` "highlight" instance (the actual highlighting code is not included so as to simplify the example and is left as an exercise for the reader). Plugin instances have an id (which is used to address them), a cleanup function and any number of private or action members. The doStuff `add()` function is called to register this instance with the plugin (notice that we did not have to implement this add function, axe did that for us). In this case, the action is called "run", so after registration, this instance can be called by calling `axe.plugins.doStuff.run('highlight', 'run', options, callback);` in the top-level iframe on the page. The cleanup functions for all plugin instances are called when the `axe.cleanup()` function is called. Note that this cleanup function will automatically call all the cleanup functions for all the plugin instances in all iframes on the page. diff --git a/doc/projects.md b/doc/projects.md index bd8afdc37a..06151592db 100644 --- a/doc/projects.md +++ b/doc/projects.md @@ -7,8 +7,8 @@ Add your project/integration to this file and submit a pull request. 1. [WorldSpace Attest](https://www.deque.com/products/worldspace-attest/) 1. [WorldSpace Assure](https://www.deque.com/products/worldspace-assure/) 1. [WorldSpace Comply](https://www.deque.com/products/worldspace-comply/) -1. [aXe Chrome Extension](https://chrome.google.com/webstore/detail/axe/lhdoppojpmngadmnindnejefpokejbdd) -1. [aXe Firefox Extension](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/) +1. [Axe Chrome Extension](https://chrome.google.com/webstore/detail/axe/lhdoppojpmngadmnindnejefpokejbdd) +1. [Axe Firefox Extension](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/) 1. [axe-cli](https://www.npmjs.com/package/axe-cli) 1. [axe-webdriverjs](https://www.npmjs.com/package/axe-webdriverjs) 1. [grunt-axe-webdriver](https://www.npmjs.com/package/grunt-axe-webdriver) @@ -20,9 +20,10 @@ Add your project/integration to this file and submit a pull request. 1. [Lighthouse](https://github.com/GoogleChrome/lighthouse) 1. [Webhint.io](https://webhint.io/) +1. [AccessibilityInsights](https://accessibilityinsights.io/) 1. [Web Accessibility Checker for Visual Studio](https://visualstudiogallery.msdn.microsoft.com/3aabefab-1681-4fea-8f95-6a62e2f0f1ec) 1. [Ace, by DAISY](https://daisy.github.io/ace) -1. [axe Selenium Python](https://github.com/mozilla-services/axe-selenium-python) +1. [Axe Selenium Python](https://github.com/mozilla-services/axe-selenium-python) 1. [gulp-axe-webdriver](https://github.com/felixzapata/gulp-axe-webdriver) 1. [Ghost-Axe](https://www.npmjs.com/package/ghost-axe) 1. [ember-a11y-testing](https://www.npmjs.com/package/ember-a11y-testing) @@ -32,13 +33,13 @@ Add your project/integration to this file and submit a pull request. 1. [Protractor-axe-report Plugin](https://github.com/E1Edatatracker/protractor-axe-report-plugin) 1. [Cypress-axe](https://github.com/avanslaars/cypress-axe) 1. [Jest-axe](https://github.com/nickcolley/jest-axe) -1. [aXe audit runner for CrawlKit](https://github.com/crawlkit/runner-axe) -1. [Selenium IDE aXe Extension](https://github.com/bkardell/selenium-ide-axe) +1. [axe audit runner for CrawlKit](https://github.com/crawlkit/runner-axe) +1. [Selenium IDE axe Extension](https://github.com/bkardell/selenium-ide-axe) 1. [Axegrinder](https://github.com/claflamme/axegrinder) 1. [Rocket Validator](https://rocketvalidator.com) -1. [aXe Reports](https://github.com/louis-reed/axe-reports) -1. [aXe WebdriverIO](https://github.com/snagi/axe-webdriverio) -1. [aXe-TestCafe](https://github.com/helen-dikareva/axe-testcafe) +1. [axe Reports](https://github.com/louis-reed/axe-reports) +1. [axe WebdriverIO](https://github.com/snagi/axe-webdriverio) +1. [axe-TestCafe](https://github.com/helen-dikareva/axe-testcafe) 1. [Web Audit University of Nebraska-Lincoln](https://webaudit.unl.edu/) 1. [Vorlon.js Remote Debugger](https://github.com/MicrosoftDX/Vorlonjs) 1. [Terra's Webdriver.io Accessibility Service](https://github.com/cerner/terra-toolkit/blob/master/docs/AxeService.md) diff --git a/doc/rule-descriptions.md b/doc/rule-descriptions.md index 233eeb49d8..c6bf76f40a 100644 --- a/doc/rule-descriptions.md +++ b/doc/rule-descriptions.md @@ -6,7 +6,7 @@ | aria-allowed-role | Ensures role attribute has an appropriate value for the element | Minor | cat.aria, best-practice | true | | aria-dpub-role-fallback | Ensures unsupported DPUB roles are only used on elements with implicit fallback roles | Moderate | cat.aria, wcag2a, wcag131 | true | | aria-hidden-body | Ensures aria-hidden='true' is not present on the document body. | Critical | cat.aria, wcag2a, wcag412 | true | -| aria-hidden-focus | Ensures aria-hidden elements do not contain focusable elements | Serious | cat.name-role-value, wcag2a, wcag412 | true | +| aria-hidden-focus | Ensures aria-hidden elements do not contain focusable elements | Serious | cat.name-role-value, wcag2a, wcag412, wcag131 | true | | aria-required-attr | Ensures elements with ARIA roles have all required ARIA attributes | Critical | cat.aria, wcag2a, wcag412 | true | | aria-required-children | Ensures elements with an ARIA role that require child roles contain them | Critical | cat.aria, wcag2a, wcag131 | true | | aria-required-parent | Ensures elements with an ARIA role that require parent roles are contained by them | Critical | cat.aria, wcag2a, wcag131 | true | @@ -15,6 +15,7 @@ | aria-valid-attr | Ensures attributes that begin with aria- are valid ARIA attributes | Critical | cat.aria, wcag2a, wcag412 | true | | audio-caption | Ensures <audio> elements have captions | Critical | cat.time-and-media, wcag2a, wcag121, section508, section508.22.a | false | | autocomplete-valid | Ensure the autocomplete attribute is correct and suitable for the form field | Serious | cat.forms, wcag21aa, wcag135 | true | +| avoid-inline-spacing | Ensure that text spacing set through style attributes can be adjusted with custom stylesheets | Serious | wcag21, wcag1412 | true | | blink | Ensures <blink> elements are not used | Serious | cat.time-and-media, wcag2a, wcag222, section508, section508.22.j | true | | button-name | Ensures buttons have discernible text | Serious, Critical | cat.name-role-value, wcag2a, wcag412, section508, section508.22.a | true | | bypass | Ensures each page has at least one mechanism for a user to bypass navigation and jump straight to the content | Serious | cat.keyboard, wcag2a, wcag241, section508, section508.22.o | true | diff --git a/doc/rule-development.md b/doc/rule-development.md index 9cedd45c48..2d2dd6424c 100644 --- a/doc/rule-development.md +++ b/doc/rule-development.md @@ -2,7 +2,7 @@ Before you start writing axe-core rules, be sure to create a proposal for them in a github issue. Read [Proposing Axe-core rules](./rule-proposal.md) for details. -A rule is a JSON Object that defines a test for aXe-core to run. At a high level, think of a rule as doing two things. First it finds all elements that it should test, and after that it runs a number of checks to see if those selected elements pass or fail the rule. +A rule is a JSON Object that defines a test for axe-core to run. At a high level, think of a rule as doing two things. First it finds all elements that it should test, and after that it runs a number of checks to see if those selected elements pass or fail the rule. ## Rule Select and Matches @@ -33,7 +33,7 @@ The actual testing of elements in axe-core is done by checks. A rule has one or | enabled | Does the rule run by default | | tags | Grouping for the rule, such as wcag2a, best-practice | | metadata.description | Description of what a rule does | -| metadata.help | Short description of a violation, used in the aXe extension sidebar | +| metadata.help | Short description of a violation, used in the axe extension sidebar | ## Check Properties @@ -93,3 +93,31 @@ this.data({ missingData: 'bgImage' }); ``` + +# Hierarchical rules + +Axe-core handles shadow DOM and cross-domain iframe rules very well - as long as you take care in the design and implementation of your rule. Any rule that evaluates the DOM hierarchy needs to think about what happens across shadow DOM boundaries and iframe boundaries. + +The rule callbacks all receive both a `node` and a `virtualNode` argument (in addition to the `options` argument). `node` points to the DOM Node that is to be evaluated, whereas `virtualNode` points to the node in the flattened tree (the hierarchy that shadow DOM creates that is used for parent child relationships across shadow DOM boundaries). + +If your rule looks at any hierarchical context (parents or children) then you need to operate on the `virtualNode` for those operations. Calls to `isHidden` and the accessible name calculation calls will all evaluate the hierarchy. The commons and utils functions will all fetch the `virtualNode` from the flattened tree if you use the `node` implementation (and then simply call the virtualNode one) and are there for backwards compatibility. This backwards compatibility comes at a perfomance cost that can be avoided by simply using the `virtualNode` to start with. We will ask you to change this during PR review, so you might as well just start out by using the `virtualNode`. + +## iframes + +Rules that evaluate the structure and/or the number of elements on the entire page (for example the heading nesting rule, or the landmark rules) will need to do this evaluation across iframe boundaries. What this means is that the check function instead of determining pass/fail/incomplete, perfomas a data gathering function. This data is then passed up the iframe hierarchy to the top window where it is passed into the `after` function, which does the evaluation of the gathered data and determines pass/fail. + +## Rules of Thumb for Rule Code Reviewers + +Rules of thumb for determining whether `virtualNode` should be used - if any of the answers to the following questions is yes, then it should use shadow DOM and `virtualNode`: + +1. Is it using the non-virtualNode version of a function that wraps the flattened tree lookup? +2. Is it evaluating the DOM hierarchy? + +Rules that do this MUST have shadow DOM test cases that show passes and fails across the shadow DOM boundary. + +Rules of thumb for determining whether an `after` function is required - if any of the answers to the following questions is yes: + +1. Does the rule evaluate number of things on a page? +2. Does the rule evaluate hierarchy of things across a whole page? + +Rules that use an `after` function MUST have iframe test cases that assert correct data passing between iframes and handle all the relevant cases across iframes. diff --git a/lib/.eslintrc b/lib/.eslintrc index c1217e824b..4bda71755c 100644 --- a/lib/.eslintrc +++ b/lib/.eslintrc @@ -38,10 +38,6 @@ 2, 5 ], - "max-statements": [ - 2, - 16 - ], "max-len": [ 2, { diff --git a/lib/checks/.eslintrc b/lib/checks/.eslintrc index a634a69de1..f83400386b 100644 --- a/lib/checks/.eslintrc +++ b/lib/checks/.eslintrc @@ -42,14 +42,6 @@ 2, 5 ], - "max-statements": [ - 2, - 15 - ], - "complexity": [ - 2, - 10 - ], "no-cond-assign": 0, "no-debugger": 0, "no-eq-null": 0, diff --git a/lib/checks/aria/allowed-attr.js b/lib/checks/aria/allowed-attr.js index 0e1b2d2fa8..b9c8fd7928 100644 --- a/lib/checks/aria/allowed-attr.js +++ b/lib/checks/aria/allowed-attr.js @@ -6,7 +6,7 @@ var attr, attrName, allowed, role = node.getAttribute('role'), - attrs = node.attributes; + attrs = axe.utils.getNodeAttributes(node); if (!role) { role = axe.commons.aria.implicitRole(node); diff --git a/lib/checks/aria/required-children.js b/lib/checks/aria/required-children.js index dbf02413c3..0ae3aada6a 100644 --- a/lib/checks/aria/required-children.js +++ b/lib/checks/aria/required-children.js @@ -30,7 +30,7 @@ function ariaOwns(nodes, role) { if (nodes[index] === null) { continue; } - let virtualTree = axe.utils.getNodeFromTree(axe._tree[0], nodes[index]); + const virtualTree = axe.utils.getNodeFromTree(nodes[index]); if (owns(nodes[index], virtualTree, role, true)) { return true; } @@ -39,7 +39,6 @@ function ariaOwns(nodes, role) { } function missingRequiredChildren(node, childRoles, all, role) { - /* eslint max-statements: ["error", 22], complexity: ["error", 17] */ var i, l = childRoles.length, missing = [], diff --git a/lib/checks/aria/required-parent.js b/lib/checks/aria/required-parent.js index 45d1a646c7..21aa9ea14f 100644 --- a/lib/checks/aria/required-parent.js +++ b/lib/checks/aria/required-parent.js @@ -73,7 +73,7 @@ var owners = getAriaOwners(node); if (owners) { for (var i = 0, l = owners.length; i < l; i++) { missingParents = getMissingContext( - axe.utils.getNodeFromTree(axe._tree[0], owners[i]), + axe.utils.getNodeFromTree(owners[i]), missingParents, true ); diff --git a/lib/checks/aria/unsupportedattr.js b/lib/checks/aria/unsupportedattr.js index b485d5019e..6c8aca65a3 100644 --- a/lib/checks/aria/unsupportedattr.js +++ b/lib/checks/aria/unsupportedattr.js @@ -2,7 +2,7 @@ const nodeName = node.nodeName.toUpperCase(); const lookupTable = axe.commons.aria.lookupTable; const role = axe.commons.aria.getRole(node); -const unsupportedAttrs = Array.from(node.attributes) +const unsupportedAttrs = Array.from(axe.utils.getNodeAttributes(node)) .filter(({ name }) => { const attribute = lookupTable.attributes[name]; diff --git a/lib/checks/aria/valid-attr-value.js b/lib/checks/aria/valid-attr-value.js index e84a02d06d..05e0715c15 100644 --- a/lib/checks/aria/valid-attr-value.js +++ b/lib/checks/aria/valid-attr-value.js @@ -1,26 +1,41 @@ options = Array.isArray(options) ? options : []; -var invalid = [], - aria = /^aria-/; +const invalid = []; +const aria = /^aria-/; +const attrs = axe.utils.getNodeAttributes(node); -var attr, - attrName, - attrs = node.attributes; +const skipAttrs = ['aria-errormessage']; -var skipAttrs = ['aria-errormessage']; +// aria-controls should only check if element exists if the element +// doesn't have aria-expanded=false or aria-selected=false (tabs) +// @see https://github.com/dequelabs/axe-core/issues/1463 +const preChecks = { + 'aria-controls': function() { + return ( + node.getAttribute('aria-expanded') !== 'false' && + node.getAttribute('aria-selected') !== 'false' + ); + }, + // aria-owns should only check if element exists if the element + // doesn't have aria-expanded=false (combobox) + // @see https://github.com/dequelabs/axe-core/issues/1524 + 'aria-owns': function() { + return node.getAttribute('aria-expanded') !== 'false'; + } +}; -for (var i = 0, l = attrs.length; i < l; i++) { - attr = attrs[i]; - attrName = attr.name; +for (let i = 0, l = attrs.length; i < l; i++) { + const attr = attrs[i]; + const attrName = attr.name; // skip any attributes handled elsewhere - if (!skipAttrs.includes(attrName)) { - if ( - options.indexOf(attrName) === -1 && - aria.test(attrName) && - !axe.commons.aria.validateAttrValue(node, attrName) - ) { - invalid.push(attrName + '="' + attr.nodeValue + '"'); - } + if ( + !skipAttrs.includes(attrName) && + options.indexOf(attrName) === -1 && + aria.test(attrName) && + (preChecks[attrName] ? preChecks[attrName]() : true) && + !axe.commons.aria.validateAttrValue(node, attrName) + ) { + invalid.push(`${attrName}="${attr.nodeValue}"`); } } diff --git a/lib/checks/aria/valid-attr.js b/lib/checks/aria/valid-attr.js index d9a46f287f..638ad83114 100644 --- a/lib/checks/aria/valid-attr.js +++ b/lib/checks/aria/valid-attr.js @@ -4,7 +4,7 @@ var invalid = [], aria = /^aria-/; var attr, - attrs = node.attributes; + attrs = axe.utils.getNodeAttributes(node); for (var i = 0, l = attrs.length; i < l; i++) { attr = attrs[i].name; diff --git a/lib/checks/lists/only-listitems.js b/lib/checks/lists/only-listitems.js index 0828109091..2a98bfd700 100644 --- a/lib/checks/lists/only-listitems.js +++ b/lib/checks/lists/only-listitems.js @@ -16,10 +16,6 @@ let base = { }; let out = virtualNode.children.reduce((out, { actualNode }) => { - /*eslint - max-statements: ["error", 20] - complexity: ["error", 12] - */ const tagName = actualNode.nodeName.toUpperCase(); if (actualNode.nodeType === 1 && dom.isVisible(actualNode, true, false)) { diff --git a/lib/checks/mobile/css-orientation-lock.js b/lib/checks/mobile/css-orientation-lock.js index 131e76e25e..0bf44d9243 100644 --- a/lib/checks/mobile/css-orientation-lock.js +++ b/lib/checks/mobile/css-orientation-lock.js @@ -78,8 +78,6 @@ Object.keys(rulesGroupByDocumentFragment).forEach(key => { // a media query has framents of css styles applied to various selectors // iteration through cssRules and see if orientation lock has been applied Array.from(r.cssRules).forEach(cssRule => { - /* eslint max-statements: ["error", 20], complexity: ["error", 15] */ - // ensure selectorText exists if (!cssRule.selectorText) { return; diff --git a/lib/checks/navigation/region.js b/lib/checks/navigation/region.js index 653146f21f..e890bea5ab 100644 --- a/lib/checks/navigation/region.js +++ b/lib/checks/navigation/region.js @@ -1,17 +1,4 @@ const { dom, aria } = axe.commons; - -// Return the skplink, if any -function getSkiplink(virtualNode) { - const firstLink = axe.utils.querySelectorAll(virtualNode, 'a[href]')[0]; - if ( - firstLink && - axe.commons.dom.getElementByReference(firstLink.actualNode, 'href') - ) { - return firstLink.actualNode; - } -} - -const skipLink = getSkiplink(virtualNode); const landmarkRoles = aria.getRolesByType('landmark'); // Create a list of nodeNames that have a landmark as an implicit role @@ -19,11 +6,6 @@ const implicitLandmarks = landmarkRoles .reduce((arr, role) => arr.concat(aria.implicitNodes(role)), []) .filter(r => r !== null); -// Check if the current element is the skiplink -function isSkipLink(vNode) { - return skipLink && skipLink === vNode.actualNode; -} - // Check if the current element is a landmark function isRegion(virtualNode) { const node = virtualNode.actualNode; @@ -61,7 +43,8 @@ function findRegionlessElms(virtualNode) { // End recursion if the element is a landmark, skiplink, or hidden content if ( isRegion(virtualNode) || - isSkipLink(virtualNode) || + (dom.isSkipLink(virtualNode.actualNode) && + dom.getElementByReference(virtualNode.actualNode, 'href')) || !dom.isVisible(node, true) ) { return []; diff --git a/lib/checks/shared/avoid-inline-spacing.js b/lib/checks/shared/avoid-inline-spacing.js new file mode 100644 index 0000000000..ae25015b97 --- /dev/null +++ b/lib/checks/shared/avoid-inline-spacing.js @@ -0,0 +1,18 @@ +const inlineSpacingCssProperties = [ + 'line-height', + 'letter-spacing', + 'word-spacing' +]; + +const overriddenProperties = inlineSpacingCssProperties.filter(property => { + if (node.style.getPropertyPriority(property) === `important`) { + return property; + } +}); + +if (overriddenProperties.length > 0) { + this.data(overriddenProperties); + return false; +} + +return true; diff --git a/lib/checks/shared/avoid-inline-spacing.json b/lib/checks/shared/avoid-inline-spacing.json new file mode 100644 index 0000000000..74c1b5075a --- /dev/null +++ b/lib/checks/shared/avoid-inline-spacing.json @@ -0,0 +1,11 @@ +{ + "id": "avoid-inline-spacing", + "evaluate": "avoid-inline-spacing.js", + "metadata": { + "impact": "serious", + "messages": { + "pass": "No inline styles with '!important' that affect text spacing has been specified", + "fail": "Remove '!important' from inline style{{=it.data && it.data.length > 1 ? 's' : ''}} {{=it.data.join(', ')}}, as overriding this is not supported by most browsers" + } + } +} diff --git a/lib/commons/.eslintrc b/lib/commons/.eslintrc index 8ad6a02039..bd7e70a6ac 100644 --- a/lib/commons/.eslintrc +++ b/lib/commons/.eslintrc @@ -39,10 +39,6 @@ 2, 5 ], - "max-statements": [ - 2, - 20 - ], "no-cond-assign": 0, "no-debugger": 0, "no-eq-null": 0, diff --git a/lib/commons/aria/get-owned-virtual.js b/lib/commons/aria/get-owned-virtual.js index ec904786ce..0fc9e3391f 100644 --- a/lib/commons/aria/get-owned-virtual.js +++ b/lib/commons/aria/get-owned-virtual.js @@ -16,7 +16,7 @@ aria.getOwnedVirtual = function getOwned({ actualNode, children }) { return dom.idrefs(actualNode, 'aria-owns').reduce((ownedElms, element) => { if (element) { - const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], element); + const virtualNode = axe.utils.getNodeFromTree(element); ownedElms.push(virtualNode); } return ownedElms; diff --git a/lib/commons/aria/index.js b/lib/commons/aria/index.js index 7389886c22..ab3fa350b2 100644 --- a/lib/commons/aria/index.js +++ b/lib/commons/aria/index.js @@ -1550,9 +1550,9 @@ lookupTable.role = { 'aria-posinset', 'aria-setsize', 'aria-required', - 'aria-errormessage' - ], - required: ['aria-checked'] + 'aria-errormessage', + 'aria-checked' + ] }, owned: null, nameFrom: ['author', 'contents'], @@ -1653,7 +1653,7 @@ lookupTable.role = { all: ['row'] }, nameFrom: ['author', 'contents'], - context: ['grid', 'table'], + context: ['grid', 'table', 'treegrid'], implicit: ['tbody', 'thead', 'tfoot'], unsupported: false }, diff --git a/lib/commons/aria/is-accessible-ref.js b/lib/commons/aria/is-accessible-ref.js index ed7e842648..0278a1571e 100644 --- a/lib/commons/aria/is-accessible-ref.js +++ b/lib/commons/aria/is-accessible-ref.js @@ -1,13 +1,24 @@ /* global aria, axe, dom */ -function findDomNode(node, functor) { - if (functor(node)) { - return node; +const idRefsRegex = /^idrefs?$/; + +function cacheIdRefs(node, refAttrs) { + if (node.hasAttribute) { + if (node.nodeName.toUpperCase() === 'LABEL' && node.hasAttribute('for')) { + axe._cache.idRefs[node.getAttribute('for')] = true; + } + + refAttrs + .filter(attr => node.hasAttribute(attr)) + .forEach(attr => { + const attrValue = node.getAttribute(attr); + axe.utils.tokenList(attrValue).forEach(id => { + axe._cache.idRefs[id] = true; + }); + }); } + for (let i = 0; i < node.children.length; i++) { - const out = findDomNode(node.children[i], functor); - if (out) { - return out; - } + cacheIdRefs(node.children[i], refAttrs); } } @@ -22,34 +33,18 @@ aria.isAccessibleRef = function isAccessibleRef(node) { root = root.documentElement || root; // account for shadow roots const id = node.id; - // Get all idref(s) attributes on the lookup table - const refAttrs = Object.keys(aria.lookupTable.attributes).filter(attr => { - const { type } = aria.lookupTable.attributes[attr]; - return /^idrefs?$/.test(type); - }); + // because axe.commons is not available in axe.utils, we can't do + // this caching when we build up the virtual tree + if (!axe._cache.idRefs) { + axe._cache.idRefs = {}; + // Get all idref(s) attributes on the lookup table + const refAttrs = Object.keys(aria.lookupTable.attributes).filter(attr => { + const { type } = aria.lookupTable.attributes[attr]; + return idRefsRegex.test(type); + }); - // Find the first element that IDREF(S) the node - let refElm = findDomNode(root, elm => { - if (elm.nodeType !== 1) { - // Elements only - return; - } - if ( - elm.nodeName.toUpperCase() === 'LABEL' && - elm.getAttribute('for') === id - ) { - return true; - } - // See if there are any aria attributes that reference the node - return refAttrs - .filter(attr => elm.hasAttribute(attr)) - .some(attr => { - const attrValue = elm.getAttribute(attr); - if (aria.lookupTable.attributes[attr].type === 'idref') { - return attrValue === id; - } - return axe.utils.tokenList(attrValue).includes(id); - }); - }); - return typeof refElm !== 'undefined'; + cacheIdRefs(root, refAttrs); + } + + return axe._cache.idRefs[id] === true; }; diff --git a/lib/commons/aria/label-virtual.js b/lib/commons/aria/label-virtual.js index 2014254dba..97eb2eb5fb 100644 --- a/lib/commons/aria/label-virtual.js +++ b/lib/commons/aria/label-virtual.js @@ -17,7 +17,7 @@ aria.labelVirtual = function({ actualNode }) { ref = dom.idrefs(actualNode, 'aria-labelledby'); candidate = ref .map(function(thing) { - const vNode = axe.utils.getNodeFromTree(axe._tree[0], thing); + const vNode = axe.utils.getNodeFromTree(thing); return vNode ? text.visibleVirtual(vNode, true) : ''; }) .join(' ') @@ -49,6 +49,6 @@ aria.labelVirtual = function({ actualNode }) { * @return {Mixed} String of visible text, or `null` if no label is found */ aria.label = function(node) { - node = axe.utils.getNodeFromTree(axe._tree[0], node); + node = axe.utils.getNodeFromTree(node); return aria.labelVirtual(node); }; diff --git a/lib/commons/aria/roles.js b/lib/commons/aria/roles.js index ee55770335..cecd4aeb1b 100644 --- a/lib/commons/aria/roles.js +++ b/lib/commons/aria/roles.js @@ -188,7 +188,7 @@ aria.implicitRole = function(node) { return null; } - var nodeAttributes = node.attributes; + var nodeAttributes = axe.utils.getNodeAttributes(node); var ariaAttributes = []; /* Get all aria-attributes defined for this node */ diff --git a/lib/commons/aria/validate-attr-value.js b/lib/commons/aria/validate-attr-value.js index bc1d057537..30369d2cee 100644 --- a/lib/commons/aria/validate-attr-value.js +++ b/lib/commons/aria/validate-attr-value.js @@ -10,7 +10,6 @@ * @return {Boolean} */ aria.validateAttrValue = function validateAttrValue(node, attr) { - /*eslint complexity: ["error",17]*/ 'use strict'; var matches, list, diff --git a/lib/commons/color/center-point-of-rect.js b/lib/commons/color/center-point-of-rect.js new file mode 100644 index 0000000000..e5200767b5 --- /dev/null +++ b/lib/commons/color/center-point-of-rect.js @@ -0,0 +1,30 @@ +/* global color */ + +/** + * Get coordinates for an element's client rects or bounding client rect + * + * @method centerPointOfRect + * @memberof axe.commons.color + * @param {DOMRect} rect + * @returns {Object | undefined} + */ +color.centerPointOfRect = function centerPointOfRect(rect) { + if (rect.left > window.innerWidth) { + return undefined; + } + + if (rect.top > window.innerHeight) { + return undefined; + } + + const x = Math.min( + Math.ceil(rect.left + rect.width / 2), + window.innerWidth - 1 + ); + const y = Math.min( + Math.ceil(rect.top + rect.height / 2), + window.innerHeight - 1 + ); + + return { x, y }; +}; diff --git a/lib/commons/color/element-has-image.js b/lib/commons/color/element-has-image.js new file mode 100644 index 0000000000..c1be869c06 --- /dev/null +++ b/lib/commons/color/element-has-image.js @@ -0,0 +1,36 @@ +/* global color */ + +/** + * Reports if an element has a background image or gradient + * + * @method elementHasImage + * @memberof axe.commons.color + * @private + * @param {Element} elm + * @param {Object|null} style + * @return {Boolean} + */ +color.elementHasImage = function elementHasImage(elm, style) { + const graphicNodes = ['IMG', 'CANVAS', 'OBJECT', 'IFRAME', 'VIDEO', 'SVG']; + const nodeName = elm.nodeName.toUpperCase(); + + if (graphicNodes.includes(nodeName)) { + axe.commons.color.incompleteData.set('bgColor', 'imgNode'); + return true; + } + + style = style || window.getComputedStyle(elm); + + const bgImageStyle = style.getPropertyValue('background-image'); + const hasBgImage = bgImageStyle !== 'none'; + + if (hasBgImage) { + const hasGradient = /gradient/.test(bgImageStyle); + axe.commons.color.incompleteData.set( + 'bgColor', + hasGradient ? 'bgGradient' : 'bgImage' + ); + } + + return hasBgImage; +}; diff --git a/lib/commons/color/get-background-color.js b/lib/commons/color/get-background-color.js index 8cffcf4be6..ef5b7f481f 100644 --- a/lib/commons/color/get-background-color.js +++ b/lib/commons/color/get-background-color.js @@ -1,121 +1,229 @@ /* global axe, color, dom */ -const graphicNodes = ['IMG', 'CANVAS', 'OBJECT', 'IFRAME', 'VIDEO', 'SVG']; /** - * Reports if an element has a background image or gradient - * @private - * @param {Element} elm - * @param {Object|null} style - * @return {Boolean} + * Returns background color for element + * Uses getBackgroundStack() to get all elements rendered underneath the current element, + * to help determine the composite background color. + * + * @method getBackgroundColor + * @memberof axe.commons.color + * @param {Element} elm Element to determine background color + * @param {Array} [bgElms=[]] elements to inspect + * @param {Boolean} [noScroll=false] should scroll + * @returns {Color} */ -function elmHasImage(elm, style) { - var nodeName = elm.nodeName.toUpperCase(); - if (graphicNodes.includes(nodeName)) { - axe.commons.color.incompleteData.set('bgColor', 'imgNode'); - return true; +color.getBackgroundColor = function getBackgroundColor( + elm, + bgElms = [], + noScroll = false +) { + if (noScroll !== true) { + /** + * Avoid scrolling overflow:hidden containers, by only aligning to top, + * when not doing so would move the center point above the viewport top. + */ + const clientHeight = elm.getBoundingClientRect().height; + const alignToTop = clientHeight - 2 >= window.innerHeight * 2; + elm.scrollIntoView(alignToTop); } - style = style || window.getComputedStyle(elm); - var bgImageStyle = style.getPropertyValue('background-image'); - var hasBgImage = bgImageStyle !== 'none'; - if (hasBgImage) { - var hasGradient = /gradient/.test(bgImageStyle); - axe.commons.color.incompleteData.set( - 'bgColor', - hasGradient ? 'bgGradient' : 'bgImage' - ); + let bgColors = []; + let elmStack = color.getBackgroundStack(elm); + + // Search the stack until we have an alpha === 1 background + (elmStack || []).some(bgElm => { + const bgElmStyle = window.getComputedStyle(bgElm); + + // Get the background color + let bgColor = color.getOwnBackgroundColor(bgElmStyle); + + if ( + // abort if a node is partially obscured and obscuring element has a background + elmPartiallyObscured(elm, bgElm, bgColor) || + // OR if the background elm is a graphic + color.elementHasImage(bgElm, bgElmStyle) + ) { + bgColors = null; + bgElms.push(bgElm); + + return true; + } + + if (bgColor.alpha !== 0) { + // store elements contributing to the br color. + bgElms.push(bgElm); + bgColors.push(bgColor); + + // Exit if the background is opaque + return bgColor.alpha === 1; + } else { + return false; + } + }); + + if (bgColors !== null && elmStack !== null) { + // Mix the colors together, on top of a default white + bgColors.push(new color.Color(255, 255, 255, 1)); + var colors = bgColors.reduce(color.flattenColors); + return colors; } - return hasBgImage; -} + + return null; +}; /** - * Returns the non-alpha-blended background color of an element - * @private + * Get all elements rendered underneath the current element, + * In the order they are displayed (front to back) + * + * @method getBackgroundStack + * @memberof axe.commons.color * @param {Element} elm - * @return {Color} + * @return {Array} */ -function getBgColor(elm, elmStyle) { - elmStyle = elmStyle || window.getComputedStyle(elm); +color.getBackgroundStack = function getBackgroundStack(elm) { + let elmStack = color.filteredRectStack(elm); - let bgColor = new color.Color(); - bgColor.parseRgbString(elmStyle.getPropertyValue('background-color')); + if (elmStack === null) { + return null; + } + elmStack = includeMissingElements(elmStack, elm); + elmStack = dom.reduceToElementsBelowFloating(elmStack, elm); + elmStack = sortPageBackground(elmStack); - if (bgColor.alpha !== 0) { - let opacity = elmStyle.getPropertyValue('opacity'); - bgColor.alpha = bgColor.alpha * opacity; + // Return all elements BELOW the current element, null if the element is undefined + let elmIndex = elmStack.indexOf(elm); + if (calculateObscuringAlpha(elmIndex, elmStack, elm) >= 0.99) { + // if the total of the elements above our element results in total obscuring, return null + axe.commons.color.incompleteData.set('bgColor', 'bgOverlap'); + return null; } - return bgColor; -} + return elmIndex !== -1 ? elmStack : null; +}; /** - * Determines overlap of node's content with a bgNode. Used for inline elements - * @private - * @param {Element} targetElement - * @param {Element} bgNode - * @return {Boolean} + * Get filtered stack of block and inline elements, excluding line breaks + * @method filteredRectStack + * @memberof axe.commons.color + * @param {Element} elm + * @return {Array} */ -function contentOverlapping(targetElement, bgNode) { - // get content box of target element - // check to see if the current bgNode is overlapping - var targetRect = targetElement.getClientRects()[0]; - var obscuringElements = dom.shadowElementsFromPoint( - targetRect.left, - targetRect.top - ); - if (obscuringElements) { - for (var i = 0; i < obscuringElements.length; i++) { - if ( - obscuringElements[i] !== targetElement && - obscuringElements[i] === bgNode - ) { - return true; +color.filteredRectStack = function filteredRectStack(elm) { + const rectStack = color.getRectStack(elm); + + if (rectStack && rectStack.length === 1) { + return rectStack[0]; + } + + if (rectStack && rectStack.length > 1) { + const boundingStack = rectStack.shift(); + let isSame; + + // iterating over arrays of DOMRects + rectStack.forEach((rectList, index) => { + if (index === 0) { + return; } + // if the stacks are the same, use the first one. otherwise, return null. + let rectA = rectStack[index - 1], + rectB = rectStack[index]; + + // if elements in clientRects are the same + // or the boundingClientRect contains the differing element, pass it + isSame = + rectA.every( + (element, elementIndex) => element === rectB[elementIndex] + ) || boundingStack.includes(elm); + }); + if (!isSame) { + axe.commons.color.incompleteData.set('bgColor', 'elmPartiallyObscuring'); + return null; } + // pass the first stack if it wasn't partially covered + return rectStack[0]; } - return false; -} + + // rect outside of viewport + axe.commons.color.incompleteData.set('bgColor', 'outsideViewport'); + return null; +}; + /** - * Calculate alpha transparency of a background element obscuring the current node - * @private - * @param {Number} elmIndex - * @param {Array} elmStack - * @param {Element} originalElm - * @return {Number|undefined} + * Get relevant stacks of block and inline elements, excluding line breaks + * @method getRectStack + * @memberof axe.commons.color + * @param {Element} elm + * @return {Array} */ -function calculateObscuringAlpha(elmIndex, elmStack, originalElm) { - var totalAlpha = 0; +color.getRectStack = function(elm) { + const boundingCoords = axe.commons.color.centerPointOfRect( + elm.getBoundingClientRect() + ); - if (elmIndex > 0) { - // there are elements above our element, check if they contribute to the background - for (var i = elmIndex - 1; i >= 0; i--) { - let bgElm = elmStack[i]; - let bgElmStyle = window.getComputedStyle(bgElm); - let bgColor = getBgColor(bgElm, bgElmStyle); - if (bgColor.alpha && contentOverlapping(originalElm, bgElm)) { - totalAlpha += bgColor.alpha; - } else { - // remove elements not contributing to the background - elmStack.splice(i, 1); + if (!boundingCoords) { + return null; + } + + let boundingStack = dom.shadowElementsFromPoint( + boundingCoords.x, + boundingCoords.y + ); + + let rects = Array.from(elm.getClientRects()); + // If the element does not have multiple rects, like for display:block, return a single stack + if (!rects || rects.length <= 1) { + return [boundingStack]; + } + + // Handle inline elements spanning multiple lines to be evaluated + let filteredArr = rects + .filter(rect => { + // exclude manual line breaks in Chrome/Safari + return rect.width && rect.width > 0; + }) + .map(rect => { + const coords = axe.commons.color.centerPointOfRect(rect); + if (coords) { + return dom.shadowElementsFromPoint(coords.x, coords.y); } - } + }); + + if (filteredArr.some(stack => stack === undefined)) { + // Can be happen when one or more of the rects sits outside the viewport + return null; } - return totalAlpha; -} + + // add bounding client rect stack for comparison later + filteredArr.splice(0, 0, boundingStack); + return filteredArr; +}; + /** - * Determine if element is partially overlapped, triggering a Can't Tell result + * Look at document and body elements for relevant background information + * @method sortPageBackground * @private - * @param {Element} elm - * @param {Element} bgElm - * @param {Object} bgColor - * @return {Boolean} + * @param {Array} elmStack + * @returns {Array} */ -function elmPartiallyObscured(elm, bgElm, bgColor) { - var obscured = - elm !== bgElm && !dom.visuallyContains(elm, bgElm) && bgColor.alpha !== 0; - if (obscured) { - axe.commons.color.incompleteData.set('bgColor', 'elmPartiallyObscured'); +function sortPageBackground(elmStack) { + let bodyIndex = elmStack.indexOf(document.body); + let bgNodes = elmStack; + + if ( + // Check that the body background is the page's background + bodyIndex > 1 && // only if there are negative z-index elements + !color.elementHasImage(document.documentElement) && + color.getOwnBackgroundColor( + window.getComputedStyle(document.documentElement) + ).alpha === 0 + ) { + // Remove body and html from it's current place + bgNodes.splice(bodyIndex, 1); + bgNodes.splice(elmStack.indexOf(document.documentElement), 1); + + // Put the body background as the lowest element + bgNodes.push(document.body); } - return obscured; + return bgNodes; } /** @@ -123,10 +231,10 @@ function elmPartiallyObscured(elm, bgElm, bgColor) { * document.elementsFromPoint misses some elements we need * i.e. TR is missing from table elementStack and leaves out bgColor * https://github.com/dequelabs/axe-core/issues/273 - * * @private * @param {Array} elmStack * @param {Element} elm + * @returns {Array} */ function includeMissingElements(elmStack, elm) { /*eslint max-depth:["error",7]*/ @@ -176,229 +284,77 @@ function includeMissingElements(elmStack, elm) { } /** - * Look at document and body elements for relevant background information + * Determine if element is partially overlapped, triggering a Can't Tell result * @private - * @param {Array} elmStack + * @param {Element} elm + * @param {Element} bgElm + * @param {Object} bgColor + * @return {Boolean} */ -function sortPageBackground(elmStack) { - let bodyIndex = elmStack.indexOf(document.body); - - let bgNodes = elmStack; - - if ( - // Check that the body background is the page's background - bodyIndex > 1 && // only if there are negative z-index elements - !elmHasImage(document.documentElement) && - getBgColor(document.documentElement).alpha === 0 - ) { - // Remove body and html from it's current place - bgNodes.splice(bodyIndex, 1); - bgNodes.splice(elmStack.indexOf(document.documentElement), 1); - - // Put the body background as the lowest element - bgNodes.push(document.body); +function elmPartiallyObscured(elm, bgElm, bgColor) { + var obscured = + elm !== bgElm && !dom.visuallyContains(elm, bgElm) && bgColor.alpha !== 0; + if (obscured) { + axe.commons.color.incompleteData.set('bgColor', 'elmPartiallyObscured'); } - return bgNodes; + return obscured; } -/** - * Get coordinates for an element's client rects or bounding client rect - * @method getCoords - * @memberof axe.commons.color - * @instance - * @param {DOMRect} rect - * @return {Object} - */ -color.getCoords = function(rect) { - let x, y; - if (rect.left > window.innerWidth) { - return; - } - if (rect.top > window.innerHeight) { - return; - } - x = Math.min(Math.ceil(rect.left + rect.width / 2), window.innerWidth - 1); - y = Math.min(Math.ceil(rect.top + rect.height / 2), window.innerHeight - 1); - return { x, y }; -}; /** - * Get relevant stacks of block and inline elements, excluding line breaks - * @method getRectStack - * @memberof axe.commons.color - * @instance - * @param {Element} elm - * @return {Array} + * Calculate alpha transparency of a background element obscuring the current node + * @private + * @param {Number} elmIndex + * @param {Array} elmStack + * @param {Element} originalElm + * @return {Number|undefined} */ -color.getRectStack = function(elm) { - let boundingCoords = color.getCoords(elm.getBoundingClientRect()); - if (!boundingCoords) { - return null; - } - - let boundingStack = dom.shadowElementsFromPoint( - boundingCoords.x, - boundingCoords.y - ); - - let rects = Array.from(elm.getClientRects()); - // If the element does not have multiple rects, like for display:block, return a single stack - if (!rects || rects.length <= 1) { - return [boundingStack]; - } - - // Handle inline elements spanning multiple lines to be evaluated - let filteredArr = rects - .filter(rect => { - // exclude manual line breaks in Chrome/Safari - return rect.width && rect.width > 0; - }) - .map(rect => { - let coords = color.getCoords(rect); - if (coords) { - return dom.shadowElementsFromPoint(coords.x, coords.y); - } - }); - - if (filteredArr.some(stack => stack === undefined)) { - // Can be happen when one or more of the rects sits outside the viewport - return null; - } +function calculateObscuringAlpha(elmIndex, elmStack, originalElm) { + var totalAlpha = 0; - // add bounding client rect stack for comparison later - filteredArr.splice(0, 0, boundingStack); - return filteredArr; -}; -/** - * Get filtered stack of block and inline elements, excluding line breaks - * @method filteredRectStack - * @memberof axe.commons.color - * @instance - * @param {Element} elm - * @return {Array} - */ -color.filteredRectStack = function(elm) { - let rectStack = color.getRectStack(elm); - if (rectStack && rectStack.length === 1) { - // default case, elm.getBoundingClientRect() - return rectStack[0]; - } else if (rectStack && rectStack.length > 1) { - let boundingStack = rectStack.shift(); - let isSame; - // iterating over arrays of DOMRects - rectStack.forEach((rectList, index) => { - if (index === 0) { - return; + if (elmIndex > 0) { + // there are elements above our element, check if they contribute to the background + for (var i = elmIndex - 1; i >= 0; i--) { + let bgElm = elmStack[i]; + let bgElmStyle = window.getComputedStyle(bgElm); + let bgColor = color.getOwnBackgroundColor(bgElmStyle); + if (bgColor.alpha && contentOverlapping(originalElm, bgElm)) { + totalAlpha += bgColor.alpha; + } else { + // remove elements not contributing to the background + elmStack.splice(i, 1); } - // if the stacks are the same, use the first one. otherwise, return null. - let rectA = rectStack[index - 1], - rectB = rectStack[index]; - - // if elements in clientRects are the same - // or the boundingClientRect contains the differing element, pass it - isSame = - rectA.every(function(element, elementIndex) { - return element === rectB[elementIndex]; - }) || boundingStack.includes(elm); - }); - if (!isSame) { - axe.commons.color.incompleteData.set('bgColor', 'elmPartiallyObscuring'); - return null; } - // pass the first stack if it wasn't partially covered - return rectStack[0]; - } else { - // rect outside of viewport - axe.commons.color.incompleteData.set('bgColor', 'outsideViewport'); - return null; } -}; -/** - * Get all elements rendered underneath the current element, In the order they are displayed (front to back) - * @method getBackgroundStack - * @memberof axe.commons.color - * @instance - * @param {Element} elm - * @return {Array} - */ -color.getBackgroundStack = function(elm) { - let elmStack = color.filteredRectStack(elm); - if (elmStack === null) { - return null; - } - elmStack = includeMissingElements(elmStack, elm); - elmStack = dom.reduceToElementsBelowFloating(elmStack, elm); - elmStack = sortPageBackground(elmStack); - - // Return all elements BELOW the current element, null if the element is undefined - let elmIndex = elmStack.indexOf(elm); - if (calculateObscuringAlpha(elmIndex, elmStack, elm) >= 0.99) { - // if the total of the elements above our element results in total obscuring, return null - axe.commons.color.incompleteData.set('bgColor', 'bgOverlap'); - return null; - } - return elmIndex !== -1 ? elmStack : null; -}; + return totalAlpha; +} /** - * Returns background color for element - * Uses color.getBackgroundStack() to get all elements rendered underneath the current element to - * help determine the background color. - * @param {Element} elm Element to determine background color - * @param {Array} [bgElms=[]] [description] - * @param {Boolean} [noScroll=false] [description] - * @return {Color} [description] + * Determines overlap of node's content with a bgNode. Used for inline elements + * @private + * @param {Element} targetElement + * @param {Element} bgNode + * @return {Boolean} */ -color.getBackgroundColor = function(elm, bgElms = [], noScroll = false) { - if (noScroll !== true) { - // Avoid scrolling overflow:hidden containers, by only aligning to top - // when not doing so would move the center point above the viewport top. - const clientHeight = elm.getBoundingClientRect().height; - const alignToTop = clientHeight - 2 >= window.innerHeight * 2; - elm.scrollIntoView(alignToTop); - } - let bgColors = []; - let elmStack = color.getBackgroundStack(elm); - - // Search the stack until we have an alpha === 1 background - (elmStack || []).some(bgElm => { - let bgElmStyle = window.getComputedStyle(bgElm); - - // Get the background color - let bgColor = getBgColor(bgElm, bgElmStyle); - - if ( - // abort if a node is partially obscured and obscuring element has a background - elmPartiallyObscured(elm, bgElm, bgColor) || - // OR if the background elm is a graphic - elmHasImage(bgElm, bgElmStyle) - ) { - bgColors = null; - bgElms.push(bgElm); - - return true; - } - - if (bgColor.alpha !== 0) { - // store elements contributing to the br color. - bgElms.push(bgElm); - bgColors.push(bgColor); - - // Exit if the background is opaque - return bgColor.alpha === 1; - } else { - return false; +function contentOverlapping(targetElement, bgNode) { + // get content box of target element + // check to see if the current bgNode is overlapping + var targetRect = targetElement.getClientRects()[0]; + var obscuringElements = dom.shadowElementsFromPoint( + targetRect.left, + targetRect.top + ); + if (obscuringElements) { + for (var i = 0; i < obscuringElements.length; i++) { + if ( + obscuringElements[i] !== targetElement && + obscuringElements[i] === bgNode + ) { + return true; + } } - }); - - if (bgColors !== null && elmStack !== null) { - // Mix the colors together, on top of a default white - bgColors.push(new color.Color(255, 255, 255, 1)); - var colors = bgColors.reduce(color.flattenColors); - return colors; } - - return null; -}; + return false; +} /** * Determines whether an element has a fully opaque background, whether solid color or an image @@ -406,6 +362,9 @@ color.getBackgroundColor = function(elm, bgElms = [], noScroll = false) { * @return {Boolean} false if the background is transparent, true otherwise */ dom.isOpaque = function(node) { - let style = window.getComputedStyle(node); - return elmHasImage(node, style) || getBgColor(node, style).alpha === 1; + const style = window.getComputedStyle(node); + return ( + color.elementHasImage(node, style) || + color.getOwnBackgroundColor(style).alpha === 1 + ); }; diff --git a/lib/commons/color/get-own-background-color.js b/lib/commons/color/get-own-background-color.js new file mode 100644 index 0000000000..ad63d25def --- /dev/null +++ b/lib/commons/color/get-own-background-color.js @@ -0,0 +1,22 @@ +/* global color */ + +/** + * Returns the non-alpha-blended background color of an element + * + * @method getOwnBackgroundColor + * @memberof axe.commons.color + * + * @param {Object} elmStyle style of the element + * @return {Color} + */ +color.getOwnBackgroundColor = function getOwnBackgroundColor(elmStyle) { + const bgColor = new color.Color(); + bgColor.parseRgbString(elmStyle.getPropertyValue('background-color')); + + if (bgColor.alpha !== 0) { + const opacity = elmStyle.getPropertyValue('opacity'); + bgColor.alpha = bgColor.alpha * opacity; + } + + return bgColor; +}; diff --git a/lib/commons/dom/find-up.js b/lib/commons/dom/find-up.js index dee05d72fd..da02d5c5e0 100644 --- a/lib/commons/dom/find-up.js +++ b/lib/commons/dom/find-up.js @@ -11,10 +11,7 @@ * @return {HTMLElement|null} Either the matching HTMLElement or `null` if there was no match */ dom.findUp = function(element, target) { - return dom.findUpVirtual( - axe.utils.getNodeFromTree(axe._tree[0], element), - target - ); + return dom.findUpVirtual(axe.utils.getNodeFromTree(element), target); }; /** @@ -29,7 +26,6 @@ dom.findUp = function(element, target) { * @return {HTMLElement|null} Either the matching HTMLElement or `null` if there was no match */ dom.findUpVirtual = function(element, target) { - /*eslint complexity: ["error", 12]*/ let parent; parent = element.actualNode; diff --git a/lib/commons/dom/get-scroll-offset.js b/lib/commons/dom/get-scroll-offset.js index e904fe2b8e..e2137b0cb2 100644 --- a/lib/commons/dom/get-scroll-offset.js +++ b/lib/commons/dom/get-scroll-offset.js @@ -1,5 +1,4 @@ /* global dom */ -/*eslint complexity: ["error", 14]*/ /** * Get the scroll offset of the document passed in * @method getScrollOffset diff --git a/lib/commons/dom/has-content-virtual.js b/lib/commons/dom/has-content-virtual.js index 450e25b12e..9b07d1e4b8 100644 --- a/lib/commons/dom/has-content-virtual.js +++ b/lib/commons/dom/has-content-virtual.js @@ -59,7 +59,7 @@ dom.hasContentVirtual = function(elm, noRecursion) { * @return {Boolean} */ dom.hasContent = function hasContent(elm, noRecursion) { - elm = axe.utils.getNodeFromTree(axe._tree[0], elm); + elm = axe.utils.getNodeFromTree(elm); return dom.hasContentVirtual(elm, noRecursion); }; diff --git a/lib/commons/dom/is-focusable.js b/lib/commons/dom/is-focusable.js index 725b155052..01f8590c93 100644 --- a/lib/commons/dom/is-focusable.js +++ b/lib/commons/dom/is-focusable.js @@ -1,5 +1,4 @@ /* global dom */ -/* eslint complexity: ["error",20] */ /** * Determines if focusing has been disabled on an element. diff --git a/lib/commons/dom/is-hidden-with-css.js b/lib/commons/dom/is-hidden-with-css.js index 7665ffa87a..7c0d2f15e3 100644 --- a/lib/commons/dom/is-hidden-with-css.js +++ b/lib/commons/dom/is-hidden-with-css.js @@ -1,4 +1,3 @@ -/* eslint complexity: ["error", 13], max-statements: ["error", 25] */ /* global dom */ /** diff --git a/lib/commons/dom/is-in-text-block.js b/lib/commons/dom/is-in-text-block.js index ad1e30ac6a..8968b8de77 100644 --- a/lib/commons/dom/is-in-text-block.js +++ b/lib/commons/dom/is-in-text-block.js @@ -17,7 +17,7 @@ function getBlockParent(node) { while (parentBlock && !isBlock(parentBlock)) { parentBlock = dom.getComposedParent(parentBlock); } - return axe.utils.getNodeFromTree(axe._tree[0], parentBlock); + return axe.utils.getNodeFromTree(parentBlock); } /** @@ -26,7 +26,6 @@ function getBlockParent(node) { * @return {Boolean} [description] */ dom.isInTextBlock = function isInTextBlock(node) { - /* eslint complexity: ["error",17]*/ if (isBlock(node)) { // Ignore if the link is a block return false; diff --git a/lib/commons/dom/is-skip-link.js b/lib/commons/dom/is-skip-link.js new file mode 100644 index 0000000000..e45525f5a6 --- /dev/null +++ b/lib/commons/dom/is-skip-link.js @@ -0,0 +1,45 @@ +/* global dom */ + +// test for hrefs that start with # or /# (for angular) +const isInternalLinkRegex = /^\/?#[^/!]/; + +/** + * Determines if element is a skip link + * @method isSkipLink + * @memberof axe.commons.dom + * @instance + * @param {Element} element + * @return {Boolean} + */ +dom.isSkipLink = function(element) { + if (!isInternalLinkRegex.test(element.getAttribute('href'))) { + return false; + } + + let firstPageLink; + if (typeof axe._cache.firstPageLink !== 'undefined') { + firstPageLink = axe._cache.firstPageLink; + } else { + // define a skip link as any anchor element whose href starts with `#...` + // and which precedes the first anchor element whose href doesn't start + // with `#...` (that is, a link to a page) + firstPageLink = axe.utils.querySelectorAll( + axe._tree, + 'a:not([href^="#"]):not([href^="/#"]):not([href^="javascript"])' + )[0]; + + // null will signify no first page link + axe._cache.firstPageLink = firstPageLink || null; + } + + // if there are no page links then all all links will need to be + // considered as skip links + if (!firstPageLink) { + return true; + } + + return ( + element.compareDocumentPosition(firstPageLink.actualNode) === + element.DOCUMENT_POSITION_FOLLOWING + ); +}; diff --git a/lib/commons/dom/is-visible.js b/lib/commons/dom/is-visible.js index 1b92751f98..ee15196e41 100644 --- a/lib/commons/dom/is-visible.js +++ b/lib/commons/dom/is-visible.js @@ -1,5 +1,4 @@ /* global dom */ -/* eslint complexity: ["error", 20] */ /** * Determines if an element is hidden with the clip rect technique @@ -33,9 +32,9 @@ function isClipped(clip) { * @return {Boolean} The element's visibilty status */ dom.isVisible = function(el, screenReader, recursed) { - //eslint complexity: 13 'use strict'; - var style, nodeName, parent; + const node = axe.utils.getNodeFromTree(el); + const cacheName = '_isVisible' + (screenReader ? 'ScreenReader' : ''); // 9 === Node.DOCUMENT if (el.nodeType === 9) { @@ -47,12 +46,16 @@ dom.isVisible = function(el, screenReader, recursed) { el = el.host; // grab the host Node } - style = window.getComputedStyle(el, null); + if (node && typeof node[cacheName] !== 'undefined') { + return node[cacheName]; + } + + const style = window.getComputedStyle(el, null); if (style === null) { return false; } - nodeName = el.nodeName.toUpperCase(); + const nodeName = el.nodeName.toUpperCase(); if ( style.getPropertyValue('display') === 'none' || @@ -68,10 +71,15 @@ dom.isVisible = function(el, screenReader, recursed) { return false; } - parent = el.assignedSlot ? el.assignedSlot : el.parentNode; + const parent = el.assignedSlot ? el.assignedSlot : el.parentNode; + let isVisible = false; if (parent) { - return dom.isVisible(parent, screenReader, true); + isVisible = dom.isVisible(parent, screenReader, true); } - return false; + if (node) { + node[cacheName] = isVisible; + } + + return isVisible; }; diff --git a/lib/commons/dom/is-visual-content.js b/lib/commons/dom/is-visual-content.js index 832ac53aa7..61d8026d00 100644 --- a/lib/commons/dom/is-visual-content.js +++ b/lib/commons/dom/is-visual-content.js @@ -1,5 +1,4 @@ /*global dom */ -/*eslint complexity: ["error",20] */ const visualRoles = [ 'checkbox', diff --git a/lib/commons/dom/visually-contains.js b/lib/commons/dom/visually-contains.js index 9939012ab7..da9cd6f248 100644 --- a/lib/commons/dom/visually-contains.js +++ b/lib/commons/dom/visually-contains.js @@ -1,5 +1,4 @@ /* global dom */ -/* eslint complexity: ["error",17] */ /** * Checks whether a parent element visually contains its child, either directly or via scrolling. diff --git a/lib/commons/dom/visually-overlaps.js b/lib/commons/dom/visually-overlaps.js index 30d4476466..a46354b427 100644 --- a/lib/commons/dom/visually-overlaps.js +++ b/lib/commons/dom/visually-overlaps.js @@ -1,5 +1,4 @@ /* global dom */ -/* eslint complexity: ["error",15] */ /** * Checks whether a parent element visually overlaps a rectangle, either directly or via scrolling. diff --git a/lib/commons/table/is-data-table.js b/lib/commons/table/is-data-table.js index e6914b33c2..5660504731 100644 --- a/lib/commons/table/is-data-table.js +++ b/lib/commons/table/is-data-table.js @@ -1,5 +1,4 @@ /* global table, dom */ -/* eslint max-statements: ["error",70], complexity: ["error",47] */ /** * Determines whether a table is a data table diff --git a/lib/commons/text/accessible-text-virtual.js b/lib/commons/text/accessible-text-virtual.js index 446efdd7a6..99091b4fa3 100644 --- a/lib/commons/text/accessible-text-virtual.js +++ b/lib/commons/text/accessible-text-virtual.js @@ -11,7 +11,7 @@ * @return {string} */ text.accessibleText = function accessibleText(element, context) { - let virtualNode = axe.utils.getNodeFromTree(axe._tree[0], element); // throws an exception on purpose if axe._tree not correct + const virtualNode = axe.utils.getNodeFromTree(element); // throws an exception on purpose if axe._tree not correct return text.accessibleTextVirtual(virtualNode, context); }; diff --git a/lib/commons/text/is-valid-autocomplete.js b/lib/commons/text/is-valid-autocomplete.js index c31d6e7642..886cf7f42f 100644 --- a/lib/commons/text/is-valid-autocomplete.js +++ b/lib/commons/text/is-valid-autocomplete.js @@ -74,7 +74,6 @@ text.isValidAutocomplete = function isValidAutocomplete( qualifiedTerms = [] } = {} ) { - /*eslint max-statements: ["error", 23] */ autocomplete = autocomplete.toLowerCase().trim(); stateTerms = stateTerms.concat(text.autocomplete.stateTerms); if (stateTerms.includes(autocomplete) || autocomplete === '') { diff --git a/lib/commons/text/label-text.js b/lib/commons/text/label-text.js index 8274916559..1954900262 100644 --- a/lib/commons/text/label-text.js +++ b/lib/commons/text/label-text.js @@ -9,7 +9,7 @@ * @return {String} Label text */ text.labelText = function labelText(virtualNode, context = {}) { - const { alreadyProcessed } = text.accessibleTextVirtual + const { alreadyProcessed } = text.accessibleTextVirtual; if ( context.inControlContext || context.inLabelledByContext || diff --git a/lib/commons/text/label-virtual.js b/lib/commons/text/label-virtual.js index 10a5b5f9fb..87319f569c 100644 --- a/lib/commons/text/label-virtual.js +++ b/lib/commons/text/label-virtual.js @@ -48,6 +48,6 @@ text.labelVirtual = function(node) { * @return {Mixed} String of visible text, or `null` if no label is found */ text.label = function(node) { - node = axe.utils.getNodeFromTree(axe._tree[0], node); + node = axe.utils.getNodeFromTree(node); return text.labelVirtual(node); }; diff --git a/lib/commons/text/native-text-methods.js b/lib/commons/text/native-text-methods.js index a4a14d09ad..f588cf3232 100644 --- a/lib/commons/text/native-text-methods.js +++ b/lib/commons/text/native-text-methods.js @@ -95,7 +95,7 @@ text.nativeTextMethods = { * @return {String} Returns ` ` */ singleSpace: function singleSpace() { - return ' ' + return ' '; } }; diff --git a/lib/commons/text/subtree-text.js b/lib/commons/text/subtree-text.js index f1c4c38b75..5818a02d8a 100644 --- a/lib/commons/text/subtree-text.js +++ b/lib/commons/text/subtree-text.js @@ -8,11 +8,11 @@ * @return {String} Accessible text */ text.subtreeText = function subtreeText(virtualNode, context = {}) { - const { alreadyProcessed } = text.accessibleTextVirtual + const { alreadyProcessed } = text.accessibleTextVirtual; context.startNode = context.startNode || virtualNode; const { strict } = context; if ( - alreadyProcessed(virtualNode, context ) || + alreadyProcessed(virtualNode, context) || !aria.namedFromContents(virtualNode, { strict }) ) { return ''; @@ -72,7 +72,7 @@ function appendAccessibleText(contentText, virtualNode, context) { const nodeName = virtualNode.actualNode.nodeName.toUpperCase(); let contentTextAdd = text.accessibleTextVirtual(virtualNode, context); if (!contentTextAdd) { - return contentText + return contentText; } if (!phrasingElements.includes(nodeName)) { diff --git a/lib/commons/text/visible-virtual.js b/lib/commons/text/visible-virtual.js index f591d32c6f..5c62337d8b 100644 --- a/lib/commons/text/visible-virtual.js +++ b/lib/commons/text/visible-virtual.js @@ -41,6 +41,6 @@ text.visibleVirtual = function(element, screenReader, noRecursing) { * @return {String} */ text.visible = function(element, screenReader, noRecursing) { - element = axe.utils.getNodeFromTree(axe._tree[0], element); + element = axe.utils.getNodeFromTree(element); return text.visibleVirtual(element, screenReader, noRecursing); }; diff --git a/lib/core/base/audit.js b/lib/core/base/audit.js index 2cb572dd50..bad1ebb8b0 100644 --- a/lib/core/base/audit.js +++ b/lib/core/base/audit.js @@ -526,9 +526,9 @@ Audit.prototype.after = function(results, options) { return results.map(function(ruleResult) { var rule = axe.utils.findBy(rules, 'id', ruleResult.id); if (!rule) { - // If you see this, you're probably running the Mocha tests with the aXe extension installed + // If you see this, you're probably running the Mocha tests with the axe extension installed throw new Error( - 'Result for unknown rule. You may be running mismatch aXe-core versions' + 'Result for unknown rule. You may be running mismatch axe-core versions' ); } @@ -552,7 +552,6 @@ Audit.prototype.getRule = function(ruleId) { * @return {Object} Validated options object */ Audit.prototype.normalizeOptions = function(options) { - /* eslint max-statements: ["error", 22] */ 'use strict'; var audit = this; diff --git a/lib/core/base/check.js b/lib/core/base/check.js index 306dfadff2..967eaadfc7 100644 --- a/lib/core/base/check.js +++ b/lib/core/base/check.js @@ -58,7 +58,6 @@ Check.prototype.enabled = true; * @param {Function} callback Function to fire when check is complete */ Check.prototype.run = function(node, options, context, resolve, reject) { - /* eslint max-statements: ["error", 17] */ 'use strict'; options = options || {}; var enabled = options.hasOwnProperty('enabled') diff --git a/lib/core/base/context.js b/lib/core/base/context.js index d87e76c925..3d05dcd939 100644 --- a/lib/core/base/context.js +++ b/lib/core/base/context.js @@ -68,7 +68,6 @@ function pushUniqueFrameSelector(context, type, selectorArray) { * @return {Object} Normalized context spec to include both `include` and `exclude` arrays */ function normalizeContext(context) { - /*eslint complexity: ["error", 13] */ 'use strict'; // typeof NodeList.length in PhantomJS === function @@ -135,7 +134,7 @@ function parseSelectorArray(context, type) { //eslint no-loop-func:0 result = result.concat( nodeList.map(node => { - return axe.utils.getNodeFromTree(context.flatTree[0], node); + return axe.utils.getNodeFromTree(node); }) ); break; @@ -147,7 +146,7 @@ function parseSelectorArray(context, type) { //eslint no-loop-func:0 result = result.concat( nodeList.map(node => { - return axe.utils.getNodeFromTree(context.flatTree[0], node); + return axe.utils.getNodeFromTree(node); }) ); } @@ -155,7 +154,7 @@ function parseSelectorArray(context, type) { if (item.documentElement instanceof Node) { result.push(context.flatTree[0]); } else { - result.push(axe.utils.getNodeFromTree(context.flatTree[0], item)); + result.push(axe.utils.getNodeFromTree(item)); } } } @@ -230,7 +229,7 @@ function getRootNode({ include, exclude }) { * @param {Object} spec Configuration or "specification" object */ function Context(spec) { - /* eslint max-statements:["error",22], no-unused-vars:0 */ + /* eslint no-unused-vars:0 */ 'use strict'; this.frames = []; diff --git a/lib/core/base/rule.js b/lib/core/base/rule.js index db6bc6b88b..45aebccc34 100644 --- a/lib/core/base/rule.js +++ b/lib/core/base/rule.js @@ -1,7 +1,6 @@ /*global RuleResult, createExecutionContext, SupportError */ function Rule(spec, parentAudit) { - /*eslint complexity: ["error", 11] */ 'use strict'; this._audit = parentAudit; @@ -90,16 +89,48 @@ Rule.prototype.matches = function() { /** * Selects `HTMLElement`s based on configured selector * @param {Context} context The resolved Context object + * @param {Mixed} options Options specific to this rule * @return {Array} All matching `HTMLElement`s */ -Rule.prototype.gather = function(context) { - 'use strict'; +Rule.prototype.gather = function(context, options = {}) { + const markStart = 'mark_gather_start_' + this.id; + const markEnd = 'mark_gather_end_' + this.id; + const markHiddenStart = 'mark_isHidden_start_' + this.id; + const markHiddenEnd = 'mark_isHidden_end_' + this.id; + + if (options.performanceTimer) { + axe.utils.performanceTimer.mark(markStart); + } + var elements = axe.utils.select(this.selector, context); if (this.excludeHidden) { - return elements.filter(function(element) { + if (options.performanceTimer) { + axe.utils.performanceTimer.mark(markHiddenStart); + } + + elements = elements.filter(function(element) { return !axe.utils.isHidden(element.actualNode); }); + + if (options.performanceTimer) { + axe.utils.performanceTimer.mark(markHiddenEnd); + axe.utils.performanceTimer.measure( + 'rule_' + this.id + '#gather_axe.utils.isHidden', + markHiddenStart, + markHiddenEnd + ); + } } + + if (options.performanceTimer) { + axe.utils.performanceTimer.mark(markEnd); + axe.utils.performanceTimer.measure( + 'rule_' + this.id + '#gather', + markStart, + markEnd + ); + } + return elements; }; @@ -142,8 +173,6 @@ Rule.prototype.runChecks = function( * @param {Function} callback Function to call when evaluate is complete; receives a RuleResult instance */ Rule.prototype.run = function(context, options, resolve, reject) { - /*eslint max-statements: ["error",17] */ - const q = axe.utils.queue(); const ruleResult = new RuleResult(this); const markStart = 'mark_rule_start_' + this.id; @@ -155,9 +184,7 @@ Rule.prototype.run = function(context, options, resolve, reject) { try { // Matches throws an error when it lacks support for document methods - nodes = this.gather(context).filter(node => - this.matches(node.actualNode, node, context) - ); + nodes = this.gatherAndMatchNodes(context, options); } catch (error) { // Exit the rule execution if matches fails reject(new SupportError({ cause: error, ruleId: this.id })); @@ -166,6 +193,7 @@ Rule.prototype.run = function(context, options, resolve, reject) { if (options.performanceTimer) { axe.log( + this.id, 'gather (', nodes.length, '):', @@ -217,7 +245,7 @@ Rule.prototype.run = function(context, options, resolve, reject) { axe.utils.performanceTimer.mark(markChecksEnd); axe.utils.performanceTimer.mark(markEnd); axe.utils.performanceTimer.measure( - 'runchecks_' + this.id, + 'rule_' + this.id + '#runchecks', markChecksStart, markChecksEnd ); @@ -228,6 +256,38 @@ Rule.prototype.run = function(context, options, resolve, reject) { q.then(() => resolve(ruleResult)).catch(error => reject(error)); }; +/** + * Selects `HTMLElement`s based on configured selector and filters them based on + * the rules matches function + * @param {Rule} rule The rule to check for after checks + * @param {Context} context The resolved Context object + * @param {Mixed} options Options specific to this rule + * @return {Array} All matching `HTMLElement`s + */ +Rule.prototype.gatherAndMatchNodes = function(context, options) { + const markMatchesStart = 'mark_matches_start_' + this.id; + const markMatchesEnd = 'mark_matches_end_' + this.id; + + let nodes = this.gather(context, options); + + if (options.performanceTimer) { + axe.utils.performanceTimer.mark(markMatchesStart); + } + + nodes = nodes.filter(node => this.matches(node.actualNode, node, context)); + + if (options.performanceTimer) { + axe.utils.performanceTimer.mark(markMatchesEnd); + axe.utils.performanceTimer.measure( + 'rule_' + this.id + '#matches', + markMatchesStart, + markMatchesEnd + ); + } + + return nodes; +}; + /** * Iterates the rule's Checks looking for ones that have an after function * @private @@ -336,7 +396,7 @@ Rule.prototype.after = function(result, options) { * @param {Object} spec - the attributes to be reconfigured */ Rule.prototype.configure = function(spec) { - /*eslint complexity:["error",14], max-statements:["error",22], no-eval:0 */ + /*eslint no-eval:0 */ 'use strict'; if (spec.hasOwnProperty('selector')) { diff --git a/lib/core/imports/index.js b/lib/core/imports/index.js index 6e82f39c2f..b49e962827 100644 --- a/lib/core/imports/index.js +++ b/lib/core/imports/index.js @@ -10,7 +10,15 @@ * Polyfill `Promise` * Reference: https://www.npmjs.com/package/es6-promise */ -require('es6-promise').polyfill(); +if (!('Promise' in window)) { + require('es6-promise').polyfill(); +} + +/** + * Polyfill `WeakMap` + * Reference: https://github.com/polygonplanet/weakmap-polyfill + */ +require('weakmap-polyfill'); /** * Namespace `axe.imports` which holds required external dependencies diff --git a/lib/core/index.js b/lib/core/index.js index 7628e88d1d..90d7826464 100644 --- a/lib/core/index.js +++ b/lib/core/index.js @@ -1,6 +1,6 @@ /*exported axe, commons */ /*global axeFunction, module, define */ -// exported namespace for aXe +// exported namespace for axe /*eslint no-use-before-define: 0, no-unused-vars: 0*/ var axe = axe || {}; axe.version = '<%= pkg.version %>'; diff --git a/lib/core/public/configure.js b/lib/core/public/configure.js index 029a9b2ba2..8e13f757e3 100644 --- a/lib/core/public/configure.js +++ b/lib/core/public/configure.js @@ -1,6 +1,5 @@ /* global reporters */ function configureChecksRulesAndBranding(spec) { - /*eslint max-statements: ["error",21]*/ 'use strict'; var audit; diff --git a/lib/core/public/run-rules.js b/lib/core/public/run-rules.js index 173b8aaaef..d740531371 100644 --- a/lib/core/public/run-rules.js +++ b/lib/core/public/run-rules.js @@ -3,6 +3,7 @@ // Clean up after resolve / reject function cleanup() { + axe._cache = undefined; axe._tree = undefined; axe._selectorData = undefined; } @@ -18,6 +19,10 @@ function cleanup() { function runRules(context, options, resolve, reject) { 'use strict'; try { + axe._cache = { + nodeMap: new WeakMap() + }; + context = new Context(context); axe._tree = context.flatTree; axe._selectorData = axe.utils.getSelectorData(context.flatTree); diff --git a/lib/core/public/run.js b/lib/core/public/run.js index 853bc011e3..68014c38e8 100644 --- a/lib/core/public/run.js +++ b/lib/core/public/run.js @@ -1,4 +1,3 @@ -/*eslint indent: 0, complexity:["error", 12]*/ function isContext(potential) { 'use strict'; switch (true) { @@ -81,7 +80,6 @@ function normalizeRunParams(context, options, callback) { * @return {Promise} Resolves with the axe results. Only available when natively supported */ axe.run = function(context, options, callback) { - /*eslint max-statements:["error",18] */ 'use strict'; if (!axe._audit) { throw new Error('No audit configured'); diff --git a/lib/core/reporters/helpers/process-aggregate.js b/lib/core/reporters/helpers/process-aggregate.js index 75218c7a0b..99331fea28 100644 --- a/lib/core/reporters/helpers/process-aggregate.js +++ b/lib/core/reporters/helpers/process-aggregate.js @@ -40,7 +40,7 @@ var resultKeys = axe.constants.resultGroups; */ /** - * Aggregrate and process the aXe results, + * Aggregrate and process the axe results, * adding desired data to nodes and relatedNodes in each rule result. * * Prepares result data for reporters. diff --git a/lib/core/reporters/raw.js b/lib/core/reporters/raw.js index d6a41ef958..8ceab31d3a 100644 --- a/lib/core/reporters/raw.js +++ b/lib/core/reporters/raw.js @@ -1,8 +1,32 @@ axe.addReporter('raw', function(results, options, callback) { 'use strict'; + if (typeof options === 'function') { callback = options; options = {}; } - callback(results); + + // Guard against tests which don't pass an array as the first param here. + if (!results || !Array.isArray(results)) { + return callback(results); + } + + const transformedResults = results.map(result => { + const transformedResult = { ...result }; + const types = ['passes', 'violations', 'incomplete', 'inapplicable']; + for (const type of types) { + // Some tests don't include all of the types, so we have to guard against that here. + // TODO: ensure tests always use "proper" results to avoid having these hacks in production code paths. + if (transformedResult[type] && Array.isArray(transformedResult[type])) { + transformedResult[type] = transformedResult[type].map(typeResult => ({ + ...typeResult, + node: typeResult.node.toJSON() + })); + } + } + + return transformedResult; + }); + + callback(transformedResults); }); diff --git a/lib/core/utils/collect-results-from-frames.js b/lib/core/utils/collect-results-from-frames.js index 48f8044cb1..8ad27569a7 100644 --- a/lib/core/utils/collect-results-from-frames.js +++ b/lib/core/utils/collect-results-from-frames.js @@ -40,7 +40,7 @@ axe.utils.sendCommandToFrame = function(node, parameters, resolve, reject) { axe.utils.respondable(win, 'axe.ping', null, undefined, function() { clearTimeout(timeout); - // Give aXe 60s (or user-supplied value) to respond to 'axe.start' + // Give axe 60s (or user-supplied value) to respond to 'axe.start' var frameWaitTime = (parameters.options && parameters.options.frameWaitTime) || 60000; diff --git a/lib/core/utils/css-parser.js b/lib/core/utils/css-parser.js index 2612583fc9..4a7ea1fc7b 100644 --- a/lib/core/utils/css-parser.js +++ b/lib/core/utils/css-parser.js @@ -1,5 +1,7 @@ (function(axe) { var parser = new axe.imports.CssSelectorParser(); + parser.registerSelectorPseudos('not'); parser.registerNestingOperators('>'); + parser.registerAttrEqualityMods('^', '$', '*'); axe.utils.cssParser = parser; })(axe); diff --git a/lib/core/utils/escape-selector.js b/lib/core/utils/escape-selector.js index 14d4707e1f..6c792b0064 100644 --- a/lib/core/utils/escape-selector.js +++ b/lib/core/utils/escape-selector.js @@ -7,8 +7,7 @@ */ axe.utils.escapeSelector = function(value) { 'use strict'; - /*eslint no-bitwise: 0, eqeqeq: 0, complexity: ["error",27], - max-statements:["error", 25], one-var: 0, -W041: 0 */ + /*eslint no-bitwise: 0, eqeqeq: 0, one-var: 0, -W041: 0 */ var string = String(value); var length = string.length; var index = -1; diff --git a/lib/core/utils/flattened-tree.js b/lib/core/utils/flattened-tree.js index fa97ec9150..5012acb2d3 100644 --- a/lib/core/utils/flattened-tree.js +++ b/lib/core/utils/flattened-tree.js @@ -28,10 +28,11 @@ var axe = axe || { utils: {} }; */ function virtualDOMfromNode(node, shadowId) { const vNodeCache = {}; - return { + const vNode = { shadowId: shadowId, children: [], actualNode: node, + _isHidden: null, // will be populated by axe.utils.isHidden get isFocusable() { if (!vNodeCache._isFocusable) { vNodeCache._isFocusable = axe.commons.dom.isFocusable(node); @@ -47,6 +48,8 @@ function virtualDOMfromNode(node, shadowId) { return vNodeCache._tabbableElements; } }; + axe._cache.nodeMap.set(node, vNode); + return vNode; } /** @@ -77,7 +80,6 @@ function getSlotChildren(node) { */ axe.utils.getFlattenedTree = function(node, shadowId) { // using a closure here and therefore cannot easily refactor toreduce the statements - /*eslint max-statements: ["error", 31] */ var retVal, realArray, nodeName; function reduceShadowDOM(res, child) { var replacements = axe.utils.getFlattenedTree(child, shadowId); @@ -145,26 +147,12 @@ axe.utils.getFlattenedTree = function(node, shadowId) { }; /** - * Recursively return a single node from a virtual dom tree + * Return a single node from the virtual dom tree * * @param {Object} vNode The flattened, virtual DOM tree * @param {Node} node The HTML DOM node */ axe.utils.getNodeFromTree = function(vNode, node) { - var found; - - if (vNode.actualNode === node) { - return vNode; - } - vNode.children.forEach(candidate => { - if (found) { - return; - } - if (candidate.actualNode === node) { - found = candidate; - } else { - found = axe.utils.getNodeFromTree(candidate, node); - } - }); - return found; + const el = node || vNode; + return axe._cache.nodeMap.get(el); }; diff --git a/lib/core/utils/get-check-option.js b/lib/core/utils/get-check-option.js index f170996d6b..ffb098afdc 100644 --- a/lib/core/utils/get-check-option.js +++ b/lib/core/utils/get-check-option.js @@ -1,4 +1,3 @@ -/*eslint complexity: ["error", 12]*/ /** * Determines which CheckOption to use, either defined on the rule options, global check options or the check itself * @param {Check} check The Check object diff --git a/lib/core/utils/get-friendly-uri-end.js b/lib/core/utils/get-friendly-uri-end.js index f1c20cf4b5..eb5467fae7 100644 --- a/lib/core/utils/get-friendly-uri-end.js +++ b/lib/core/utils/get-friendly-uri-end.js @@ -1,4 +1,4 @@ -/* eslint max-statements:["error",18], complexity:["error",27], no-script-url:0 */ +/* eslint no-script-url:0 */ /** * Check if a string contains mostly numbers */ diff --git a/lib/core/utils/get-node-attributes.js b/lib/core/utils/get-node-attributes.js new file mode 100644 index 0000000000..675926d955 --- /dev/null +++ b/lib/core/utils/get-node-attributes.js @@ -0,0 +1,21 @@ +/* global axe */ + +/** + * Return the list of attributes of a node. + * @method getNodeAttributes + * @memberof axe.utils + * @param {Element} node + * @returns {NamedNodeMap} + */ +axe.utils.getNodeAttributes = function getNodeAttributes(node) { + // eslint-disable-next-line no-restricted-syntax + if (node.attributes instanceof window.NamedNodeMap) { + // eslint-disable-next-line no-restricted-syntax + return node.attributes; + } + + // if the attributes property is not of type NamedNodeMap then the DOM + // has been clobbered. E.g.
. + // We can clone the node to isolate it and then return the attributes + return node.cloneNode(false).attributes; +}; diff --git a/lib/core/utils/get-selector.js b/lib/core/utils/get-selector.js index e0af47ad0d..afe6dcd183 100644 --- a/lib/core/utils/get-selector.js +++ b/lib/core/utils/get-selector.js @@ -79,7 +79,7 @@ function filterAttributes(at) { * the counts for how many elements with that feature exist */ axe.utils.getSelectorData = function(domTree) { - /* eslint max-statements:["error", 22], no-loop-func:0*/ + /* eslint no-loop-func:0*/ // Initialize the return structure with the three maps let data = { @@ -119,8 +119,8 @@ axe.utils.getSelectorData = function(domTree) { } // count all the filtered attributes - if (node.attributes) { - Array.from(node.attributes) + if (node.hasAttributes()) { + Array.from(axe.utils.getNodeAttributes(node)) .filter(filterAttributes) .forEach(at => { let atnv = getAttributeNameValue(node, at); @@ -238,8 +238,8 @@ function uncommonAttributes(node, selectorData) { let attData = selectorData.attributes; let tagData = selectorData.tags; - if (node.attributes) { - Array.from(node.attributes) + if (node.hasAttributes()) { + Array.from(axe.utils.getNodeAttributes(node)) .filter(filterAttributes) .forEach(at => { const atnv = getAttributeNameValue(node, at); @@ -334,7 +334,7 @@ function getThreeLeastCommonFeatures(elm, selectorData) { */ function generateSelector(elm, options, doc) { - /*eslint max-statements:["error", 22], no-loop-func:0*/ + /*eslint no-loop-func:0*/ if (!axe._selectorData) { throw new Error('Expect axe._selectorData to be set up'); } diff --git a/lib/core/utils/get-xpath.js b/lib/core/utils/get-xpath.js index 15f87721e6..b24afa80cf 100644 --- a/lib/core/utils/get-xpath.js +++ b/lib/core/utils/get-xpath.js @@ -1,6 +1,5 @@ /*global axe */ -/*eslint max-statements: ["error", 36], complexity: ["error", 22]*/ function getXPathArray(node, path) { var sibling, count; // Gets an XPath for an element which describes its hierarchical location. diff --git a/lib/core/utils/is-hidden.js b/lib/core/utils/is-hidden.js index d56a531f44..82e93c937a 100644 --- a/lib/core/utils/is-hidden.js +++ b/lib/core/utils/is-hidden.js @@ -8,7 +8,7 @@ */ axe.utils.isHidden = function isHidden(el, recursed) { 'use strict'; - var parent; + const node = axe.utils.getNodeFromTree(el); // 9 === Node.DOCUMENT if (el.nodeType === 9) { @@ -20,7 +20,11 @@ axe.utils.isHidden = function isHidden(el, recursed) { el = el.host; // grab the host Node } - var style = window.getComputedStyle(el, null); + if (node && node._isHidden !== null) { + return node._isHidden; + } + + const style = window.getComputedStyle(el, null); if ( !style || @@ -34,7 +38,15 @@ axe.utils.isHidden = function isHidden(el, recursed) { return true; } - parent = el.assignedSlot ? el.assignedSlot : el.parentNode; + const parent = el.assignedSlot ? el.assignedSlot : el.parentNode; + const isHidden = axe.utils.isHidden(parent, true); + + // cache the results of the isHidden check on the parent tree + // so we don't have to look at the parent tree again for all its + // descendants + if (node) { + node._isHidden = isHidden; + } - return axe.utils.isHidden(parent, true); + return isHidden; }; diff --git a/lib/core/utils/performance-timer.js b/lib/core/utils/performance-timer.js index a69123d9b1..f4f3136b94 100644 --- a/lib/core/utils/performance-timer.js +++ b/lib/core/utils/performance-timer.js @@ -27,7 +27,7 @@ utils.performanceTimer = (function() { */ return { /** - * @member {Function} start Kicks off performance timing for overall aXe audit + * @member {Function} start Kicks off performance timing for overall axe audit */ start: function() { this.mark('mark_axe_start'); @@ -90,7 +90,12 @@ utils.performanceTimer = (function() { window.performance && window.performance.getEntriesByType !== undefined ) { - var measures = window.performance.getEntriesByType('measure'); + // only output measures that were started after axe started, otherwise + // we get measures made by the page before axe ran (which is confusing) + var axeStart = window.performance.getEntriesByName('mark_axe_start')[0]; + var measures = window.performance + .getEntriesByType('measure') + .filter(measure => measure.startTime >= axeStart.startTime); for (var i = 0; i < measures.length; ++i) { var req = measures[i]; if (req.name === measureName) { diff --git a/lib/core/utils/preload-cssom.js b/lib/core/utils/preload-cssom.js index c95f12181c..0b06225546 100644 --- a/lib/core/utils/preload-cssom.js +++ b/lib/core/utils/preload-cssom.js @@ -1,10 +1,9 @@ /** - * NOTE: + * NOTE: * this `eslint` rule is disabled because of calling `getStyleSheetFactory` before it is defined (further below). */ /* eslint no-use-before-define: 0 */ - /** * Given a rootNode - construct CSSOM * -> get all source nodes (document & document fragments) within given root node @@ -33,7 +32,7 @@ axe.utils.preloadCssom = function preloadCssom({ return q; } - const dynamicDoc = document.implementation.createHTMLDocument(); + const dynamicDoc = document.implementation.createHTMLDocument('New Document'); const convertDataToStylesheet = getStyleSheetFactory(dynamicDoc); q.defer((resolve, reject) => { diff --git a/lib/core/utils/qsa.js b/lib/core/utils/qsa.js index eef976e80c..2139cb9d0f 100644 --- a/lib/core/utils/qsa.js +++ b/lib/core/utils/qsa.js @@ -66,13 +66,11 @@ var escapeRegExp = (function() { var reUnescape = /\\/g; function convertAttributes(atts) { - /*eslint indent:0*/ /*! Credit Mootools Copyright Mootools, MIT License */ if (!atts) { return; } return atts.map(att => { - // eslint complexity:["error", 13] var attributeKey = att.name.replace(reUnescape, ''); var attributeValue = (att.value || '').replace(reUnescape, ''); var test, regexp; @@ -154,7 +152,7 @@ function convertPseudos(pseudos) { var expressions; if (p.name === 'not') { - expressions = axe.utils.cssParser.parse(p.value); + expressions = p.value; expressions = expressions.selectors ? expressions.selectors : [expressions]; @@ -218,7 +216,6 @@ function matchesSelector(node, exp) { } matchExpressions = function(domTree, expressions, recurse, filter) { - /*eslint max-statements:["error", 34], complexity:["error", 22]*/ let stack = []; let nodes = Array.isArray(domTree) ? domTree : [domTree]; let currentLevel = createLocalVariables( diff --git a/lib/core/utils/respondable.js b/lib/core/utils/respondable.js index de5b7ae0c5..a72a5c2998 100644 --- a/lib/core/utils/respondable.js +++ b/lib/core/utils/respondable.js @@ -13,7 +13,7 @@ ]); /** - * get the unique string to be used to identify our instance of aXe + * get the unique string to be used to identify our instance of axe * @private */ function _getSource() { @@ -206,7 +206,6 @@ window.addEventListener( 'message', function(e) { - /* eslint max-statements: ["error", 20]*/ var data = parseMessage(e.data); if (!data) { return; diff --git a/lib/core/utils/rule-should-run.js b/lib/core/utils/rule-should-run.js index a8456097b6..25037d7d7b 100644 --- a/lib/core/utils/rule-should-run.js +++ b/lib/core/utils/rule-should-run.js @@ -5,7 +5,6 @@ * @param {object} runOnly Value of runOnly with type=tags * @return {bool} */ -/* eslint complexity: ["error", 14]*/ function matchTags(rule, runOnly) { 'use strict'; var include, exclude, matching; diff --git a/lib/core/utils/scroll-state.js b/lib/core/utils/scroll-state.js index cb3151dff9..db24ca5c12 100644 --- a/lib/core/utils/scroll-state.js +++ b/lib/core/utils/scroll-state.js @@ -20,7 +20,7 @@ function getScroll(elm) { */ function setScroll(elm, top, left) { if (elm === window) { - return elm.scroll(top, left); + return elm.scroll(left, top); } else { elm.scrollTop = top; elm.scrollLeft = left; diff --git a/lib/core/utils/select.js b/lib/core/utils/select.js index 422787f353..76f0070d87 100644 --- a/lib/core/utils/select.js +++ b/lib/core/utils/select.js @@ -100,7 +100,6 @@ function reduceIncludes(includes) { * @param {Context} context The "resolved" context object, @see Context * @return {Array} Matching virtual DOM nodes sorted by DOM order */ -/*eslint max-statements:["error", 20]*/ axe.utils.select = function select(selector, context) { 'use strict'; diff --git a/lib/core/utils/uuid.js b/lib/core/utils/uuid.js index ceff9d9938..af00cf4fbb 100644 --- a/lib/core/utils/uuid.js +++ b/lib/core/utils/uuid.js @@ -1,5 +1,4 @@ -/*eslint no-bitwise: 0, eqeqeq: 0, curly: 0, strict: 0, no-eq-null: 0, -max-statements: 0, complexity: 0, no-shadow: 0, no-undef: 0 */ +/*eslint no-bitwise: 0, eqeqeq: 0, curly: 0, strict: 0, no-eq-null: 0, no-shadow: 0, no-undef: 0 */ // uuid.js // // Copyright (c) 2010-2012 Robert Kieffer diff --git a/lib/intro.stub b/lib/intro.stub index 7563c112ef..6d76fb1e4c 100644 --- a/lib/intro.stub +++ b/lib/intro.stub @@ -1,4 +1,4 @@ -/*! aXe v<%= pkg.version %> +/*! axe v<%= pkg.version %> * Copyright (c) <%= grunt.template.today("yyyy") %> Deque Systems, Inc. * * Your use of this Source Code Form is subject to the terms of the Mozilla Public diff --git a/lib/misc/incomplete-fallback.json b/lib/misc/incomplete-fallback.json index 4db8db68e7..f8a8c7bac5 100644 --- a/lib/misc/incomplete-fallback.json +++ b/lib/misc/incomplete-fallback.json @@ -1,3 +1,3 @@ { - "incompleteFallbackMessage": "aXe couldn't tell the reason. Time to break out the element inspector!" + "incompleteFallbackMessage": "axe couldn't tell the reason. Time to break out the element inspector!" } diff --git a/lib/rules/.eslintrc b/lib/rules/.eslintrc index a4a643b2ea..ba2c9cb4a4 100644 --- a/lib/rules/.eslintrc +++ b/lib/rules/.eslintrc @@ -42,14 +42,6 @@ 2, 5 ], - "max-statements": [ - 2, - 15 - ], - "complexity": [ - 2, - 10 - ], "no-cond-assign": 0, "no-debugger": 0, "no-eq-null": 0, diff --git a/lib/rules/aria-allowed-attr-matches.js b/lib/rules/aria-allowed-attr-matches.js index 782c99b465..09b5d69edc 100644 --- a/lib/rules/aria-allowed-attr-matches.js +++ b/lib/rules/aria-allowed-attr-matches.js @@ -1,6 +1,6 @@ const aria = /^aria-/; if (node.hasAttributes()) { - let attrs = node.attributes; + let attrs = axe.utils.getNodeAttributes(node); for (let i = 0, l = attrs.length; i < l; i++) { if (aria.test(attrs[i].name)) { return true; diff --git a/lib/rules/aria-has-attr-matches.js b/lib/rules/aria-has-attr-matches.js index a9bdbc4a5a..c12eb40965 100644 --- a/lib/rules/aria-has-attr-matches.js +++ b/lib/rules/aria-has-attr-matches.js @@ -1,6 +1,6 @@ var aria = /^aria-/; if (node.hasAttributes()) { - var attrs = node.attributes; + var attrs = axe.utils.getNodeAttributes(node); for (var i = 0, l = attrs.length; i < l; i++) { if (aria.test(attrs[i].name)) { return true; diff --git a/lib/rules/aria-hidden-focus.json b/lib/rules/aria-hidden-focus.json index 878f29d37c..4d3460eada 100755 --- a/lib/rules/aria-hidden-focus.json +++ b/lib/rules/aria-hidden-focus.json @@ -3,7 +3,7 @@ "selector": "[aria-hidden=\"true\"]", "matches": "aria-hidden-focus-matches.js", "excludeHidden": false, - "tags": ["cat.name-role-value", "wcag2a", "wcag412"], + "tags": ["cat.name-role-value", "wcag2a", "wcag412", "wcag131"], "metadata": { "description": "Ensures aria-hidden elements do not contain focusable elements", "help": "ARIA hidden element must not contain focusable elements" diff --git a/lib/rules/avoid-inline-spacing.json b/lib/rules/avoid-inline-spacing.json new file mode 100644 index 0000000000..95e8a5d46e --- /dev/null +++ b/lib/rules/avoid-inline-spacing.json @@ -0,0 +1,12 @@ +{ + "id": "avoid-inline-spacing", + "selector": "[style]", + "tags": ["wcag21", "wcag1412"], + "metadata": { + "description": "Ensure that text spacing set through style attributes can be adjusted with custom stylesheets", + "help": "Inline text spacing must be adjustable with custom stylesheets" + }, + "all": ["avoid-inline-spacing"], + "any": [], + "none": [] +} diff --git a/lib/rules/color-contrast-matches.js b/lib/rules/color-contrast-matches.js index f5045aa459..bb7699ddc4 100644 --- a/lib/rules/color-contrast-matches.js +++ b/lib/rules/color-contrast-matches.js @@ -53,10 +53,7 @@ if (nodeName === 'LABEL' || nodeParentLabel) { if (nodeParentLabel) { relevantNode = nodeParentLabel; // we need an input candidate from a parent to account for label children - relevantVirtualNode = axe.utils.getNodeFromTree( - axe._tree[0], - nodeParentLabel - ); + relevantVirtualNode = axe.utils.getNodeFromTree(nodeParentLabel); } // explicit label of disabled input let doc = axe.commons.dom.getRootNode(relevantNode); diff --git a/lib/rules/html-has-lang.json b/lib/rules/html-has-lang.json index 9fa967c003..cc28f6e577 100644 --- a/lib/rules/html-has-lang.json +++ b/lib/rules/html-has-lang.json @@ -1,6 +1,7 @@ { "id": "html-has-lang", "selector": "html", + "matches": "window-is-top.js", "tags": ["cat.language", "wcag2a", "wcag311"], "metadata": { "description": "Ensures every HTML document has a lang attribute", diff --git a/lib/rules/skip-link-matches.js b/lib/rules/skip-link-matches.js index 24bab16507..c642f0d443 100644 --- a/lib/rules/skip-link-matches.js +++ b/lib/rules/skip-link-matches.js @@ -1 +1 @@ -return /^#[^/!]/.test(node.getAttribute('href')); +return axe.commons.dom.isSkipLink(node); diff --git a/lib/rules/skip-link.json b/lib/rules/skip-link.json index c47905ea6f..7d7e5d71b4 100644 --- a/lib/rules/skip-link.json +++ b/lib/rules/skip-link.json @@ -1,6 +1,6 @@ { "id": "skip-link", - "selector": "a[href]", + "selector": "a[href^=\"#\"], a[href^=\"/#\"]", "matches": "skip-link-matches.js", "tags": ["cat.keyboard", "best-practice"], "metadata": { diff --git a/locales/nl.json b/locales/nl.json index 9cc0e29b0c..9008261be3 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -1,36 +1,36 @@ { - "lang": "nl", - "checks": { - "abstractrole": { - "pass": "Er zijn geen abstracte rollen (role) gebruikt", - "fail": "Gebruik geen abstracte rollen (role)" - }, - "color-contrast": { - "pass": "Element heeft voldoende contrast, namelijk {{=it.data.contrastRatio}}", - "fail": "Element heeft onvoldoende contrast, {{=it.data.contrastRatio}} (Voorgrondkleur: {{=it.data.fgColor}}, achtergrondkleur: {{=it.data.bgColor}}, tekstgrootte: {{=it.data.fontSize}}, tekstdikte: {{=it.data.fontWeight}})", - "incomplete": { - "bgImage": "Element's achtergrondkleur kon niet worden bepaald vanwegen een achtergrondafbeelding", - "bgGradient": "Element's achtergrondkleur kon niet worden bepaald vanwegen een gradient kleur", - "imgNode": "Element's achtergrondkleur kon niet worden bepaald vanwegen een image node", - "bgOverlap": "Element's achtergrondkleur kon niet worden bepaald vanwegen een overlappend element", - "fgAlpha" : "Element's achtergrondkleur kon niet worden bepaald vanwegen alpha transparency", - "default": "Contrastkleur kon niet bepaald worden" - } - } - }, - "rules": { - "aria-required-attr": { - "description": "Zorg dat elementen met ARIA rollen (role) de vereiste ARIA attributen hebben", - "help": "Voorzien de vereiste ARIA attributen" - } - }, - "failureSummaries": { - "none": { - "failureMessage": "Los al het volgende op:{{~it:value}}\n {{=value.split('\\n').join('\\n ')}}{{~}}" - }, - "any": { - "failureMessage": "Gebruik een van de volgende oplossingen:{{~it:value}}\n {{=value.split('\\n').join('\\n ')}}{{~}}" - } - }, - "incompleteFallbackMessage": "aXe kon de reden niet vertellen. Tijd om de element inspecteur uit te breken!" -} \ No newline at end of file + "lang": "nl", + "checks": { + "abstractrole": { + "pass": "Er zijn geen abstracte rollen (role) gebruikt", + "fail": "Gebruik geen abstracte rollen (role)" + }, + "color-contrast": { + "pass": "Element heeft voldoende contrast, namelijk {{=it.data.contrastRatio}}", + "fail": "Element heeft onvoldoende contrast, {{=it.data.contrastRatio}} (Voorgrondkleur: {{=it.data.fgColor}}, achtergrondkleur: {{=it.data.bgColor}}, tekstgrootte: {{=it.data.fontSize}}, tekstdikte: {{=it.data.fontWeight}})", + "incomplete": { + "bgImage": "Element's achtergrondkleur kon niet worden bepaald vanwegen een achtergrondafbeelding", + "bgGradient": "Element's achtergrondkleur kon niet worden bepaald vanwegen een gradient kleur", + "imgNode": "Element's achtergrondkleur kon niet worden bepaald vanwegen een image node", + "bgOverlap": "Element's achtergrondkleur kon niet worden bepaald vanwegen een overlappend element", + "fgAlpha": "Element's achtergrondkleur kon niet worden bepaald vanwegen alpha transparency", + "default": "Contrastkleur kon niet bepaald worden" + } + } + }, + "rules": { + "aria-required-attr": { + "description": "Zorg dat elementen met ARIA rollen (role) de vereiste ARIA attributen hebben", + "help": "Voorzien de vereiste ARIA attributen" + } + }, + "failureSummaries": { + "none": { + "failureMessage": "Los al het volgende op:{{~it:value}}\n {{=value.split('\\n').join('\\n ')}}{{~}}" + }, + "any": { + "failureMessage": "Gebruik een van de volgende oplossingen:{{~it:value}}\n {{=value.split('\\n').join('\\n ')}}{{~}}" + } + }, + "incompleteFallbackMessage": "axe kon de reden niet vertellen. Tijd om de element inspecteur uit te breken!" +} diff --git a/package.json b/package.json index a77e7d1b80..aa2dd76a01 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "unit", "tdd", "bdd", - "aXe" + "axe" ], "main": "axe.js", "typings": "axe.d.ts", @@ -62,23 +62,23 @@ "build": "grunt", "eslint": "eslint --color --format stylish '{lib,test,build,doc}/**/*.js' 'Gruntfile.js'", "test": "npm run retire && tsc && grunt test", + "test:examples": "node ./doc/examples/test-examples", "version": "echo \"use 'npm run release' to bump axe-core version\" && exit 1", "prepublishOnly": "grunt build && grunt file-exists", - "retire": "retire --jspath 'lib' --nodepath './' --ignorefile '.retireignore.json' --severity 'medium'", + "retire": "retire --jspath lib --nodepath ./ --ignorefile .retireignore.json --severity medium", "release": "standard-version -a", "rule-gen": "node build/rule-generator", "next-release": "standard-version --scripts.prebump=./build/next-version.js --skip.commit=true --skip.tag=true", "sri-update": "grunt build && node build/sri-update && git add sri-history.json", - "fmt": "prettier --write *.{json,md,js} **/*.ts './{build,doc,lib,test}/**/*.{json,md,js,ts}'" + "fmt": "prettier --write *.{json,md,js} **/*.ts './{build,doc,lib,test}/**/*.{json,md,js,ts,html}'" }, "devDependencies": { - "@babel/core": "^7.2.2", + "@babel/core": "^7.4.4", + "@babel/plugin-proposal-object-rest-spread": "^7.4.4", + "@babel/polyfill": "^7.4.4", + "@babel/preset-env": "^7.4.4", "aria-query": "^3.0.0", "axios": "^0.18.0", - "babel-core": "^6.26.0", - "babel-plugin-transform-object-rest-spread": "^6.6.5", - "babel-polyfill": "^6.7.4", - "babel-preset-env": "^1.7.0", "babelify": "^10.0.0", "blanket": "~1.2.3", "browserify": "^16.2.3", @@ -93,9 +93,9 @@ "eslint-config-prettier": "^4.1.0", "execa": "^1.0.0", "fs-extra": "^7.0.0", - "globby": "^8.0.1", + "globby": "^9.1.0", "grunt": "^1.0.3", - "grunt-babel": "^7.0.0", + "grunt-babel": "^8.0.0", "grunt-contrib-clean": "^2.0.0", "grunt-contrib-concat": "^1.0.1", "grunt-contrib-connect": "^2.0.0", @@ -106,27 +106,28 @@ "grunt-parallel": "^0.5.1", "grunt-run": "^0.8.1", "html-entities": "^1.2.0", - "husky": "^1.3.1", + "husky": "^2.0.0", "jquery": "^3.0.0", "jsdoc": "^3.5.5", "lint-staged": "^8.0.0", - "make-dir": "^2.1.0", + "make-dir": "^3.0.0", "markdown-table": "^1.1.2", "minami": "^1.2.3", "mkdirp": "^0.5.1", - "mocha": "^6.0.0", - "prettier": "^1.14.0", + "mocha": "^6.1.2", + "prettier": "^1.17.0", "retire": "^2.0.1", "revalidator": "~0.3.1", "selenium-webdriver": "~3.6.0", "sri-toolbox": "^0.2.0", "standard-version": "^5.0.0", "typescript": "^2.9.2", - "uglify-js": "^3.4.4" + "uglify-js": "^3.4.4", + "weakmap-polyfill": "^2.0.0" }, "dependencies": {}, "lint-staged": { - "*.{md,json,ts}": [ + "*.{md,json,ts,html}": [ "prettier --write", "git add" ], diff --git a/test/checks/aria/allowed-attr.js b/test/checks/aria/allowed-attr.js index 746e224fef..28b8bb5f71 100644 --- a/test/checks/aria/allowed-attr.js +++ b/test/checks/aria/allowed-attr.js @@ -109,6 +109,7 @@ describe('aria-allowed-attr', function() { node.tabIndex = 1; node.setAttribute('role', 'radio'); node.setAttribute('aria-required', 'true'); + node.setAttribute('aria-checked', 'true'); fixture.appendChild(node); assert.isTrue( diff --git a/test/checks/aria/required-children.js b/test/checks/aria/required-children.js index eeb51ff1ad..7ebb0dbefc 100644 --- a/test/checks/aria/required-children.js +++ b/test/checks/aria/required-children.js @@ -32,8 +32,8 @@ describe('aria-required-children', function() { var shadowRoot = target.attachShadow({ mode: 'open' }); shadowRoot.innerHTML = '

Nothing here.

'; - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); - var virtualTarget = axe.utils.getNodeFromTree(tree[0], target); + axe.testUtils.flatTreeSetup(fixture); + var virtualTarget = axe.utils.getNodeFromTree(target); var params = [target, undefined, virtualTarget]; assert.isFalse( @@ -63,8 +63,8 @@ describe('aria-required-children', function() { var shadowRoot = target.attachShadow({ mode: 'open' }); shadowRoot.innerHTML = '

Nothing here.

'; - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); - var virtualTarget = axe.utils.getNodeFromTree(tree[0], target); + axe.testUtils.flatTreeSetup(fixture); + var virtualTarget = axe.utils.getNodeFromTree(target); var params = [target, undefined, virtualTarget]; assert.isFalse( @@ -113,8 +113,8 @@ describe('aria-required-children', function() { shadowRoot.innerHTML = '

Nothing here.

Textbox

'; - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); - var virtualTarget = axe.utils.getNodeFromTree(tree[0], target); + axe.testUtils.flatTreeSetup(fixture); + var virtualTarget = axe.utils.getNodeFromTree(target); var params = [target, undefined, virtualTarget]; assert.isTrue( diff --git a/test/checks/aria/required-parent.js b/test/checks/aria/required-parent.js index 07fa75e0f2..170094e6ee 100644 --- a/test/checks/aria/required-parent.js +++ b/test/checks/aria/required-parent.js @@ -32,9 +32,9 @@ describe('aria-required-parent', function() { .attachShadow({ mode: 'open' }); shadowRoot.innerHTML = '

Nothing here.

'; - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); var shadowContent = shadowRoot.querySelector('#target'); - var virtualTarget = axe.utils.getNodeFromTree(tree[0], shadowContent); + var virtualTarget = axe.utils.getNodeFromTree(shadowContent); var params = [shadowContent, undefined, virtualTarget]; assert.isFalse( @@ -102,9 +102,9 @@ describe('aria-required-parent', function() { .attachShadow({ mode: 'open' }); shadowRoot.innerHTML = '

Nothing here.

'; - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); var shadowContent = shadowRoot.querySelector('#target'); - var virtualTarget = axe.utils.getNodeFromTree(tree[0], shadowContent); + var virtualTarget = axe.utils.getNodeFromTree(shadowContent); var params = [shadowContent, undefined, virtualTarget]; assert.isTrue( @@ -124,9 +124,9 @@ describe('aria-required-parent', function() { .attachShadow({ mode: 'open' }); shadowRoot.innerHTML = '

Nothing here.

'; - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); var shadowContent = shadowRoot.querySelector('#target'); - var virtualTarget = axe.utils.getNodeFromTree(tree[0], shadowContent); + var virtualTarget = axe.utils.getNodeFromTree(shadowContent); var params = [shadowContent, undefined, virtualTarget]; assert.isFalse( diff --git a/test/checks/aria/valid-attr-value.js b/test/checks/aria/valid-attr-value.js index aaef387a5f..d62a25695e 100644 --- a/test/checks/aria/valid-attr-value.js +++ b/test/checks/aria/valid-attr-value.js @@ -129,6 +129,82 @@ describe('aria-valid-attr-value', function() { ); }); + it('should pass on aria-controls and aria-expanded=false when the element is not in the DOM', function() { + fixtureSetup( + '' + ); + var passing1 = fixture.querySelector('button'); + assert.isTrue( + checks['aria-valid-attr-value'].evaluate.call(checkContext, passing1) + ); + }); + + it('should pass on aria-controls and aria-selected=false when the element is not in the DOM', function() { + fixtureSetup( + '' + ); + var passing1 = fixture.querySelector('button'); + assert.isTrue( + checks['aria-valid-attr-value'].evaluate.call(checkContext, passing1) + ); + }); + + it('should fail on aria-controls and aria-expanded=true when the element is not in the DOM', function() { + fixtureSetup( + '' + ); + var failing1 = fixture.querySelector('button'); + assert.isFalse( + checks['aria-valid-attr-value'].evaluate.call(checkContext, failing1) + ); + }); + + it('should fail on aria-controls and aria-selected=true when the element is not in the DOM', function() { + fixtureSetup( + '' + ); + var failing1 = fixture.querySelector('button'); + assert.isFalse( + checks['aria-valid-attr-value'].evaluate.call(checkContext, failing1) + ); + }); + + it('should fail on aria-controls when the element is not in the DOM', function() { + fixtureSetup(''); + var failing1 = fixture.querySelector('button'); + assert.isFalse( + checks['aria-valid-attr-value'].evaluate.call(checkContext, failing1) + ); + }); + + it('should pass on aria-owns and aria-expanded=false when the element is not in the DOM', function() { + fixtureSetup( + '' + ); + var passing1 = fixture.querySelector('button'); + assert.isTrue( + checks['aria-valid-attr-value'].evaluate.call(checkContext, passing1) + ); + }); + + it('should fail on aria-owns and aria-expanded=true when the element is not in the DOM', function() { + fixtureSetup( + '' + ); + var failing1 = fixture.querySelector('button'); + assert.isFalse( + checks['aria-valid-attr-value'].evaluate.call(checkContext, failing1) + ); + }); + + it('should fail on aria-owns when the element is not in the DOM', function() { + fixtureSetup(''); + var failing1 = fixture.querySelector('button'); + assert.isFalse( + checks['aria-valid-attr-value'].evaluate.call(checkContext, failing1) + ); + }); + describe('options', function() { it('should exclude supplied attributes', function() { fixture.innerHTML = diff --git a/test/checks/color/color-contrast.js b/test/checks/color/color-contrast.js index e9bde325a2..5b80b48a70 100644 --- a/test/checks/color/color-contrast.js +++ b/test/checks/color/color-contrast.js @@ -227,7 +227,7 @@ describe('color-contrast', function() { it('should return true when a label wraps a text input', function() { fixtureSetup(''); var target = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], target); + var virtualNode = axe.utils.getNodeFromTree(target); if (window.PHANTOMJS) { assert.ok('PhantomJS is a liar'); } else { diff --git a/test/checks/forms/fieldset.js b/test/checks/forms/fieldset.js index c60d82c812..5575ba1060 100644 --- a/test/checks/forms/fieldset.js +++ b/test/checks/forms/fieldset.js @@ -27,7 +27,7 @@ describe('fieldset', function() { ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isTrue( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); @@ -49,7 +49,7 @@ describe('fieldset', function() { ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isFalse( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); @@ -76,7 +76,7 @@ describe('fieldset', function() { '" name="uniqueyname">' ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isFalse( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); @@ -103,7 +103,7 @@ describe('fieldset', function() { '" name="uniqueyname">' ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isFalse( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); @@ -131,7 +131,7 @@ describe('fieldset', function() { '" name="uniqueyname">' ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isFalse( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); @@ -158,7 +158,7 @@ describe('fieldset', function() { '" name="uniqueyname">' ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isTrue( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); @@ -176,7 +176,7 @@ describe('fieldset', function() { ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isFalse( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); @@ -202,7 +202,7 @@ describe('fieldset', function() { ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isFalse( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); @@ -230,7 +230,7 @@ describe('fieldset', function() { '" name="uniqueyname">
' ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isFalse( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); @@ -259,7 +259,7 @@ describe('fieldset', function() { ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isTrue( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); @@ -282,7 +282,7 @@ describe('fieldset', function() { ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isTrue( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); @@ -304,7 +304,7 @@ describe('fieldset', function() { '" name="uniqueyname">' ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isTrue( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); @@ -322,7 +322,7 @@ describe('fieldset', function() { '' ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isTrue( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); @@ -348,7 +348,7 @@ describe('fieldset', function() { '' ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isTrue( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); @@ -374,7 +374,7 @@ describe('fieldset', function() { '' ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isTrue( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); @@ -398,7 +398,7 @@ describe('fieldset', function() { fixtureSetup(fieldset); var node = shadow.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isTrue( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); @@ -423,7 +423,7 @@ describe('fieldset', function() { fixtureSetup(div); var node = div.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isTrue( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); @@ -448,7 +448,7 @@ describe('fieldset', function() { ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isTrue( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); @@ -471,7 +471,7 @@ describe('fieldset', function() { '" name="s.%$#n">' ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isFalse( checks.fieldset.evaluate.call(checkContext, node, {}, virtualNode) ); diff --git a/test/checks/forms/group-labelledby.js b/test/checks/forms/group-labelledby.js index daca2f19f6..de4b08f58a 100644 --- a/test/checks/forms/group-labelledby.js +++ b/test/checks/forms/group-labelledby.js @@ -317,9 +317,9 @@ describe('group-labelledby', function() { type + '" aria-labelledby="shared three" name="groupname">'; - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); var shadowContent = shadowRoot.querySelector('#target'); - var virtualTarget = axe.utils.getNodeFromTree(tree[0], shadowContent); + var virtualTarget = axe.utils.getNodeFromTree(shadowContent); var params = [shadowContent, undefined, virtualTarget]; assert.isFalse(check.evaluate.apply(checkContext, params)); @@ -355,9 +355,9 @@ describe('group-labelledby', function() { type + '" aria-labelledby="shared three" name="groupname">'; - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); var shadowContent = shadowRoot.querySelector('#target'); - var virtualTarget = axe.utils.getNodeFromTree(tree[0], shadowContent); + var virtualTarget = axe.utils.getNodeFromTree(shadowContent); var params = [shadowContent, undefined, virtualTarget]; assert.isFalse(check.evaluate.apply(checkContext, params)); @@ -394,9 +394,9 @@ describe('group-labelledby', function() { type + '" id="target" name="samename" aria-labelledby="shared three">'; - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); var shadowContent = shadowRoot.querySelector('#target'); - var virtualTarget = axe.utils.getNodeFromTree(tree[0], shadowContent); + var virtualTarget = axe.utils.getNodeFromTree(shadowContent); var params = [shadowContent, undefined, virtualTarget]; assert.isTrue(check.evaluate.apply(checkContext, params)); diff --git a/test/checks/keyboard/focusable-disabled.js b/test/checks/keyboard/focusable-disabled.js index b2bf0fed78..1c023fe289 100644 --- a/test/checks/keyboard/focusable-disabled.js +++ b/test/checks/keyboard/focusable-disabled.js @@ -106,9 +106,9 @@ describe('focusable-disabled', function() { var node = fixture.querySelector('#target'); var shadow = node.attachShadow({ mode: 'open' }); shadow.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); axe._selectorData = axe.utils.getSelectorData(axe._tree); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); var actual = check.evaluate.call(checkContext, node, {}, virtualNode); assert.isFalse(actual); } diff --git a/test/checks/keyboard/focusable-not-tabbable.js b/test/checks/keyboard/focusable-not-tabbable.js index c83e0aa960..a0665adf6f 100644 --- a/test/checks/keyboard/focusable-not-tabbable.js +++ b/test/checks/keyboard/focusable-not-tabbable.js @@ -102,9 +102,9 @@ describe('focusable-not-tabbable', function() { var node = fixture.querySelector('#target'); var shadow = node.attachShadow({ mode: 'open' }); shadow.innerHTML = '

btn

'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); axe._selectorData = axe.utils.getSelectorData(axe._tree); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); var actual = check.evaluate.call(checkContext, node, {}, virtualNode); assert.isFalse(actual); } diff --git a/test/checks/label/duplicate-img-label.js b/test/checks/label/duplicate-img-label.js index 60e0aba6b0..bd15675702 100644 --- a/test/checks/label/duplicate-img-label.js +++ b/test/checks/label/duplicate-img-label.js @@ -13,12 +13,12 @@ describe('duplicate-img-label', function() { it('should return false if no img is present', function() { fixture.innerHTML = ''; var node = fixture.querySelector('#target'); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); assert.isFalse( checks['duplicate-img-label'].evaluate( node, undefined, - axe.utils.getNodeFromTree(tree[0], node) + axe.utils.getNodeFromTree(node) ) ); }); @@ -26,11 +26,11 @@ describe('duplicate-img-label', function() { it('should return false if no text is present', function() { fixture.innerHTML = ''; var node = fixture.querySelector('#target'); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); var result = checks['duplicate-img-label'].evaluate( node, undefined, - axe.utils.getNodeFromTree(tree[0], node) + axe.utils.getNodeFromTree(node) ); assert.isFalse(result); }); @@ -39,12 +39,12 @@ describe('duplicate-img-label', function() { fixture.innerHTML = ''; var node = fixture.querySelector('#target'); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); assert.isFalse( checks['duplicate-img-label'].evaluate( node, undefined, - axe.utils.getNodeFromTree(tree[0], node) + axe.utils.getNodeFromTree(node) ) ); }); @@ -53,12 +53,12 @@ describe('duplicate-img-label', function() { fixture.innerHTML = ''; var node = fixture.querySelector('#target'); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); assert.isFalse( checks['duplicate-img-label'].evaluate( node, undefined, - axe.utils.getNodeFromTree(tree[0], node) + axe.utils.getNodeFromTree(node) ) ); }); @@ -67,12 +67,12 @@ describe('duplicate-img-label', function() { fixture.innerHTML = ''; var node = fixture.querySelector('#target'); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); assert.isTrue( checks['duplicate-img-label'].evaluate( node, undefined, - axe.utils.getNodeFromTree(tree[0], node) + axe.utils.getNodeFromTree(node) ) ); }); @@ -81,12 +81,12 @@ describe('duplicate-img-label', function() { fixture.innerHTML = ''; var node = fixture.querySelector('#target'); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); assert.isTrue( checks['duplicate-img-label'].evaluate( node, undefined, - axe.utils.getNodeFromTree(tree[0], node) + axe.utils.getNodeFromTree(node) ) ); }); @@ -94,12 +94,12 @@ describe('duplicate-img-label', function() { it('should return false if img and text are both blank', function() { fixture.innerHTML = ''; var node = fixture.querySelector('#target'); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); assert.isFalse( checks['duplicate-img-label'].evaluate( node, undefined, - axe.utils.getNodeFromTree(tree[0], node) + axe.utils.getNodeFromTree(node) ) ); }); @@ -108,12 +108,12 @@ describe('duplicate-img-label', function() { fixture.innerHTML = ''; var node = fixture.querySelector('#target'); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); assert.isFalse( checks['duplicate-img-label'].evaluate( node, undefined, - axe.utils.getNodeFromTree(tree[0], node) + axe.utils.getNodeFromTree(node) ) ); }); diff --git a/test/checks/label/help-same-as-label.js b/test/checks/label/help-same-as-label.js index 9cc371dae2..145383dab6 100644 --- a/test/checks/label/help-same-as-label.js +++ b/test/checks/label/help-same-as-label.js @@ -15,12 +15,12 @@ describe('help-same-as-label', function() { node.setAttribute('aria-label', 'Duplicate'); fixture.appendChild(node); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); assert.isTrue( checks['help-same-as-label'].evaluate( node, undefined, - axe.utils.getNodeFromTree(tree[0], node) + axe.utils.getNodeFromTree(node) ) ); }); @@ -37,12 +37,12 @@ describe('help-same-as-label', function() { fixture.appendChild(node); fixture.appendChild(dby); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); assert.isTrue( checks['help-same-as-label'].evaluate( node, undefined, - axe.utils.getNodeFromTree(tree[0], node) + axe.utils.getNodeFromTree(node) ) ); }); @@ -54,12 +54,12 @@ describe('help-same-as-label', function() { fixture.appendChild(node); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); assert.isFalse( checks['help-same-as-label'].evaluate( node, undefined, - axe.utils.getNodeFromTree(tree[0], node) + axe.utils.getNodeFromTree(node) ) ); }); @@ -75,12 +75,12 @@ describe('help-same-as-label', function() { fixture.appendChild(node); fixture.appendChild(dby); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); assert.isFalse( checks['help-same-as-label'].evaluate( node, undefined, - axe.utils.getNodeFromTree(tree[0], node) + axe.utils.getNodeFromTree(node) ) ); }); diff --git a/test/checks/label/implicit.js b/test/checks/label/implicit.js index b292418035..e2100cd50e 100644 --- a/test/checks/label/implicit.js +++ b/test/checks/label/implicit.js @@ -12,7 +12,7 @@ describe('implicit-label', function() { it('should return false if an empty label is present', function() { fixtureSetup(''); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isFalse(checks['implicit-label'].evaluate(node, {}, virtualNode)); }); @@ -21,14 +21,14 @@ describe('implicit-label', function() { '' ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isFalse(checks['implicit-label'].evaluate(node, {}, virtualNode)); }); it('should return true if a non-empty label is present', function() { fixtureSetup(''); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isTrue(checks['implicit-label'].evaluate(node, {}, virtualNode)); }); @@ -37,7 +37,7 @@ describe('implicit-label', function() { node.type = 'text'; fixtureSetup(node); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isFalse(checks['implicit-label'].evaluate(node, {}, virtualNode)); }); }); diff --git a/test/checks/label/title-only.js b/test/checks/label/title-only.js index 35f93cf152..0cd86e175d 100644 --- a/test/checks/label/title-only.js +++ b/test/checks/label/title-only.js @@ -15,13 +15,13 @@ describe('title-only', function() { fixture.appendChild(node); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); assert.isTrue( checks['title-only'].evaluate( node, undefined, - axe.utils.getNodeFromTree(tree[0], node) + axe.utils.getNodeFromTree(node) ) ); node.setAttribute('aria-label', 'woop'); @@ -29,7 +29,7 @@ describe('title-only', function() { checks['title-only'].evaluate( node, undefined, - axe.utils.getNodeFromTree(tree[0], node) + axe.utils.getNodeFromTree(node) ) ); }); @@ -45,13 +45,13 @@ describe('title-only', function() { fixture.appendChild(node); fixture.appendChild(dby); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture)); + axe.testUtils.flatTreeSetup(fixture); assert.isTrue( checks['title-only'].evaluate( node, undefined, - axe.utils.getNodeFromTree(tree[0], node) + axe.utils.getNodeFromTree(node) ) ); node.setAttribute('aria-label', 'woop'); @@ -59,7 +59,7 @@ describe('title-only', function() { checks['title-only'].evaluate( node, undefined, - axe.utils.getNodeFromTree(tree[0], node) + axe.utils.getNodeFromTree(node) ) ); }); diff --git a/test/checks/lists/only-dlitems.js b/test/checks/lists/only-dlitems.js index 6bbf7cd59f..af5aaba532 100644 --- a/test/checks/lists/only-dlitems.js +++ b/test/checks/lists/only-dlitems.js @@ -4,6 +4,7 @@ describe('only-dlitems', function() { var fixture = document.getElementById('fixture'); var checkSetup = axe.testUtils.checkSetup; var shadowSupport = axe.testUtils.shadowSupport; + var isIE11 = axe.testUtils.isIE11; var checkContext = axe.testUtils.MockCheckContext(); @@ -126,15 +127,19 @@ describe('only-dlitems', function() { assert.deepEqual(checkContext._relatedNodes, []); }); - it('should return false if is used along side dt', function() { - var checkArgs = checkSetup( - '
A list
' - ); + // This currently breaks in IE11 + (isIE11 ? it.skip : it)( + 'should return false if is used along side dt', + function() { + var checkArgs = checkSetup( + '
A list
' + ); - assert.isFalse( - checks['only-dlitems'].evaluate.apply(checkContext, checkArgs) - ); - }); + assert.isFalse( + checks['only-dlitems'].evaluate.apply(checkContext, checkArgs) + ); + } + ); it('should return false if is used along side dt', function() { var checkArgs = checkSetup( diff --git a/test/checks/lists/only-listitems.js b/test/checks/lists/only-listitems.js index 28d34282db..431061c6e1 100644 --- a/test/checks/lists/only-listitems.js +++ b/test/checks/lists/only-listitems.js @@ -4,6 +4,7 @@ describe('only-listitems', function() { var fixture = document.getElementById('fixture'); var checkSetup = axe.testUtils.checkSetup; var shadowSupport = axe.testUtils.shadowSupport; + var isIE11 = axe.testUtils.isIE11; var checkContext = axe.testUtils.MockCheckContext(); @@ -139,15 +140,19 @@ describe('only-listitems', function() { ); }); - it('should return false if is used along side li', function() { - var checkArgs = checkSetup( - '
  1. A list
' - ); + // This currently breaks in IE11 + (isIE11 ? it.skip : it)( + 'should return false if is used along side li', + function() { + var checkArgs = checkSetup( + '
  1. A list
' + ); - assert.isFalse( - checks['only-listitems'].evaluate.apply(checkContext, checkArgs) - ); - }); + assert.isFalse( + checks['only-listitems'].evaluate.apply(checkContext, checkArgs) + ); + } + ); it('should return false if is used along side li', function() { var checkArgs = checkSetup( diff --git a/test/checks/mobile/css-orientation-lock.js b/test/checks/mobile/css-orientation-lock.js index 2a5a1e2842..5ac3d94904 100644 --- a/test/checks/mobile/css-orientation-lock.js +++ b/test/checks/mobile/css-orientation-lock.js @@ -3,7 +3,10 @@ describe('css-orientation-lock tests', function() { var checkContext = axe.testUtils.MockCheckContext(); var origCheck = checks['css-orientation-lock']; - var dynamicDoc = document.implementation.createHTMLDocument(); + var dynamicDoc = document.implementation.createHTMLDocument( + 'Dynamic document for CSS Orientation Lock tests' + ); + var isIE11 = axe.testUtils.isIE11; afterEach(function() { checks['css-orientation-lock'] = origCheck; @@ -216,26 +219,30 @@ describe('css-orientation-lock tests', function() { assert.isTrue(actual); }); - it('returns false if TRANSFORM style applied is ROTATE, and is divisible by 90 and not divisible by 180', function() { - var actual = checks['css-orientation-lock'].evaluate.call( - checkContext, - document, - {}, - undefined, - { - cssom: [ - { - shadowId: undefined, - root: document, - sheet: getSheet( - SHEET_DATA.MEDIA_STYLE_ORIENTATION_WITH_TRANSFORM_ROTATE_90 - ) - } - ] - } - ); - assert.isFalse(actual); - }); + // This currently breaks in IE11 + (isIE11 ? it.skip : it)( + 'returns false if TRANSFORM style applied is ROTATE, and is divisible by 90 and not divisible by 180', + function() { + var actual = checks['css-orientation-lock'].evaluate.call( + checkContext, + document, + {}, + undefined, + { + cssom: [ + { + shadowId: undefined, + root: document, + sheet: getSheet( + SHEET_DATA.MEDIA_STYLE_ORIENTATION_WITH_TRANSFORM_ROTATE_90 + ) + } + ] + } + ); + assert.isFalse(actual); + } + ); // Note: // external stylesheets is tested in integration tests diff --git a/test/checks/navigation/skip-link.js b/test/checks/navigation/skip-link.js index d44f3ae71b..1b9eac0a27 100644 --- a/test/checks/navigation/skip-link.js +++ b/test/checks/navigation/skip-link.js @@ -7,33 +7,28 @@ describe('skip-link', function() { fixture.innerHTML = ''; }); - it('should return false if the href points to another document', function() { - fixture.innerHTML = - 'Click Here

Introduction

'; - var node = fixture.querySelector('a'); - assert.isFalse(checks['skip-link'].evaluate(node)); - }); - - it('should return false if the href points to a non-existent element', function() { - fixture.innerHTML = - 'Click Here

Introduction

'; - var node = fixture.querySelector('a'); - assert.isFalse(checks['skip-link'].evaluate(node)); - }); - it('should return true if the href points to an element with an ID', function() { fixture.innerHTML = 'Click Here

Introduction

'; + axe._tree = axe.utils.getFlattenedTree(fixture); var node = fixture.querySelector('a'); assert.isTrue(checks['skip-link'].evaluate(node)); }); it('should return true if the href points to an element with an name', function() { fixture.innerHTML = 'Click Here'; + axe._tree = axe.utils.getFlattenedTree(fixture); var node = fixture.querySelector('a'); assert.isTrue(checks['skip-link'].evaluate(node)); }); + it('should return false if the href points to a non-existent element', function() { + fixture.innerHTML = + 'Click Here

Introduction

'; + var node = fixture.querySelector('a'); + assert.isFalse(checks['skip-link'].evaluate(node)); + }); + it('should return undefined if the target has display:none', function() { fixture.innerHTML = 'Click Here' + @@ -49,18 +44,4 @@ describe('skip-link', function() { var node = fixture.querySelector('a'); assert.isUndefined(checks['skip-link'].evaluate(node)); }); - - it('should return true if the URI encoded href points to an element with an ID', function() { - fixture.innerHTML = - 'Click Here

Introduction

'; - var node = fixture.querySelector('a'); - assert.isTrue(checks['skip-link'].evaluate(node)); - }); - - it('should return true if the URI is an Angular skiplink', function() { - fixture.innerHTML = - 'Click Here

Introduction

'; - var node = fixture.querySelector('a'); - assert.isTrue(checks['skip-link'].evaluate(node)); - }); }); diff --git a/test/checks/shared/avoid-inline-spacing.js b/test/checks/shared/avoid-inline-spacing.js new file mode 100644 index 0000000000..ff500bba19 --- /dev/null +++ b/test/checks/shared/avoid-inline-spacing.js @@ -0,0 +1,121 @@ +describe('avoid-inline-spacing tests', function() { + 'use strict'; + + var fixture = document.getElementById('fixture'); + var queryFixture = axe.testUtils.queryFixture; + var check = checks['avoid-inline-spacing']; + var checkContext = axe.testUtils.MockCheckContext(); + + afterEach(function() { + fixture.innerHTML = ''; + checkContext.reset(); + }); + + it('returns true when no inline spacing styles are specified', function() { + var vNode = queryFixture( + '

The quick brown fox jumped over the lazy dog

' + ); + var actual = check.evaluate.call(checkContext, vNode.actualNode); + assert.isTrue(actual); + assert.isNull(checkContext._data); + }); + + it('returns true when inline spacing styles has invalid value', function() { + var vNode = queryFixture( + '

The quick brown fox jumped over the lazy dog

' + ); + var actual = check.evaluate.call(checkContext, vNode.actualNode); + assert.isTrue(actual); + assert.isNull(checkContext._data); + }); + + it('returns true when inline spacing styles has invalid value and `!important` priority', function() { + var vNode = queryFixture( + '

The quick brown fox jumped over the lazy dog

' + ); + var actual = check.evaluate.call(checkContext, vNode.actualNode); + assert.isTrue(actual); + assert.isNull(checkContext._data); + }); + + it('returns true when `line-height` style specified has no `!important` priority', function() { + var vNode = queryFixture( + '

The quick brown fox jumped over the lazy dog

' + ); + var actual = check.evaluate.call(checkContext, vNode.actualNode); + assert.isTrue(actual); + assert.isNull(checkContext._data); + }); + + it('returns true when `letter-spacing` style specified has no `!important` priority', function() { + var vNode = queryFixture( + '

The quick brown fox jumped over the lazy dog

' + ); + var actual = check.evaluate.call(checkContext, vNode.actualNode); + assert.isTrue(actual); + assert.isNull(checkContext._data); + }); + + it('returns true when `word-spacing` style specified has no `!important` priority', function() { + var vNode = queryFixture( + '

The quick brown fox jumped over the lazy dog

' + ); + var actual = check.evaluate.call(checkContext, vNode.actualNode); + assert.isTrue(actual); + assert.isNull(checkContext._data); + }); + + it('returns true when none of the multiple inline spacing styles specified have priority of `!important`', function() { + var vNode = queryFixture( + '

The quick brown fox jumped over the lazy dog

' + ); + var actual = check.evaluate.call(checkContext, vNode.actualNode); + assert.isTrue(actual); + assert.isNull(checkContext._data); + }); + + it('returns false when `line-height` style specified has `!important` priority', function() { + var vNode = queryFixture( + '

The quick brown fox jumped over the lazy dog

' + ); + var actual = check.evaluate.call(checkContext, vNode.actualNode); + assert.isFalse(actual); + assert.deepEqual(checkContext._data, ['line-height']); + }); + + it('returns false when `letter-spacing` style specified has `!important` priority', function() { + var vNode = queryFixture( + '

The quick brown fox jumped over the lazy dog

' + ); + var actual = check.evaluate.call(checkContext, vNode.actualNode); + assert.isFalse(actual); + assert.deepEqual(checkContext._data, ['letter-spacing']); + }); + + it('returns false when `word-spacing` style specified has `!important` priority', function() { + var vNode = queryFixture( + '

The quick brown fox jumped over the lazy dog

' + ); + var actual = check.evaluate.call(checkContext, vNode.actualNode); + assert.isFalse(actual); + assert.deepEqual(checkContext._data, ['word-spacing']); + }); + + it('returns false when any of the multiple inline spacing styles specifies priority of `!important`', function() { + var vNode = queryFixture( + '

The quick brown fox jumped over the lazy dog

' + ); + var actual = check.evaluate.call(checkContext, vNode.actualNode); + assert.isFalse(actual); + assert.deepEqual(checkContext._data, ['letter-spacing']); + }); + + it('returns false when multiple inline spacing styles specifies priority of `!important`', function() { + var vNode = queryFixture( + '

The quick brown fox jumped over the lazy dog

' + ); + var actual = check.evaluate.call(checkContext, vNode.actualNode); + assert.isFalse(actual); + assert.deepEqual(checkContext._data, ['line-height', 'letter-spacing']); + }); +}); diff --git a/test/checks/tables/td-has-header.js b/test/checks/tables/td-has-header.js index ac023de37c..9761b60bd7 100644 --- a/test/checks/tables/td-has-header.js +++ b/test/checks/tables/td-has-header.js @@ -37,7 +37,7 @@ describe('td-has-header', function() { ' ' + ' ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); var result = checks['td-has-header'].evaluate.call(checkContext, node); @@ -49,7 +49,7 @@ describe('td-has-header', function() { fixture.innerHTML = '' + ' ' + '
hi hello
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isTrue(checks['td-has-header'].evaluate.call(checkContext, node)); }); @@ -61,7 +61,7 @@ describe('td-has-header', function() { ' hi hello ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isTrue(checks['td-has-header'].evaluate.call(checkContext, node)); }); @@ -73,7 +73,7 @@ describe('td-has-header', function() { ' hi hello ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isTrue(checks['td-has-header'].evaluate.call(checkContext, node)); }); @@ -86,7 +86,7 @@ describe('td-has-header', function() { ' hi hello ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isTrue(checks['td-has-header'].evaluate.call(checkContext, node)); }); @@ -100,7 +100,7 @@ describe('td-has-header', function() { ' hi hello ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isTrue(checks['td-has-header'].evaluate.call(checkContext, node)); }); @@ -112,7 +112,7 @@ describe('td-has-header', function() { ' hello ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isTrue(checks['td-has-header'].evaluate.call(checkContext, node)); }); @@ -121,7 +121,7 @@ describe('td-has-header', function() { fixture.innerHTML = '' + ' ' + '
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isTrue(checks['td-has-header'].evaluate.call(checkContext, node)); }); @@ -130,7 +130,7 @@ describe('td-has-header', function() { fixture.innerHTML = '' + ' ' + '
hi hello
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isFalse(checks['td-has-header'].evaluate.call(checkContext, node)); @@ -147,7 +147,7 @@ describe('td-has-header', function() { ' hi hello Ok ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isFalse(checks['td-has-header'].evaluate.call(checkContext, node)); @@ -165,7 +165,7 @@ describe('td-has-header', function() { ' Hello goodbye ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isFalse(checks['td-has-header'].evaluate.call(checkContext, node)); @@ -177,7 +177,7 @@ describe('td-has-header', function() { ' Hello goodbye ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isFalse(checks['td-has-header'].evaluate.call(checkContext, node)); @@ -190,7 +190,7 @@ describe('td-has-header', function() { ' hi hello ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isFalse(checks['td-has-header'].evaluate.call(checkContext, node)); }); @@ -206,7 +206,7 @@ describe('td-has-header', function() { ' data ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = axe.utils.querySelectorAll(axe._tree, 'table')[0].actualNode; assert.isTrue(checks['td-has-header'].evaluate.call(checkContext, node)); }); diff --git a/test/checks/tables/th-has-data-cells.js b/test/checks/tables/th-has-data-cells.js index e50c7ca332..3507734a16 100644 --- a/test/checks/tables/th-has-data-cells.js +++ b/test/checks/tables/th-has-data-cells.js @@ -17,7 +17,7 @@ describe('th-has-data-cells', function() { ' hi hello ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isTrue( checks['th-has-data-cells'].evaluate.call(checkContext, node) @@ -31,7 +31,7 @@ describe('th-has-data-cells', function() { ' hi hello' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isTrue( checks['th-has-data-cells'].evaluate.call(checkContext, node) @@ -45,7 +45,7 @@ describe('th-has-data-cells', function() { ' H H ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isTrue( checks['th-has-data-cells'].evaluate.call(checkContext, node) @@ -59,7 +59,7 @@ describe('th-has-data-cells', function() { ' H H ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isTrue( checks['th-has-data-cells'].evaluate.call(checkContext, node) @@ -72,7 +72,7 @@ describe('th-has-data-cells', function() { ' ' + ' ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isTrue( checks['th-has-data-cells'].evaluate.call(checkContext, node) @@ -91,7 +91,7 @@ describe('th-has-data-cells', function() { ' hi ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isTrue( checks['th-has-data-cells'].evaluate.call(checkContext, node) @@ -105,7 +105,7 @@ describe('th-has-data-cells', function() { ' hi ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isUndefined( checks['th-has-data-cells'].evaluate.call(checkContext, node) @@ -119,7 +119,7 @@ describe('th-has-data-cells', function() { ' hi ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isUndefined( checks['th-has-data-cells'].evaluate.call(checkContext, node) @@ -129,10 +129,10 @@ describe('th-has-data-cells', function() { it('should return undefined if a td with role=columnheader is used that has no data cells', function() { fixture.innerHTML = '' + - ' ' + + ' ' + '
aXe AXE
axe AXE
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = fixture.querySelector('table'); assert.isUndefined( checks['th-has-data-cells'].evaluate.call(checkContext, node) @@ -150,7 +150,7 @@ describe('th-has-data-cells', function() { ' ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var node = axe.utils.querySelectorAll(axe._tree, 'table')[0].actualNode; assert.isTrue( checks['th-has-data-cells'].evaluate.call(checkContext, node) diff --git a/test/checks/visibility/hidden-content.js b/test/checks/visibility/hidden-content.js index 3319abb4d6..95c356aa3e 100644 --- a/test/checks/visibility/hidden-content.js +++ b/test/checks/visibility/hidden-content.js @@ -51,8 +51,8 @@ describe('hidden content', function() { it('should skip whitelisted elements', function() { var node = document.querySelector('head'); - axe._tree = axe.utils.getFlattenedTree(document.documentElement); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + axe.testUtils.flatTreeSetup(document.documentElement); + var virtualNode = axe.utils.getNodeFromTree(node); assert.isTrue( checks['hidden-content'].evaluate(node, undefined, virtualNode) ); @@ -65,22 +65,22 @@ describe('hidden content', function() { .attachShadow({ mode: 'open' }); shadowRoot.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var shadow = document.querySelector('#shadow'); - var virtualShadow = axe.utils.getNodeFromTree(axe._tree[0], shadow); + var virtualShadow = axe.utils.getNodeFromTree(shadow); assert.isTrue( checks['hidden-content'].evaluate(shadow, undefined, virtualShadow) ); var target = shadowRoot.querySelector('#target'); - var virtualTarget = axe.utils.getNodeFromTree(axe._tree[0], target); + var virtualTarget = axe.utils.getNodeFromTree(target); assert.isUndefined( checks['hidden-content'].evaluate(target, undefined, virtualTarget) ); var content = document.querySelector('#content'); - var virtualContent = axe.utils.getNodeFromTree(axe._tree[0], content); + var virtualContent = axe.utils.getNodeFromTree(content); assert.isTrue( checks['hidden-content'].evaluate(content, undefined, virtualContent) ); diff --git a/test/commons/color/center-point-of-rect.js b/test/commons/color/center-point-of-rect.js new file mode 100644 index 0000000000..289b85b877 --- /dev/null +++ b/test/commons/color/center-point-of-rect.js @@ -0,0 +1,38 @@ +describe('color.centerPointOfRect', function() { + 'use strict'; + + it('returns `undefined` when element is placed outside of viewport (left position > window dimension)', function() { + var actual = axe.commons.color.centerPointOfRect({ + left: 9999, + top: 0, + width: 200, + height: 100 + }); + assert.isUndefined(actual); + }); + + it('returns `{x,y}` when element is with in viewport', function() { + var actual = axe.commons.color.centerPointOfRect({ + left: 0, + top: 0, + width: 200, + height: 100 + }); + assert.isDefined(actual); + assert.hasAllKeys(actual, ['x', 'y']); + }); + + it('returns `{x,y}` when element is with in viewport (check returned coordinate values)', function() { + var actual = axe.commons.color.centerPointOfRect({ + left: 100, + top: 100, + width: 250, + height: 250 + }); + + assert.isDefined(actual); + assert.hasAllKeys(actual, ['x', 'y']); + assert.equal(actual.x, 225); + assert.equal(actual.y, 225); + }); +}); diff --git a/test/commons/color/element-has-image.js b/test/commons/color/element-has-image.js new file mode 100644 index 0000000000..1ea568614b --- /dev/null +++ b/test/commons/color/element-has-image.js @@ -0,0 +1,86 @@ +describe('color.elementHasImage', function() { + 'use strict'; + + var fixture = document.getElementById('fixture'); + var queryFixture = axe.testUtils.queryFixture; + var elementHasImage = axe.commons.color.elementHasImage; + var origColorIncompleteData = axe.commons.color.incompleteData; + + afterEach(function() { + fixture.innerHTML = ''; + axe._tree = undefined; + axe.commons.color.incompleteData = origColorIncompleteData; + }); + + it('returns true when `HTMLElement` is of graphical type', function() { + ['img', 'canvas', 'object', 'iframe', 'video', 'svg'].forEach(function( + nodeName + ) { + var vNode = queryFixture( + '<' + nodeName + ' id="target">' + ); + var actual = elementHasImage(vNode.actualNode); + assert.isTrue(actual); + assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'imgNode'); + }); + }); + + it('returns false when `HTMLElement` has no background-image style set', function() { + var vNode = queryFixture( + '
No background style
' + ); + var actual = elementHasImage(vNode.actualNode); + assert.isFalse(actual); + }); + + it('returns false when `HTMLElement` has background-image style set to none', function() { + var vNode = queryFixture( + '
Some text...
' + ); + var actual = elementHasImage(vNode.actualNode); + assert.isFalse(actual); + }); + + it('returns true when `HTMLElement` has background-image (url)', function() { + var vNode = queryFixture( + '
Some text...
' + ); + var actual = elementHasImage(vNode.actualNode); + assert.isTrue(actual); + assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'bgImage'); + }); + + it('returns true when `HTMLElement` has background-image (gradient)', function() { + var vNode = queryFixture( + '
Some text...
' + ); + var actual = elementHasImage(vNode.actualNode); + assert.isTrue(actual); + assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'bgGradient'); + }); + + it('returns true when `HTMLElement` has background-image (gradient) and ensure incompleteData setter is invoked', function() { + var incompleteDataCalled = false; + var vNode = queryFixture( + '
Some text...
' + ); + // override `incompleteData` setter + axe.commons.color.incompleteData = (function() { + var data = {}; + return { + set: function(key, value) { + incompleteDataCalled = true; + data[key] = value; + return data[key]; + }, + get: function(key) { + return data[key]; + } + }; + })(); + var actual = elementHasImage(vNode.actualNode); + assert.isTrue(actual); + assert.isTrue(incompleteDataCalled); + assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'bgGradient'); + }); +}); diff --git a/test/commons/color/get-background-color.js b/test/commons/color/get-background-color.js index b6ed61dfe6..cecc58bcae 100644 --- a/test/commons/color/get-background-color.js +++ b/test/commons/color/get-background-color.js @@ -20,7 +20,7 @@ describe('color.getBackgroundColor', function() { var target = fixture.querySelector('#target'); var parent = fixture.querySelector('#parent'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes); var expected = new axe.commons.color.Color(128, 0, 0, 1); assert.closeTo(actual.red, expected.red, 0.5); @@ -44,7 +44,7 @@ describe('color.getBackgroundColor', function() { var target = fixture.querySelector('#target'); var pos = fixture.querySelector('#pos'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes); var expected = new axe.commons.color.Color(64, 64, 0, 1); @@ -64,7 +64,7 @@ describe('color.getBackgroundColor', function() { var target = fixture.querySelector('#target'); var under = fixture.querySelector('#under'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes); var expected = new axe.commons.color.Color(64, 64, 0, 1); assert.closeTo(actual.red, expected.red, 0.5); @@ -90,7 +90,7 @@ describe('color.getBackgroundColor', function() { var target = fixture.querySelector('#target'); var under = fixture.querySelector('#under'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes); var expected = new axe.commons.color.Color(64, 64, 0, 1); @@ -109,7 +109,7 @@ describe('color.getBackgroundColor', function() { var target = fixture.querySelector('#target'); var parent = fixture.querySelector('#parent'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes); var expected = new axe.commons.color.Color(64, 64, 0, 1); assert.closeTo(actual.red, expected.red, 0.5); @@ -127,7 +127,7 @@ describe('color.getBackgroundColor', function() { var target = fixture.querySelector('#target'); var parent = fixture.querySelector('#parent'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes); var expected = new axe.commons.color.Color(64, 64, 0, 1); assert.equal(actual.red, expected.red); @@ -146,7 +146,7 @@ describe('color.getBackgroundColor', function() { var target = fixture.querySelector('#target'); var parent = fixture.querySelector('#parent'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes); assert.isNull(actual); assert.deepEqual(bgNodes, [target, parent]); @@ -156,7 +156,7 @@ describe('color.getBackgroundColor', function() { it('should return white if transparency goes all the way up to document', function() { fixture.innerHTML = '
'; var target = fixture.querySelector('#target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target); var expected = new axe.commons.color.Color(255, 255, 255, 1); assert.equal(actual.red, expected.red); @@ -172,7 +172,7 @@ describe('color.getBackgroundColor', function() { '
'; var target = fixture.querySelector('#target'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes); assert.isNull(actual); assert.deepEqual(bgNodes, [target]); @@ -183,7 +183,7 @@ describe('color.getBackgroundColor', function() { fixture.innerHTML = '
' + '
Hello
'; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor( document.getElementById('target'), [] @@ -196,7 +196,7 @@ describe('color.getBackgroundColor', function() { fixture.innerHTML = '
' + '
Hello
'; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor( document.getElementById('target'), [] @@ -214,7 +214,7 @@ describe('color.getBackgroundColor', function() { ''; var target = fixture.querySelector('#target'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes); var expected = new axe.commons.color.Color(0, 128, 0, 1); assert.equal(actual.red, expected.red); @@ -230,7 +230,7 @@ describe('color.getBackgroundColor', function() { '
' + '

Text oh heyyyy and here\'s
a link

' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor( document.getElementById('target'), [] @@ -251,7 +251,7 @@ describe('color.getBackgroundColor', function() { '
' + '

Text oh heyyyy and here\'s
a link

' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor( document.getElementById('target'), [] @@ -306,7 +306,7 @@ describe('color.getBackgroundColor', function() { var target = fixture.querySelector('#target'), parent = fixture.querySelector('#parent'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes); var expected = new axe.commons.color.Color(243, 243, 243, 1); assert.equal(actual.red, expected.red); @@ -328,7 +328,7 @@ describe('color.getBackgroundColor', function() { var target = fixture.querySelector('#target'), parent = fixture.querySelector('#parent'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes); var expected = new axe.commons.color.Color(243, 243, 243, 1); assert.equal(actual.red, expected.red); @@ -350,7 +350,7 @@ describe('color.getBackgroundColor', function() { var target = fixture.querySelector('#target'), parent = fixture.querySelector('#parent'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes); var expected = new axe.commons.color.Color(243, 243, 243, 1); assert.equal(actual.red, expected.red); @@ -369,7 +369,7 @@ describe('color.getBackgroundColor', function() { var bgNodes = []; var target = fixture.querySelector('#target'); var parent = fixture.querySelector('#parent'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes); var expected = new axe.commons.color.Color(255, 255, 255, 1); assert.equal(actual.red, expected.red); @@ -386,7 +386,7 @@ describe('color.getBackgroundColor', function() { ''; var target = fixture.querySelector('#target'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes); var expected = new axe.commons.color.Color(0, 0, 0, 1); assert.equal(actual.red, expected.red); @@ -404,7 +404,7 @@ describe('color.getBackgroundColor', function() { var target = fixture.querySelector('#target'); var parent = fixture.querySelector('#parent'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes); var expected = new axe.commons.color.Color(255, 255, 255, 1); assert.equal(actual.red, expected.red); @@ -430,7 +430,7 @@ describe('color.getBackgroundColor', function() { var target = fixture.querySelector('#target'), parent = fixture.querySelector('#parent'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes); var expected = new axe.commons.color.Color(243, 243, 243, 1); assert.equal(actual.red, expected.red); @@ -452,7 +452,7 @@ describe('color.getBackgroundColor', function() { var target = fixture.querySelector('#target'); var parent = fixture.querySelector('#parent'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes); var expected = new axe.commons.color.Color(255, 255, 255, 1); @@ -473,7 +473,7 @@ describe('color.getBackgroundColor', function() { var target = fixture.querySelector('#target'); var parent = fixture.querySelector('#parent'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes); var expected = new axe.commons.color.Color(255, 255, 255, 1); @@ -496,7 +496,7 @@ describe('color.getBackgroundColor', function() { var target = fixture.querySelector('#target'); var shifted = fixture.querySelector('#shifted'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, bgNodes, false); var expected = new axe.commons.color.Color(0, 0, 0, 1); @@ -522,7 +522,7 @@ describe('color.getBackgroundColor', function() { var target = fixture.querySelector('#target'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var outcome = axe.commons.color.getBackgroundColor(target, bgNodes, false); assert.isNull(outcome); assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'bgImage'); @@ -542,7 +542,7 @@ describe('color.getBackgroundColor', function() { var target = fixture.querySelector('#target'); var bgNodes = []; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var outcome = axe.commons.color.getBackgroundColor(target, bgNodes, false); assert.isNull(outcome); assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'imgNode'); @@ -559,7 +559,7 @@ describe('color.getBackgroundColor', function() { var bgNodes = []; window.scroll(0, 0); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); axe.commons.color.getBackgroundColor(targetEl, bgNodes, true); assert.equal(window.pageYOffset, 0); @@ -576,7 +576,7 @@ describe('color.getBackgroundColor', function() { var bgNodes = []; window.scroll(0, 0); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); axe.commons.color.getBackgroundColor(targetEl, bgNodes, false); assert.notEqual(window.pageYOffset, 0); @@ -588,7 +588,7 @@ describe('color.getBackgroundColor', function() { 'style="z-index:-1; position:absolute; width:100%; height:2em; background: #000">' + '
Some text
'; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor( document.getElementById('target'), [] @@ -610,7 +610,7 @@ describe('color.getBackgroundColor', function() { var orig = document.body.style.background; document.body.style.background = '#FFF'; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor( document.getElementById('target'), [] @@ -631,7 +631,7 @@ describe('color.getBackgroundColor', function() { var orig = document.body.style.background; document.body.style.background = '#F00'; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor( document.getElementById('target'), [] @@ -650,7 +650,7 @@ describe('color.getBackgroundColor', function() { var orig = document.body.style.background; document.body.style.background = '#F00'; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor( document.getElementById('target'), [] @@ -669,7 +669,7 @@ describe('color.getBackgroundColor', function() { var orig = document.documentElement.style.background; document.documentElement.style.background = '#0F0'; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor( document.getElementById('target'), [] @@ -697,7 +697,7 @@ describe('color.getBackgroundColor', function() { // This shouldn't cause a scroll var target1 = document.getElementById('tgt1'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); axe.commons.color.getBackgroundColor(target1, []); // Otherwise this would not be on the black bg anymore: @@ -716,7 +716,7 @@ describe('color.getBackgroundColor', function() { html += 'foo
'; } fixture.innerHTML = '' + html + ''; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var outcome = axe.commons.color.getBackgroundColor(fixture.firstChild, []); assert.isNull(outcome); assert.equal( @@ -730,7 +730,7 @@ describe('color.getBackgroundColor', function() { '
' + '
Text' + '
'; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var target = fixture.querySelector('#target'); var actual = axe.commons.color.getBackgroundColor(target, []); @@ -748,7 +748,7 @@ describe('color.getBackgroundColor', function() { '
' + 'Text' + '
'; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var target = shadow.querySelector('#shadowTarget'); var actual = axe.commons.color.getBackgroundColor(target, []); @@ -768,7 +768,7 @@ describe('color.getBackgroundColor', function() { var shadow = container.attachShadow({ mode: 'open' }); shadow.innerHTML = 'Text'; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var target = shadow.querySelector('#shadowTarget'); var actual = axe.commons.color.getBackgroundColor(target, [], false); @@ -790,7 +790,7 @@ describe('color.getBackgroundColor', function() { '
'; var target = shadow.querySelector('#target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, []); var expected = new axe.commons.color.Color(0, 0, 0, 1); @@ -810,10 +810,10 @@ describe('color.getBackgroundColor', function() { var shadow = container.attachShadow({ mode: 'open' }); shadow.innerHTML = '
Text
'; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var target = shadow.querySelector('#shadowTarget'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(target, []); assert.equal(actual.red, 255); assert.equal(actual.green, 255); @@ -834,7 +834,7 @@ describe('color.getBackgroundColor', function() { shadow1.innerHTML = '
'; var elm2 = document.querySelector('#elm2'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(elm2, []); assert.equal(actual.red, 0); assert.equal(actual.blue, 0); @@ -863,7 +863,7 @@ describe('color.getBackgroundColor', function() { ''; var elm3 = shadow2.querySelector('#elm3'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var actual = axe.commons.color.getBackgroundColor(elm3, []); assert.closeTo(actual.red, 128, 2); assert.closeTo(actual.blue, 128, 2); @@ -881,7 +881,7 @@ describe('color.getBackgroundColor', function() { var shadow = container.attachShadow({ mode: 'open' }); shadow.innerHTML = '
Text
More text
'; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var target = shadow.querySelector('#shadowTarget'); var actual = axe.commons.color.getBackgroundColor(target, []); assert.equal(actual.red, 0); @@ -900,7 +900,7 @@ describe('color.getBackgroundColor', function() { var shadow = container.attachShadow({ mode: 'open' }); shadow.innerHTML = '
Text
More text
'; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var target = shadow.querySelector('#shadowTarget'); var actual = axe.commons.color.getBackgroundColor(target, []); assert.isNull(actual); @@ -915,7 +915,7 @@ describe('color.getBackgroundColor', function() { div.innerHTML = 'Link'; var shadow = div.attachShadow({ mode: 'open' }); shadow.innerHTML = '

'; - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); var linkElm = div.querySelector('a'); var actual = axe.commons.color.getBackgroundColor(linkElm, []); assert.equal(actual.red, 0); diff --git a/test/commons/color/get-own-background-color.js b/test/commons/color/get-own-background-color.js new file mode 100644 index 0000000000..097c3edf20 --- /dev/null +++ b/test/commons/color/get-own-background-color.js @@ -0,0 +1,92 @@ +describe('color.getOwnBackgroundColor', function() { + 'use strict'; + + var fixture = document.getElementById('fixture'); + var queryFixture = axe.testUtils.queryFixture; + var getOwnBackgroundColor = axe.commons.color.getOwnBackgroundColor; + var isPhantom = window.PHANTOMJS ? true : false; + + afterEach(function() { + fixture.innerHTML = ''; + axe._tree = undefined; + }); + + it('returns `new axe.commons.color.Color` instance when no background is set', function() { + var vNode = queryFixture( + '
' + '
' + ); + var actual = getOwnBackgroundColor( + window.getComputedStyle(vNode.actualNode) + ); + assert.equal(actual.red, 0); + assert.equal(actual.green, 0); + assert.equal(actual.blue, 0); + if (!isPhantom) { + assert.equal(actual.alpha, 0); + } + }); + + it('returns color with rgba values of specified background-color value', function() { + var vNode = queryFixture( + '
' + + '
' + ); + var actual = getOwnBackgroundColor( + window.getComputedStyle(vNode.actualNode) + ); + assert.equal(actual.red, 255); + assert.equal(actual.green, 192); + assert.equal(actual.blue, 203); + if (!isPhantom) { + assert.equal(actual.alpha, 1); + } + }); + + it('returns color with rgba values and alpha', function() { + var vNode = queryFixture( + '
' + + '
' + ); + var actual = getOwnBackgroundColor( + window.getComputedStyle(vNode.actualNode) + ); + assert.equal(actual.red, 0); + assert.equal(actual.green, 128); + assert.equal(actual.blue, 0); + if (!isPhantom) { + assert.equal(actual.alpha, 0.5); + } + }); + + it('returns color with rgba values and opacity (for blending)', function() { + var vNode = queryFixture( + '
' + + '
' + ); + var actual = getOwnBackgroundColor( + window.getComputedStyle(vNode.actualNode) + ); + assert.equal(actual.red, 0); + assert.equal(actual.green, 128); + assert.equal(actual.blue, 0); + if (!isPhantom) { + assert.equal(actual.alpha, 0.5); + } + }); + + it('returns color with rgba values, alpha and opacity', function() { + var vNode = queryFixture( + '
' + + '
' + ); + var actual = getOwnBackgroundColor( + window.getComputedStyle(vNode.actualNode) + ); + assert.equal(actual.red, 0); + assert.equal(actual.green, 128); + assert.equal(actual.blue, 0); + if (!isPhantom) { + assert.equal(actual.alpha, 0.25); + } + }); +}); diff --git a/test/commons/dom/find-up.js b/test/commons/dom/find-up.js index 983c7b0125..ab2bc7704a 100644 --- a/test/commons/dom/find-up.js +++ b/test/commons/dom/find-up.js @@ -18,7 +18,7 @@ describe('dom.findUp', function() { var start = document.getElementById('start'), target = document.getElementById('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.equal( axe.commons.dom.findUp(start, '.target'), target, @@ -30,7 +30,7 @@ describe('dom.findUp', function() { fixture.innerHTML = '
'; var start = document.getElementById('start'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isNull(axe.commons.dom.findUp(start, '.nomatchyplzkthx')); }); @@ -38,7 +38,7 @@ describe('dom.findUp', function() { fixture.innerHTML = '
'; var start = document.getElementById('start'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isNull(axe.commons.dom.findUp(start, '.target')); }); @@ -60,7 +60,7 @@ describe('dom.findUp', function() { fixture.innerHTML = ''; makeShadowTree(fixture.querySelector('div')); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture.firstChild)); + var tree = axe.testUtils.flatTreeSetup(fixture.firstChild); var el = axe.utils.querySelectorAll(tree, 'a')[0]; assert.equal( axe.commons.dom.findUp(el.actualNode, 'label'), @@ -86,7 +86,7 @@ describe('dom.findUp', function() { fixture.innerHTML = ''; makeShadowTree(fixture.querySelector('div')); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture.firstChild)); + var tree = axe.testUtils.flatTreeSetup(fixture.firstChild); var el = axe.utils.querySelectorAll(tree, 'a')[0]; assert.equal( axe.commons.dom.findUp(el.actualNode, 'label'), @@ -104,7 +104,7 @@ describe('dom.findUp', function() { shadow.innerHTML = '
item 1
'; var listItem = shadow.querySelector('[role=listitem]'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.equal( axe.commons.dom.findUp(listItem, '[role=list]'), fixture.firstChild @@ -128,7 +128,7 @@ describe('dom.findUp', function() { fixture.innerHTML = ''; makeShadowTree(fixture.querySelector('div')); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture.firstChild)); + var tree = axe.testUtils.flatTreeSetup(fixture.firstChild); var el = axe.utils.querySelectorAll(tree, 'a')[0]; assert.equal( axe.commons.dom.findUp(el.actualNode, 'label'), @@ -155,7 +155,7 @@ describe('dom.findUp', function() { fixture.innerHTML = ''; makeShadowTree(fixture.querySelector('div')); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture.firstChild)); + var tree = axe.testUtils.flatTreeSetup(fixture.firstChild); var el = axe.utils.querySelectorAll(tree, 'a')[0]; var target = axe.utils.querySelectorAll(tree, '.target')[0]; assert.equal( @@ -180,7 +180,7 @@ describe('dom.findUp', function() { fixture.innerHTML = ''; makeShadowTree(fixture.querySelector('div')); - var tree = (axe._tree = axe.utils.getFlattenedTree(fixture.firstChild)); + var tree = axe.testUtils.flatTreeSetup(fixture.firstChild); var el = axe.utils.querySelectorAll(tree, 'a')[0]; assert.equal( axe.commons.dom.findUp(el.actualNode, 'label'), @@ -199,7 +199,7 @@ describe('dom.findUp', function() { shadow.innerHTML = '
item 1
'; var listItem = shadow.querySelector('[role=listitem]'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.equal( axe.commons.dom.findUp(listItem, '[role=list]'), fixture.firstChild diff --git a/test/commons/dom/get-tabbable-elements.js b/test/commons/dom/get-tabbable-elements.js index f5f5f53318..da584767ba 100644 --- a/test/commons/dom/get-tabbable-elements.js +++ b/test/commons/dom/get-tabbable-elements.js @@ -18,7 +18,7 @@ describe('dom.getTabbableElements', function() { '' ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); var actual = getTabbableElementsFn(virtualNode); assert.lengthOf(actual, 1); assert.equal(actual[0].actualNode.nodeName.toUpperCase(), 'TEXTAREA'); @@ -29,7 +29,7 @@ describe('dom.getTabbableElements', function() { '
' + '' + '
' ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); var actual = getTabbableElementsFn(virtualNode); assert.lengthOf(actual, 0); }); @@ -39,7 +39,7 @@ describe('dom.getTabbableElements', function() { '
' + '' + '
' ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); var actual = getTabbableElementsFn(virtualNode); assert.lengthOf(actual, 0); }); @@ -49,7 +49,7 @@ describe('dom.getTabbableElements', function() { '
' + '

Some text

' + '
' ); var node = fixture.querySelector('#target'); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); var actual = getTabbableElementsFn(virtualNode); assert.lengthOf(actual, 0); }); @@ -62,9 +62,9 @@ describe('dom.getTabbableElements', function() { var shadow = node.attachShadow({ mode: 'open' }); shadow.innerHTML = ''; // re build tree after shadowDOM is constructed - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); axe._selectorData = axe.utils.getSelectorData(axe._tree); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); var actual = getTabbableElementsFn(virtualNode); assert.lengthOf(actual, 1); assert.equal(actual[0].actualNode.nodeName.toUpperCase(), 'BUTTON'); @@ -79,9 +79,9 @@ describe('dom.getTabbableElements', function() { var shadow = node.attachShadow({ mode: 'open' }); shadow.innerHTML = ''; // re build tree after shadowDOM is constructed - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); axe._selectorData = axe.utils.getSelectorData(axe._tree); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); var actual = getTabbableElementsFn(virtualNode); assert.lengthOf(actual, 0); } @@ -95,9 +95,9 @@ describe('dom.getTabbableElements', function() { var shadow = node.attachShadow({ mode: 'open' }); shadow.innerHTML = '

I am not tabbable

'; // re build tree after shadowDOM is constructed - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); axe._selectorData = axe.utils.getSelectorData(axe._tree); - var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node); + var virtualNode = axe.utils.getNodeFromTree(node); var actual = getTabbableElementsFn(virtualNode); assert.lengthOf(actual, 0); } diff --git a/test/commons/dom/has-content-virtual.js b/test/commons/dom/has-content-virtual.js index 3016512711..43e0f3ac12 100644 --- a/test/commons/dom/has-content-virtual.js +++ b/test/commons/dom/has-content-virtual.js @@ -66,11 +66,11 @@ describe('dom.hasContentVirtual', function() { it('is called through hasContent, with a DOM node', function() { var hasContent = axe.commons.dom.hasContent; fixture.innerHTML = '
text
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); assert.isTrue(hasContent(fixture.querySelector('#target'))); fixture.innerHTML = '
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); assert.isFalse(hasContent(fixture.querySelector('#target'))); }); diff --git a/test/commons/dom/is-skip-link.js b/test/commons/dom/is-skip-link.js new file mode 100644 index 0000000000..e28bad9adb --- /dev/null +++ b/test/commons/dom/is-skip-link.js @@ -0,0 +1,71 @@ +describe('dom.isSkipLink', function() { + 'use strict'; + + var fixture = document.getElementById('fixture'); + + afterEach(function() { + fixture.innerHTML = ''; + }); + + it('should return true if the href points to an ID', function() { + fixture.innerHTML = 'Click Here'; + axe._tree = axe.utils.getFlattenedTree(fixture); + var node = fixture.querySelector('a'); + assert.isTrue(axe.commons.dom.isSkipLink(node)); + }); + + it('should return false if the href points to another document', function() { + fixture.innerHTML = 'Click Here'; + axe._tree = axe.utils.getFlattenedTree(fixture); + var node = fixture.querySelector('a'); + assert.isFalse(axe.commons.dom.isSkipLink(node)); + }); + + it('should return true if the URI encoded href points to an element with an ID', function() { + fixture.innerHTML = 'Click Here'; + axe._tree = axe.utils.getFlattenedTree(fixture); + var node = fixture.querySelector('a'); + assert.isTrue(axe.commons.dom.isSkipLink(node)); + }); + + it('should return true if the URI is an Angular skiplink', function() { + fixture.innerHTML = 'Click Here'; + axe._tree = axe.utils.getFlattenedTree(fixture); + var node = fixture.querySelector('a'); + assert.isTrue(axe.commons.dom.isSkipLink(node)); + }); + + it('should return true for multiple skip-links', function() { + fixture.innerHTML = + 'Click Here>Click Here>Click Here>'; + axe._tree = axe.utils.getFlattenedTree(fixture); + var nodes = fixture.querySelectorAll('a'); + for (var i = 0; i < nodes.length; i++) { + assert.isTrue(axe.commons.dom.isSkipLink(nodes[i])); + } + }); + + it('should return true if the element is before a page link', function() { + fixture.innerHTML = + 'New Page'; + axe._tree = axe.utils.getFlattenedTree(fixture); + var node = fixture.querySelector('#skip-link'); + assert.isTrue(axe.commons.dom.isSkipLink(node)); + }); + + it('should return false if the element is after a page link', function() { + fixture.innerHTML = + 'New Page'; + axe._tree = axe.utils.getFlattenedTree(fixture); + var node = fixture.querySelector('#skip-link'); + assert.isFalse(axe.commons.dom.isSkipLink(node)); + }); + + it('should ignore links that start with `href=javascript`', function() { + fixture.innerHTML = + 'New Page'; + axe._tree = axe.utils.getFlattenedTree(fixture); + var node = fixture.querySelector('#skip-link'); + assert.isTrue(axe.commons.dom.isSkipLink(node)); + }); +}); diff --git a/test/commons/table/get-cell-position.js b/test/commons/table/get-cell-position.js index 5d498f09d9..22ee8a4e10 100644 --- a/test/commons/table/get-cell-position.js +++ b/test/commons/table/get-cell-position.js @@ -17,7 +17,7 @@ describe('table.getCellPosition', function() { var target = document.getElementById('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.deepEqual(axe.commons.table.getCellPosition(target), { x: 1, y: 1 @@ -34,7 +34,7 @@ describe('table.getCellPosition', function() { var target = document.getElementById('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.deepEqual(axe.commons.table.getCellPosition(target), { x: 2, y: 1 @@ -51,7 +51,7 @@ describe('table.getCellPosition', function() { var target = document.getElementById('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.deepEqual(axe.commons.table.getCellPosition(target), { x: 2, y: 1 @@ -68,7 +68,7 @@ describe('table.getCellPosition', function() { var target = document.getElementById('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.deepEqual(axe.commons.table.getCellPosition(target), { x: 2, y: 1 @@ -87,7 +87,7 @@ describe('table.getCellPosition', function() { var target = document.getElementById('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.deepEqual(axe.commons.table.getCellPosition(target), { x: 1, y: 2 diff --git a/test/commons/table/get-headers.js b/test/commons/table/get-headers.js index b48b64ec9d..26829b8a57 100644 --- a/test/commons/table/get-headers.js +++ b/test/commons/table/get-headers.js @@ -20,7 +20,7 @@ describe('table.getHeaders', function() { var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.deepEqual(axe.commons.table.getHeaders(target), [ $id('t1'), $id('t2') @@ -36,7 +36,7 @@ describe('table.getHeaders', function() { var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.deepEqual(axe.commons.table.getHeaders(target), [ $id('t1'), $id('t2') @@ -53,7 +53,7 @@ describe('table.getHeaders', function() { var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.deepEqual(axe.commons.table.getHeaders(target), [ $id('t1'), $id('t2'), @@ -70,7 +70,7 @@ describe('table.getHeaders', function() { var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.deepEqual(axe.commons.table.getHeaders(target), [ $id('t1'), $id('t2'), @@ -87,7 +87,7 @@ describe('table.getHeaders', function() { var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.deepEqual(axe.commons.table.getHeaders(target), [$id('t1')]); }); @@ -100,7 +100,7 @@ describe('table.getHeaders', function() { var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.deepEqual(axe.commons.table.getHeaders(target), [ $id('t1'), $id('t2') @@ -116,7 +116,7 @@ describe('table.getHeaders', function() { var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.deepEqual(axe.commons.table.getHeaders(target), [ $id('t1'), $id('t2'), @@ -133,7 +133,7 @@ describe('table.getHeaders', function() { var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.deepEqual(axe.commons.table.getHeaders(target), []); }); }); diff --git a/test/commons/table/get-scope.js b/test/commons/table/get-scope.js index 93408f9c27..68f4153d67 100644 --- a/test/commons/table/get-scope.js +++ b/test/commons/table/get-scope.js @@ -46,7 +46,7 @@ describe('table.getScope', function() { ''; var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.equal(axe.commons.table.getScope(target), 'auto'); }); @@ -58,7 +58,7 @@ describe('table.getScope', function() { ''; var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.equal(axe.commons.table.getScope(target), 'auto'); }); @@ -70,7 +70,7 @@ describe('table.getScope', function() { ''; var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.equal(axe.commons.table.getScope(target), 'auto'); }); }); @@ -117,7 +117,7 @@ describe('table.getScope', function() { ''; var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.equal(axe.commons.table.getScope(target), 'col'); }); @@ -129,7 +129,7 @@ describe('table.getScope', function() { ''; var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.equal(axe.commons.table.getScope(target), 'col'); }); @@ -141,7 +141,7 @@ describe('table.getScope', function() { ' ' + ''; var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.equal(axe.commons.table.getScope(target), 'col'); }); @@ -152,7 +152,7 @@ describe('table.getScope', function() { ' ' + ''; var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.equal(axe.commons.table.getScope(target), 'col'); }); }); @@ -199,7 +199,7 @@ describe('table.getScope', function() { ''; var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.equal(axe.commons.table.getScope(target), 'row'); }); @@ -210,7 +210,7 @@ describe('table.getScope', function() { ' ' + ''; var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.equal(axe.commons.table.getScope(target), 'row'); }); @@ -222,7 +222,7 @@ describe('table.getScope', function() { ' ' + ''; var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.equal(axe.commons.table.getScope(target), 'row'); }); }); @@ -234,7 +234,7 @@ describe('table.getScope', function() { ' foo bar ' + ''; var target = $id('target'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.equal(axe.commons.table.getScope(target), 'auto'); }); }); diff --git a/test/commons/table/is-data-table.js b/test/commons/table/is-data-table.js index 0ce1f115f2..e2a7e6cb76 100644 --- a/test/commons/table/is-data-table.js +++ b/test/commons/table/is-data-table.js @@ -40,7 +40,7 @@ describe('table.isDataTable', function() { ''; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -48,7 +48,7 @@ describe('table.isDataTable', function() { fixture.innerHTML = '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -56,7 +56,7 @@ describe('table.isDataTable', function() { fixture.innerHTML = '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -64,7 +64,7 @@ describe('table.isDataTable', function() { fixture.innerHTML = '
'; var node = fixture.querySelector('[role="table"]'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -73,56 +73,56 @@ describe('table.isDataTable', function() { fixture.innerHTML = '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); it('banner', function() { fixture.innerHTML = '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); it('complementary', function() { fixture.innerHTML = '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); it('contentinfo', function() { fixture.innerHTML = '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); it('form', function() { fixture.innerHTML = '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); it('main', function() { fixture.innerHTML = '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); it('navigation', function() { fixture.innerHTML = '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); it('search', function() { fixture.innerHTML = '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); }); @@ -135,7 +135,7 @@ describe('table.isDataTable', function() { ''; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isFalse(axe.commons.table.isDataTable(node)); }); @@ -143,7 +143,7 @@ describe('table.isDataTable', function() { fixture.innerHTML = '' + '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -151,7 +151,7 @@ describe('table.isDataTable', function() { fixture.innerHTML = '' + '' + '
Hello
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -159,7 +159,7 @@ describe('table.isDataTable', function() { fixture.innerHTML = '' + '' + '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -167,7 +167,7 @@ describe('table.isDataTable', function() { fixture.innerHTML = '' + '' + '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -175,7 +175,7 @@ describe('table.isDataTable', function() { fixture.innerHTML = '' + '' + '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -183,7 +183,7 @@ describe('table.isDataTable', function() { fixture.innerHTML = '' + '' + '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -191,7 +191,7 @@ describe('table.isDataTable', function() { fixture.innerHTML = '' + '' + '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -200,7 +200,7 @@ describe('table.isDataTable', function() { '' + '' + '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -209,7 +209,7 @@ describe('table.isDataTable', function() { '' + '' + '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -218,7 +218,7 @@ describe('table.isDataTable', function() { '' + '' + '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -227,7 +227,7 @@ describe('table.isDataTable', function() { '' + '' + '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -236,7 +236,7 @@ describe('table.isDataTable', function() { '' + '' + '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -245,21 +245,21 @@ describe('table.isDataTable', function() { '' + '' + '
ok
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isFalse(axe.commons.table.isDataTable(node)); fixture.innerHTML = '' + '' + '
ok
'; node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isFalse(axe.commons.table.isDataTable(node)); fixture.innerHTML = '' + '' + '
ok
'; node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -270,7 +270,7 @@ describe('table.isDataTable', function() { ''; var node = fixture.querySelector('#out'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isFalse(axe.commons.table.isDataTable(node)); }); @@ -279,7 +279,7 @@ describe('table.isDataTable', function() { '' + '' + '' + '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isFalse(axe.commons.table.isDataTable(node)); }); @@ -287,7 +287,7 @@ describe('table.isDataTable', function() { fixture.innerHTML = '' + '' + '
'; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isFalse(axe.commons.table.isDataTable(node)); }); @@ -299,7 +299,7 @@ describe('table.isDataTable', function() { ''; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -311,7 +311,7 @@ describe('table.isDataTable', function() { ''; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -328,7 +328,7 @@ describe('table.isDataTable', function() { ''; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); @@ -348,7 +348,7 @@ describe('table.isDataTable', function() { ''; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); it('should be true if it has 20 or more rows', function() { @@ -358,7 +358,7 @@ describe('table.isDataTable', function() { ''; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); it('should be false if its width is 95% of the document width', function() { @@ -368,7 +368,7 @@ describe('table.isDataTable', function() { ''; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isFalse(axe.commons.table.isDataTable(node)); }); @@ -379,7 +379,7 @@ describe('table.isDataTable', function() { ''; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isFalse(axe.commons.table.isDataTable(node)); }); @@ -391,7 +391,7 @@ describe('table.isDataTable', function() { ''; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isFalse(axe.commons.table.isDataTable(node)); }); @@ -403,7 +403,7 @@ describe('table.isDataTable', function() { ''; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isFalse(axe.commons.table.isDataTable(node)); }); @@ -415,7 +415,7 @@ describe('table.isDataTable', function() { ''; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isFalse(axe.commons.table.isDataTable(node)); }); @@ -428,7 +428,7 @@ describe('table.isDataTable', function() { ''; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isFalse(axe.commons.table.isDataTable(node)); }); @@ -439,7 +439,7 @@ describe('table.isDataTable', function() { ''; var node = fixture.querySelector('table'); - axe._tree = axe.utils.getFlattenedTree(fixture.firstChild); + axe.testUtils.flatTreeSetup(fixture.firstChild); assert.isTrue(axe.commons.table.isDataTable(node)); }); }); diff --git a/test/commons/text/accessible-text.js b/test/commons/text/accessible-text.js index 9280a82b6f..18e072db83 100644 --- a/test/commons/text/accessible-text.js +++ b/test/commons/text/accessible-text.js @@ -12,7 +12,7 @@ describe('text.accessibleTextVirtual', function() { it('is called through accessibleText with a DOM node', function() { var accessibleText = axe.commons.text.accessibleText; fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('input'); assert.equal(accessibleText(target), ''); }); @@ -31,7 +31,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var rule2a = axe.utils.querySelectorAll(axe._tree, '#rule2a')[0]; var rule2c = axe.utils.querySelectorAll(axe._tree, '#rule2c')[0]; @@ -56,7 +56,7 @@ describe('text.accessibleTextVirtual', function() { ' times' + ' ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var rule2a = axe.utils.querySelectorAll(axe._tree, '#beep')[0]; var rule2b = axe.utils.querySelectorAll(axe._tree, '#flash')[0]; @@ -77,7 +77,7 @@ describe('text.accessibleTextVirtual', function() { '
This is a label
' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#t1')[0]; assert.equal( @@ -93,7 +93,7 @@ describe('text.accessibleTextVirtual', function() { '
This is a label
' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#t1')[0]; assert.equal( @@ -109,7 +109,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#t1')[0]; assert.equal( @@ -121,7 +121,7 @@ describe('text.accessibleTextVirtual', function() { it('should allow setting the initial includeHidden value', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#lbl1')[0]; assert.equal( @@ -146,7 +146,7 @@ describe('text.accessibleTextVirtual', function() { '
This is a label
' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#t1')[0]; assert.equal(axe.commons.text.accessibleTextVirtual(target), 'ARIA Label'); @@ -160,7 +160,7 @@ describe('text.accessibleTextVirtual', function() { '
This is a label
' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#target')[0]; assert.equal( @@ -177,7 +177,7 @@ describe('text.accessibleTextVirtual', function() { '
This is a label
' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#target')[0]; assert.equal( @@ -194,7 +194,7 @@ describe('text.accessibleTextVirtual', function() { '
This is a label
' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#target')[0]; assert.equal(axe.commons.text.accessibleTextVirtual(target), ''); @@ -207,7 +207,7 @@ describe('text.accessibleTextVirtual', function() { '
This is a label
' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#t1')[0]; assert.equal(axe.commons.text.accessibleTextVirtual(target), 'HTML Label'); @@ -220,7 +220,7 @@ describe('text.accessibleTextVirtual', function() { '
This is a label
' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#t2label')[0]; assert.equal( @@ -236,7 +236,7 @@ describe('text.accessibleTextVirtual', function() { '
This is a label
' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#t2label')[0]; assert.equal( @@ -254,7 +254,7 @@ describe('text.accessibleTextVirtual', function() { '
This is a label
' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#t2label')[0]; // Chrome 72: This is This is a label of @@ -270,7 +270,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '
' + '
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#target')[0]; assert.equal( axe.commons.text.accessibleTextVirtual(target), @@ -283,7 +283,7 @@ describe('text.accessibleTextVirtual', function() { '
' + '
' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#target')[0]; assert.equal( axe.commons.text.accessibleTextVirtual(target), @@ -298,7 +298,7 @@ describe('text.accessibleTextVirtual', function() { '
This is a label
' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#t2label')[0]; assert.equal( @@ -317,7 +317,7 @@ describe('text.accessibleTextVirtual', function() { '
This is a label
' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#t2')[0]; assert.equal( @@ -333,7 +333,7 @@ describe('text.accessibleTextVirtual', function() { '
This is a label
' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#t2')[0]; assert.equal( @@ -349,7 +349,7 @@ describe('text.accessibleTextVirtual', function() { '
This is a label
' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#t2')[0]; // Chrome 70: "This is This is a label of everything" @@ -370,7 +370,7 @@ describe('text.accessibleTextVirtual', function() { '
This is a label
' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#t2')[0]; // Chrome 70: "This is This is a label of everything" @@ -389,7 +389,7 @@ describe('text.accessibleTextVirtual', function() { '
This is a label
' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#t2')[0]; // Chrome 70: "This is This is a label of everything" @@ -409,7 +409,7 @@ describe('text.accessibleTextVirtual', function() { '
This is a label
' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#t2')[0]; assert.equal( @@ -427,7 +427,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#target')[0]; // Chrome 70: "" // Firefox 62: "Chosen" @@ -445,7 +445,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#target')[0]; assert.equal(axe.commons.text.accessibleTextVirtual(target), ''); }); @@ -462,7 +462,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#target')[0]; assert.equal(axe.commons.text.accessibleTextVirtual(target), ''); }); @@ -476,7 +476,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, '#target')[0]; // Chrome 70: "" // Firefox 62: "" @@ -486,7 +486,7 @@ describe('text.accessibleTextVirtual', function() { it('shoud properly fall back to title', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, 'a')[0]; assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello'); @@ -494,7 +494,7 @@ describe('text.accessibleTextVirtual', function() { it('should give text even for role=presentation on anchors', function() { fixture.innerHTML = 'Hello'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, 'a')[0]; assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello'); @@ -502,7 +502,7 @@ describe('text.accessibleTextVirtual', function() { it('should give text even for role=presentation on buttons', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, 'button')[0]; assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello'); @@ -510,21 +510,21 @@ describe('text.accessibleTextVirtual', function() { it('should give text even for role=presentation on summary', function() { fixture.innerHTML = 'Hello'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, 'summary')[0]; assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello'); }); it('shoud properly fall back to title', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, 'a')[0]; assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello'); }); it('should give text even for role=none on anchors', function() { fixture.innerHTML = 'Hello'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, 'a')[0]; assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello'); @@ -532,7 +532,7 @@ describe('text.accessibleTextVirtual', function() { it('should give text even for role=none on buttons', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, 'button')[0]; assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello'); @@ -540,7 +540,7 @@ describe('text.accessibleTextVirtual', function() { it('should give text even for role=none on summary', function() { fixture.innerHTML = 'Hello'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, 'summary')[0]; assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello'); @@ -548,7 +548,7 @@ describe('text.accessibleTextVirtual', function() { it('should not add extra spaces around phrasing elements', function() { fixture.innerHTML = 'HelloWorld'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, 'a')[0]; assert.equal(axe.commons.text.accessibleTextVirtual(target), 'HelloWorld'); @@ -556,7 +556,7 @@ describe('text.accessibleTextVirtual', function() { it('should add spaces around non-phrasing elements', function() { fixture.innerHTML = 'Hello
World
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, 'a')[0]; assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello World'); @@ -565,7 +565,7 @@ describe('text.accessibleTextVirtual', function() { it('should not look at scripts', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = axe.utils.querySelectorAll(axe._tree, 'a')[0]; assert.equal(axe.commons.text.accessibleTextVirtual(target), ''); @@ -573,7 +573,7 @@ describe('text.accessibleTextVirtual', function() { it('should use ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo David'); }); @@ -1518,7 +1518,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy'); }); @@ -1531,7 +1531,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy Monday'); }); @@ -1544,7 +1544,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy 4'); }); @@ -1552,7 +1552,7 @@ describe('text.accessibleTextVirtual', function() { it('passes test 12', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy'); }); @@ -1564,7 +1564,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'fancy fruit'); }); @@ -1576,14 +1576,14 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'test content'); }); it('passes test 15', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), '1'); }); @@ -1591,7 +1591,7 @@ describe('text.accessibleTextVirtual', function() { it('passes test 16', function() { fixture.innerHTML = 'a'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), '1'); }); @@ -1602,7 +1602,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), ''); }); @@ -1610,7 +1610,7 @@ describe('text.accessibleTextVirtual', function() { it('passes test 18', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), ''); }); @@ -1621,7 +1621,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), ''); }); @@ -1629,7 +1629,7 @@ describe('text.accessibleTextVirtual', function() { it('passes test 20', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), '1'); }); @@ -1640,7 +1640,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'peanuts popcorn apple jacks'); }); @@ -1649,7 +1649,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'l peanuts'); }); @@ -1659,7 +1659,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'l peanuts popcorn'); }); @@ -1670,7 +1670,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + 'a'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'l peanuts popcorn apple jacks'); }); @@ -1681,7 +1681,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 't peanuts popcorn apple jacks'); }); @@ -1690,14 +1690,14 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '
foo
' + 'bar'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'bar'); }); it('passes test 27', function() { fixture.innerHTML = '
foo
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Tag'); }); @@ -1706,7 +1706,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '
foo
' + 'bar'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'bar'); }); @@ -1716,7 +1716,7 @@ describe('text.accessibleTextVirtual', function() { '
foo
' + 'bar' + 'baz'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'bar baz'); }); @@ -1724,14 +1724,14 @@ describe('text.accessibleTextVirtual', function() { // Should only pass in strict mode it('passes test 30', function() { fixture.innerHTML = '
Div with text
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target, { strict: true }), ''); }); it('passes test 31', function() { fixture.innerHTML = '
foo
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo'); }); @@ -1740,7 +1740,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '
' + '
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Tag'); }); @@ -1749,7 +1749,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '
foo
' + 'bar'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo'); }); @@ -1757,7 +1757,7 @@ describe('text.accessibleTextVirtual', function() { it('passes test 34', function() { fixture.innerHTML = 'ABC'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Tag'); }); @@ -1766,7 +1766,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = 'foo' + '

bar

'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'bar'); }); @@ -1775,21 +1775,21 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '' + '

foo

'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Tag foo'); }); it('passes test 37', function() { fixture.innerHTML = 'ABC'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'ABC'); }); it('passes test 38', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Tag'); }); @@ -1800,7 +1800,7 @@ describe('text.accessibleTextVirtual', function() { '

foo

' + '

bar

' + '

baz

'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo bar baz'); }); @@ -1809,7 +1809,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '' + '
foo'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo bar'); }); @@ -1817,7 +1817,7 @@ describe('text.accessibleTextVirtual', function() { it('passes test 41', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo'); }); @@ -1825,7 +1825,7 @@ describe('text.accessibleTextVirtual', function() { it('passes test 42', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo'); }); @@ -1834,7 +1834,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo'); }); @@ -1842,7 +1842,7 @@ describe('text.accessibleTextVirtual', function() { it('passes test 44', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo'); }); @@ -1850,7 +1850,7 @@ describe('text.accessibleTextVirtual', function() { it('passes test 45', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo'); }); @@ -1858,7 +1858,7 @@ describe('text.accessibleTextVirtual', function() { it('passes test 46', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo'); }); @@ -1867,7 +1867,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo bar baz'); }); @@ -1876,7 +1876,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo bar baz'); }); @@ -1885,7 +1885,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo bar baz'); }); @@ -1894,7 +1894,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo bar baz'); }); @@ -1903,7 +1903,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo bar baz'); }); @@ -1917,7 +1917,7 @@ describe('text.accessibleTextVirtual', function() { '
' + ' ' + '
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo bar baz'); }); @@ -1931,7 +1931,7 @@ describe('text.accessibleTextVirtual', function() { '
' + ' ' + '
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo bar baz'); }); @@ -1945,7 +1945,7 @@ describe('text.accessibleTextVirtual', function() { '
' + ' ' + '
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo baz'); }); @@ -1959,7 +1959,7 @@ describe('text.accessibleTextVirtual', function() { '
' + ' ' + '
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo baz'); }); @@ -1973,7 +1973,7 @@ describe('text.accessibleTextVirtual', function() { '
' + ' ' + '
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo baz'); }); @@ -1987,7 +1987,7 @@ describe('text.accessibleTextVirtual', function() { '
' + ' ' + '
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo baz'); }); @@ -1996,7 +1996,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'States:'); }); @@ -2005,7 +2005,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'States:'); }); @@ -2013,7 +2013,7 @@ describe('text.accessibleTextVirtual', function() { it('passes test 60', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'States:'); }); @@ -2021,7 +2021,7 @@ describe('text.accessibleTextVirtual', function() { it('passes test 61', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'File:'); }); @@ -2030,7 +2030,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'States:'); }); @@ -2042,7 +2042,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo David'); }); @@ -2054,7 +2054,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo David'); }); @@ -2066,7 +2066,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo David'); }); @@ -2078,7 +2078,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo David'); }); @@ -2090,7 +2090,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo David'); }); @@ -2105,7 +2105,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy'); }); @@ -2120,7 +2120,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy'); }); @@ -2135,7 +2135,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy'); }); @@ -2150,7 +2150,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy'); }); @@ -2165,7 +2165,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy'); }); @@ -2178,7 +2178,7 @@ describe('text.accessibleTextVirtual', function() { '
' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy Monday'); }); @@ -2191,7 +2191,7 @@ describe('text.accessibleTextVirtual', function() { ' aria-valuemin="1" aria-valuemax="7" aria-valuenow="4">' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy Monday'); }); @@ -2204,7 +2204,7 @@ describe('text.accessibleTextVirtual', function() { ' aria-valuemin="1" aria-valuemax="7" aria-valuenow="4">' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy Monday'); }); @@ -2217,7 +2217,7 @@ describe('text.accessibleTextVirtual', function() { ' aria-valuemin="1" aria-valuemax="7" aria-valuenow="4">' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy Monday'); }); @@ -2230,7 +2230,7 @@ describe('text.accessibleTextVirtual', function() { ' aria-valuemax="7" aria-valuenow="4">' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy Monday'); }); @@ -2243,7 +2243,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy 4'); }); @@ -2256,7 +2256,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy 4'); }); @@ -2269,7 +2269,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy 4'); }); @@ -2282,7 +2282,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy 4'); }); @@ -2295,7 +2295,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy 4'); }); @@ -2303,28 +2303,28 @@ describe('text.accessibleTextVirtual', function() { it('passes test 83', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy'); }); it('passes test 84', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy'); }); it('passes test 85', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy'); }); it('passes test 86', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy'); }); @@ -2332,7 +2332,7 @@ describe('text.accessibleTextVirtual', function() { it('passes test 87', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'crazy'); }); @@ -2344,7 +2344,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'fancy fruit'); }); @@ -2356,7 +2356,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'fancy fruit'); }); @@ -2368,7 +2368,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'fancy fruit'); }); @@ -2380,7 +2380,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'fancy fruit'); }); @@ -2392,7 +2392,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'fancy fruit'); }); @@ -2404,7 +2404,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'fancy fruit'); }); @@ -2416,7 +2416,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'fancy fruit'); }); @@ -2428,7 +2428,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'fancy fruit'); }); @@ -2440,7 +2440,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'fancy fruit'); }); @@ -2452,7 +2452,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'fancy fruit'); }); @@ -2471,7 +2471,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' times.' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); // Chrome 72: "Flash the screen 1 times" // Safari 12.0: "Flash the screen 1 times" @@ -2490,7 +2490,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' times.' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Flash the screen times.'); }); @@ -2506,7 +2506,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' times.' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Flash the screen 1 times.'); }); @@ -2516,7 +2516,7 @@ describe('text.accessibleTextVirtual', function() { '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo 5 baz'); }); @@ -2526,14 +2526,14 @@ describe('text.accessibleTextVirtual', function() { '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo 5 baz'); }); it('passes test 103', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo'); }); @@ -2552,7 +2552,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' times.' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Flash the screen 1 times.'); }); @@ -2568,7 +2568,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' times.' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Flash the screen times.'); }); @@ -2584,7 +2584,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' times.' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Flash the screen 1 times.'); }); @@ -2594,7 +2594,7 @@ describe('text.accessibleTextVirtual', function() { '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo 5 baz'); }); @@ -2604,14 +2604,14 @@ describe('text.accessibleTextVirtual', function() { '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo 5 baz'); }); it('passes test 109', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo'); }); @@ -2619,7 +2619,7 @@ describe('text.accessibleTextVirtual', function() { it('passes test 110', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo'); }); @@ -2638,7 +2638,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' times.' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Flash the screen 1 times.'); }); @@ -2654,7 +2654,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' times.' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Flash the screen times.'); }); @@ -2670,7 +2670,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' times.' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Flash the screen 1 times.'); }); @@ -2680,7 +2680,7 @@ describe('text.accessibleTextVirtual', function() { '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo 5 baz'); }); @@ -2690,14 +2690,14 @@ describe('text.accessibleTextVirtual', function() { '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo 5 baz'); }); it('passes test 116', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo'); }); @@ -2716,7 +2716,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' times.' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Flash the screen 1 times.'); }); @@ -2732,7 +2732,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' times.' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Flash the screen times.'); }); @@ -2748,7 +2748,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' times.' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); // Chrome 72: "Flash the screen 1 times" // Firefox 62: "Flash the screen 1 times" @@ -2761,7 +2761,7 @@ describe('text.accessibleTextVirtual', function() { '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); // Chrome 70: Foo 5 baz // Firefox 62: Foo 5 baz @@ -2774,14 +2774,14 @@ describe('text.accessibleTextVirtual', function() { '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo 5 baz'); }); it('passes test 122', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo'); }); @@ -2800,7 +2800,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' times.' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); // Chrome 72: "Flash the screen 1 times." // Firefox 62: "Flash the screen 1 times." @@ -2819,7 +2819,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' times.' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Flash the screen times.'); }); @@ -2835,7 +2835,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' times.' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); // Chrome 72: Flash the screen 1 times // Firefox 62: Flash the screen 1 times @@ -2848,7 +2848,7 @@ describe('text.accessibleTextVirtual', function() { '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo 5 baz'); }); @@ -2858,14 +2858,14 @@ describe('text.accessibleTextVirtual', function() { '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo 5 baz'); }); it('passes test 128', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'foo'); }); @@ -2900,7 +2900,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); // Chrome 72: "My name is Eli the weird. (QED) Where are my marbles?" // Safari 12.0: "My name is Eli the weird. (QED) Where are my marbles?" @@ -2940,7 +2940,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal( accessibleText(target), @@ -2976,7 +2976,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal( accessibleText(target), @@ -3019,7 +3019,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' ' + ' '; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Important stuff'); }); @@ -3027,7 +3027,7 @@ describe('text.accessibleTextVirtual', function() { it('passes test 144', function() { fixture.innerHTML = ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Choose your language'); }); @@ -3037,7 +3037,7 @@ describe('text.accessibleTextVirtual', function() { '
' + ' English ' + '
'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Choose your language.'); }); @@ -3053,7 +3053,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' times.' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Flash the screen 1 times.'); }); @@ -3065,7 +3065,7 @@ describe('text.accessibleTextVirtual', function() { '
1
' + ' times.' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Flash the screen 1 times.'); }); @@ -3074,7 +3074,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'a test This is'); }); @@ -3083,7 +3083,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'This is a test'); }); @@ -3092,7 +3092,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'What is your name?'); }); @@ -3105,7 +3105,7 @@ describe('text.accessibleTextVirtual', function() { '' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'This is a test.'); }); @@ -3127,7 +3127,7 @@ describe('text.accessibleTextVirtual', function() { // ' 10' + ' 910' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), '2 4 6 8 10'); }); @@ -3148,7 +3148,7 @@ describe('text.accessibleTextVirtual', function() { '
  • 3
  • ' + ' ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Flash the screen 2 times.'); }); @@ -3167,7 +3167,7 @@ describe('text.accessibleTextVirtual', function() { ' ' + ' ' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Flash the screen 1 times.'); }); @@ -3189,7 +3189,7 @@ describe('text.accessibleTextVirtual', function() { // ' ' + ' and don\'t you forget it.' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'My name is Eli the weird. (QED)'); }); @@ -3198,7 +3198,7 @@ describe('text.accessibleTextVirtual', function() { fixture.innerHTML = 'United States'; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'California'); }); @@ -3209,12 +3209,12 @@ describe('text.accessibleTextVirtual', function() { 'Country of origin:' + '' + ''; - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#test'); assert.equal(accessibleText(target), 'Country of origin: United States'); }); - /** + /** // In case anyone even wants it, here's the script used to generate these test cases function getTestCase(content, index = 0) { const regex = new RegExp('if given\n([^]*)\nthen the accessible name of the element with id of "(.*)" is "(.*)"') @@ -3222,16 +3222,16 @@ describe('text.accessibleTextVirtual', function() { if (!out || out.length !== 4) { return; } - + const [, html, id, expected] = out; const strings = html.split(/\n/g).map( line => `'${line.substr(2)}'` ).join(' +\n ') + ';' - + return ` it('passes test ${index + 1}', function () { fixture.innerHTML = ${strings} - axe._tree = axe.utils.getFlattenedTree(fixture); + axe.testUtils.flatTreeSetup(fixture); var target = fixture.querySelector('#${id}'); assert.equal(accessibleText(target), '${expected}'); });` diff --git a/test/commons/text/form-control-value.js b/test/commons/text/form-control-value.js index b75822867d..af6dbc98ca 100644 --- a/test/commons/text/form-control-value.js +++ b/test/commons/text/form-control-value.js @@ -3,6 +3,7 @@ describe('text.formControlValue', function() { var formControlValue = axe.commons.text.formControlValue; var fixtureSetup = axe.testUtils.fixtureSetup; var fixture = document.querySelector('#fixture'); + var isIE11 = axe.testUtils.isIE11; function queryFixture(code, query) { fixtureSetup(code); @@ -119,29 +120,33 @@ describe('text.formControlValue', function() { }); }); - it('returns `` for non-text input elements', function() { - fixtureSetup( - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' - ); - axe.utils - .querySelectorAll(axe._tree[0], '#fixture input') - .forEach(function(target) { - assert.equal( - nativeTextboxValue(target), - '', - 'Expected no value for ' + target.actualNode.outerHTML - ); - }); - }); + // This currently breaks in IE11 + (isIE11 ? it.skip : it)( + 'returns `` for non-text input elements', + function() { + fixtureSetup( + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + ); + axe.utils + .querySelectorAll(axe._tree[0], '#fixture input') + .forEach(function(target) { + assert.equal( + nativeTextboxValue(target), + '', + 'Expected no value for ' + target.actualNode.outerHTML + ); + }); + } + ); it('returns the value of DOM nodes', function() { fixture.innerHTML = ''; diff --git a/test/commons/text/label-text.js b/test/commons/text/label-text.js index efb763fe2a..2d5ac9ef9e 100644 --- a/test/commons/text/label-text.js +++ b/test/commons/text/label-text.js @@ -1,125 +1,120 @@ describe('text.labelText', function() { - var labelText = axe.commons.text.labelText; var queryFixture = axe.testUtils.queryFixture; - it('returns the text of an implicit label', function () { + it('returns the text of an implicit label', function() { var target = queryFixture( - '' - ) - assert.equal(labelText(target), 'My implicit label') - }) + '' + ); + assert.equal(labelText(target), 'My implicit label'); + }); - it('returns the text of an explicit label', function () { + it('returns the text of an explicit label', function() { var target = queryFixture( - '' + - '' - ) - assert.equal(labelText(target), 'My explicit label') - }) + '' + '' + ); + assert.equal(labelText(target), 'My explicit label'); + }); - it('ignores the text of nested implicit labels', function () { + it('ignores the text of nested implicit labels', function() { var target = queryFixture( '' - ) - assert.equal(labelText(target), 'My inner label') - }) + '' + + '' + ); + assert.equal(labelText(target), 'My inner label'); + }); - it('concatinates multiple explicit labels', function () { + it('concatinates multiple explicit labels', function() { var target = queryFixture( '' + - '' + - '' - ) - assert.equal(labelText(target), 'My label 1 My label 2') - }) + '' + + '' + ); + assert.equal(labelText(target), 'My label 1 My label 2'); + }); - it('concatinates explicit and implicit labels', function () { + it('concatinates explicit and implicit labels', function() { var target = queryFixture( '' + - '' - ) - assert.equal(labelText(target), 'My explicit label My implicit label') - }) + '' + ); + assert.equal(labelText(target), 'My explicit label My implicit label'); + }); - it('returns label text in the DOM order', function () { + it('returns label text in the DOM order', function() { var target = queryFixture( '' + - '' + - '' - ) - assert.equal(labelText(target), 'Label 1 My implicit Label 2 Label 3') - }) + '' + + '' + ); + assert.equal(labelText(target), 'Label 1 My implicit Label 2 Label 3'); + }); - it('does not return the same label twice', function () { + it('does not return the same label twice', function() { var target = queryFixture( '' - ) - assert.equal(labelText(target), 'My implicit and explicit label') - }) + 'My implicit and explicit label' + + '' + + '' + ); + assert.equal(labelText(target), 'My implicit and explicit label'); + }); - it('ignores the value of a textbox', function () { + it('ignores the value of a textbox', function() { var target = queryFixture( '' - ) - assert.equal(labelText(target), 'My label') - }) + '' + + '' + ); + assert.equal(labelText(target), 'My label'); + }); - it('ignores the content of a textarea', function () { + it('ignores the content of a textarea', function() { var target = queryFixture( '