diff --git a/doc/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
similarity index 100%
rename from doc/.github/ISSUE_TEMPLATE.md
rename to .github/ISSUE_TEMPLATE.md
diff --git a/doc/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
similarity index 100%
rename from doc/.github/PULL_REQUEST_TEMPLATE.md
rename to .github/PULL_REQUEST_TEMPLATE.md
diff --git a/.markdownlint.json b/.markdownlint.json
new file mode 100644
index 0000000000..784c3bce07
--- /dev/null
+++ b/.markdownlint.json
@@ -0,0 +1,24 @@
+{
+ "default": true,
+ "MD001": false,
+ "MD002": false,
+ "MD004": false,
+ "MD006": false,
+ "MD007": false,
+ "MD009": false,
+ "MD010": false,
+ "MD012": false,
+ "MD013": false,
+ "MD022": false,
+ "MD024": false,
+ "MD026": false,
+ "MD029": false,
+ "MD030": false,
+ "MD031": false,
+ "MD032": false,
+ "MD033": false,
+ "MD034": false,
+ "MD036": false,
+ "MD040": false,
+ "MD041": false
+}
diff --git a/.retireignore.json b/.retireignore.json
index eba3029354..a6de6668ea 100644
--- a/.retireignore.json
+++ b/.retireignore.json
@@ -11,6 +11,10 @@
"path": "node_modules/grunt-retire",
"justification" : "Used only for testing"
},
+ {
+ "path": "node_modules/sshpk",
+ "justification" : "A dependency of retirejs/request"
+ },
{
"path": "node_modules/grunt-mocha",
"justification" : "Used only for testing"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 65dc426f17..4ac77b6274 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,21 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+## [3.0.2](https://github.com/dequelabs/axe-core/compare/v3.0.0-beta.2...v3.0.2) (2018-04-24)
+
+
+### Bug Fixes
+
+* **rule:** Allow empty aria-labelledby values ([#829](https://github.com/dequelabs/axe-core/issues/829)) ([d280c5f](https://github.com/dequelabs/axe-core/commit/d280c5f))
+* Prevent color rules from crashing Chrome 66+ [#856](https://github.com/dequelabs/axe-core/issues/856) ([#861](https://github.com/dequelabs/axe-core/issues/861)) ([147b665](https://github.com/dequelabs/axe-core/commit/147b665))
+* **respondable:** Identify the current axe instance by its application name when it exists ([affd75c](https://github.com/dequelabs/axe-core/commit/affd75c))
+* **respondable:** Use the hard-coded axe.application name as default ([ab4a49f](https://github.com/dequelabs/axe-core/commit/ab4a49f))
+* **rule:** Ignore hashbang URLs for skiplinks ([#827](https://github.com/dequelabs/axe-core/issues/827)) ([e1f0c57](https://github.com/dequelabs/axe-core/commit/e1f0c57))
+* **rule:** Tag video-caption only as SC 1.2.2 ([87818e7](https://github.com/dequelabs/axe-core/commit/87818e7))
+
+
+
## [3.0.1](https://github.com/dequelabs/axe-core/compare/v3.0.0...v3.0.1) (2018-04-03)
diff --git a/Gruntfile.js b/Gruntfile.js
index 7c9c7b6090..64f5058cd5 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -17,6 +17,7 @@ module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-mocha');
grunt.loadTasks('build/tasks');
grunt.loadNpmTasks('grunt-parallel');
+ grunt.loadNpmTasks('grunt-markdownlint');
var langs;
if (grunt.option('lang')) {
@@ -334,6 +335,18 @@ module.exports = function (grunt) {
'!**/node_modules/**/*.js'
]
}
+ },
+ markdownlint: {
+ all: {
+ options: {
+ config: grunt.file.readJSON('.markdownlint.json')
+ },
+ src: [
+ 'README.md',
+ '.github/*.md',
+ 'doc/**/*.md'
+ ]
+ }
}
});
@@ -343,7 +356,7 @@ module.exports = function (grunt) {
'babel', 'concat:engine', 'uglify']);
grunt.registerTask('test', ['build', 'retire', 'testconfig', 'fixture', 'connect',
- 'mocha', 'parallel', 'eslint']);
+ 'mocha', 'parallel', 'eslint', 'markdownlint']);
grunt.registerTask('ci-build', ['build', 'retire', 'testconfig', 'fixture', 'connect',
'parallel', 'eslint']);
diff --git a/README.md b/README.md
index fcada14574..1dd9fabf28 100644
--- a/README.md
+++ b/README.md
@@ -63,12 +63,17 @@ axe.run(function (err, results) {
```
## Supported Browsers
-The [aXe API](doc/API.md) supports the following browsers:
+The [aXe API](doc/API.md) fully supports the following browsers:
-* Internet Explorer v9, 10, 11
+* Microsoft Edge v40 and above
* Google Chrome v42 and above
* Mozilla Firefox v38 and above
* Apple Safari v7 and above
+* Internet Explorer v9, 10, 11
+
+Support means that we will fix bugs and attempt to test each browser regularly. Only Firefox and Chrome are currently tested on every pull request.
+
+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.
## The Accessibility Rules
@@ -95,7 +100,7 @@ To update existing translation file, re-run `grunt translate --lang=`.
## Contributing
-Read the [Proposing Axe-core Rules guide ](./doc/rule-proposal.md)
+Read the [Proposing Axe-core Rules guide](./doc/rule-proposal.md)
Read the [documentation on the architecture](./doc/developer-guide.md)
diff --git a/axe.d.ts b/axe.d.ts
index 9c7ff12a4a..d8f3ae8fb3 100644
--- a/axe.d.ts
+++ b/axe.d.ts
@@ -1,4 +1,4 @@
-// Type definitions for axe-core 3.0.1
+// Type definitions for axe-core 3.0.2
// Project: https://github.com/dequelabs/axe-core
// Definitions by: Marcy Sutton
@@ -13,18 +13,14 @@ declare module axe {
type RunOnlyType = "rule" | "rules" | "tag" | "tags";
type RunOnlyObject = {
- include?: string[],
- exclude?: string[]
+ include?: string[] | string[][],
+ exclude?: string[] | string[][]
}
type RunCallback = (error: Error, results:AxeResults) => void;
- interface ElementContext {
- node?: Object,
- selector?: string,
- include?: any[],
- exclude?: any[]
- }
+ type ElementContext = Node | string | RunOnlyObject;
+
interface RunOnly {
type: RunOnlyType,
values?: TagValue[] | RunOnlyObject
@@ -132,7 +128,7 @@ declare module axe {
* @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.
*/
- function run(context: ElementContext): Promise
+ function run(context?: ElementContext): Promise
function run(options: RunOptions): Promise
function run(callback: (error: Error, results:AxeResults) => void): void
function run(context: ElementContext, callback: RunCallback): void
diff --git a/bower.json b/bower.json
index 9c88b5f872..0160188fd1 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
{
"name": "axe-core",
- "version": "3.0.1",
+ "version": "3.0.2",
"contributors": [
{
"name": "David Sturley",
diff --git a/doc/rule-descriptions.md b/doc/rule-descriptions.md
index a0df1e7d8f..68e1a42261 100644
--- a/doc/rule-descriptions.md
+++ b/doc/rule-descriptions.md
@@ -15,7 +15,7 @@
| blink | Ensures <blink> elements are not used | cat.time-and-media, wcag2a, wcag222, section508, section508.22.j | true |
| button-name | Ensures buttons have discernible text | 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 | cat.keyboard, wcag2a, wcag241, section508, section508.22.o | true |
-| checkboxgroup | Ensures related <input type="checkbox"> elements have a group and that that group designation is consistent | cat.forms, best-practice | true |
+| checkboxgroup | Ensures related <input type="checkbox"> elements have a group and that the group designation is consistent | cat.forms, best-practice | true |
| color-contrast | Ensures the contrast between foreground and background colors meets WCAG 2 AA contrast ratio thresholds | cat.color, wcag2aa, wcag143 | true |
| definition-list | Ensures <dl> elements are structured correctly | cat.structure, wcag2a, wcag131 | true |
| dlitem | Ensures <dt> and <dd> elements are contained by a <dl> | cat.structure, wcag2a, wcag131 | true |
@@ -65,5 +65,5 @@
| td-headers-attr | Ensure that each cell in a table using the headers refers to another cell in that table | cat.tables, wcag2a, wcag131, section508, section508.22.g | true |
| th-has-data-cells | Ensure that each table header in a data table refers to data cells | cat.tables, wcag2a, wcag131, section508, section508.22.g | true |
| valid-lang | Ensures lang attributes have valid values | cat.language, wcag2aa, wcag312 | true |
-| video-caption | Ensures <video> elements have captions | cat.text-alternatives, wcag2a, wcag122, wcag123, section508, section508.22.a | true |
+| video-caption | Ensures <video> elements have captions | cat.text-alternatives, wcag2a, wcag122, section508, section508.22.a | true |
| video-description | Ensures <video> elements have audio descriptions | cat.text-alternatives, wcag2aa, wcag125, section508, section508.22.b | true |
\ No newline at end of file
diff --git a/lib/checks/navigation/internal-link-present.js b/lib/checks/navigation/internal-link-present.js
index ad7fd17cb6..56cddbb62c 100644
--- a/lib/checks/navigation/internal-link-present.js
+++ b/lib/checks/navigation/internal-link-present.js
@@ -1,2 +1,4 @@
const links = axe.utils.querySelectorAll(virtualNode, 'a[href]');
-return links.some(vLink => vLink.actualNode.getAttribute('href')[0] === '#');
+return links.some(vLink => {
+ return /^#[^/!]/.test(vLink.actualNode.getAttribute('href'))
+});
diff --git a/lib/commons/aria/attributes.js b/lib/commons/aria/attributes.js
index c9d8323d5a..b444af85cf 100644
--- a/lib/commons/aria/attributes.js
+++ b/lib/commons/aria/attributes.js
@@ -55,7 +55,7 @@ aria.validateAttr = function (att) {
* @return {Boolean}
*/
aria.validateAttrValue = function (node, attr) {
- /*eslint complexity: ["error",15]*/
+ /*eslint complexity: ["error",17]*/
'use strict';
var matches, list,
value = node.getAttribute(attr),
@@ -84,6 +84,10 @@ aria.validateAttrValue = function (node, attr) {
return !!(value && doc.getElementById(value));
case 'idrefs':
+ // exempt attributes that allow empty strings
+ if ((attrInfo.values && attrInfo.values.indexOf('') !== -1) && value.trim().length === 0) {
+ return true;
+ }
list = axe.utils.tokenList(value);
// Check if any value isn't in the list of values
return list.reduce(function (result, token) {
diff --git a/lib/commons/aria/index.js b/lib/commons/aria/index.js
index b84460f7bc..7b09042504 100644
--- a/lib/commons/aria/index.js
+++ b/lib/commons/aria/index.js
@@ -87,7 +87,8 @@ lookupTable.attributes = {
type: 'string'
},
'aria-labelledby': {
- type: 'idrefs'
+ type: 'idrefs',
+ values: ['']
},
'aria-level': {
type: 'int'
diff --git a/lib/commons/dom/shadow-elements-from-point.js b/lib/commons/dom/shadow-elements-from-point.js
index 84765bc10e..34c5c6abcd 100644
--- a/lib/commons/dom/shadow-elements-from-point.js
+++ b/lib/commons/dom/shadow-elements-from-point.js
@@ -9,11 +9,17 @@
* @param {Object} [root] Shadow root or document root
* @return {Array}
*/
-dom.shadowElementsFromPoint = function(nodeX, nodeY, root = document) {
- return root.elementsFromPoint(nodeX, nodeY)
+dom.shadowElementsFromPoint = function(nodeX, nodeY, root = document, i=0) {
+ if (i > 999) {
+ throw new Error('Infinite loop detected');
+ }
+ return Array.from(root.elementsFromPoint(nodeX, nodeY))
+ // As of Chrome 66, elementFromPoint will return elements from parent trees.
+ // We only want to touch each tree once, so we're filtering out nodes on other trees.
+ .filter(nodes => dom.getRootNode(nodes) === root)
.reduce((stack, elm) => {
if (axe.utils.isShadowRoot(elm)) {
- const shadowStack = dom.shadowElementsFromPoint(nodeX, nodeY, elm.shadowRoot);
+ const shadowStack = dom.shadowElementsFromPoint(nodeX, nodeY, elm.shadowRoot, i+1);
stack = stack.concat(shadowStack);
// filter host nodes which get included regardless of overlap
// TODO: refactor multiline overlap checking inside shadow dom
diff --git a/lib/core/utils/respondable.js b/lib/core/utils/respondable.js
index ae00ddd8e3..ef3af5ecc0 100644
--- a/lib/core/utils/respondable.js
+++ b/lib/core/utils/respondable.js
@@ -11,8 +11,8 @@
* @private
*/
function _getSource() {
- var application = 'axe', version = '', src;
- if (typeof axe !== 'undefined' && axe._audit && !axe._audit.application) {
+ var application = 'axeAPI', version = '', src;
+ if (typeof axe !== 'undefined' && axe._audit && axe._audit.application) {
application = axe._audit.application;
}
if (typeof axe !== 'undefined') {
@@ -37,8 +37,8 @@
return ( // Check the version matches
postedMessage._source === messageSource ||
// Allow free communication with axe test
- postedMessage._source === 'axe.x.y.z' ||
- messageSource === 'axe.x.y.z'
+ postedMessage._source === 'axeAPI.x.y.z' ||
+ messageSource === 'axeAPI.x.y.z'
);
}
return false;
@@ -135,16 +135,16 @@
/**
* Publishes the "respondable" message to the appropriate subscriber
* @private
- * @param {Event} event The event object of the postMessage
- * @param {Object} data The data sent with the message
- * @param {Boolean} keepalive Whether to allow multiple responses - default is false
+ * @param {Window} source The window from which the message originated
+ * @param {Object} data The data sent with the message
+ * @param {Boolean} keepalive Whether to allow multiple responses - default is false
*/
- function publish(target, data, keepalive) {
+ function publish(source, data, keepalive) {
var topic = data.topic;
var subscriber = subscribers[topic];
if (subscriber) {
- var responder = createResponder(target, null, data.uuid);
+ var responder = createResponder(source, null, data.uuid);
subscriber(data.message, keepalive, responder);
}
}
diff --git a/lib/rules/checkboxgroup.json b/lib/rules/checkboxgroup.json
index 23ac7aa02f..185b16abab 100644
--- a/lib/rules/checkboxgroup.json
+++ b/lib/rules/checkboxgroup.json
@@ -6,7 +6,7 @@
"best-practice"
],
"metadata": {
- "description": "Ensures related elements have a group and that that group designation is consistent",
+ "description": "Ensures related elements have a group and that the group designation is consistent",
"help": "Checkbox inputs with the same name attribute value must be part of a group"
},
"all": [],
diff --git a/lib/rules/layout-table-matches.js b/lib/rules/layout-table-matches.js
index bc1598ffde..373c7ef8fd 100644
--- a/lib/rules/layout-table-matches.js
+++ b/lib/rules/layout-table-matches.js
@@ -1 +1,5 @@
-return !axe.commons.table.isDataTable(node);
\ No newline at end of file
+var role = (node.getAttribute('role') || '').toLowerCase();
+
+return !((role === 'presentation' || role === 'none') &&
+ !axe.commons.dom.isFocusable(node)) &&
+ !axe.commons.table.isDataTable(node);
\ No newline at end of file
diff --git a/lib/rules/skip-link-matches.js b/lib/rules/skip-link-matches.js
index 7ee5bcbd17..24bab16507 100644
--- a/lib/rules/skip-link-matches.js
+++ b/lib/rules/skip-link-matches.js
@@ -1,2 +1 @@
-const href = node.getAttribute('href');
-return (href[0] === '#' && href.length > 1);
+return /^#[^/!]/.test(node.getAttribute('href'));
diff --git a/lib/rules/video-caption.json b/lib/rules/video-caption.json
index 81470fb595..7b052f3157 100644
--- a/lib/rules/video-caption.json
+++ b/lib/rules/video-caption.json
@@ -6,7 +6,6 @@
"cat.text-alternatives",
"wcag2a",
"wcag122",
- "wcag123",
"section508",
"section508.22.a"
],
diff --git a/package.json b/package.json
index 6e2bdc6f41..7850b7598f 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "axe-core",
"description": "Accessibility engine for automated Web UI testing",
- "version": "3.0.1",
+ "version": "3.0.2",
"license": "MPL-2.0",
"engines": {
"node": ">=4"
@@ -78,6 +78,7 @@
"grunt-contrib-uglify": "^2.1.0",
"grunt-contrib-watch": "^1.0.0",
"grunt-eslint": "^20.1.0",
+ "grunt-markdownlint": "^1.1.2",
"grunt-mocha": "^1.0.4",
"grunt-parallel": "^0.5.1",
"grunt-retire": "^1.0.7",
diff --git a/sri-history.json b/sri-history.json
index 34eac1e717..429101c6a1 100644
--- a/sri-history.json
+++ b/sri-history.json
@@ -102,5 +102,9 @@
"3.0.1": {
"axe.js": "sha256-Vf/arxSrHppK2X5x6VgBZLJnCy8yK6P6uH99WwzQ30s=",
"axe.min.js": "sha256-vMPyo7vifw5RTaVEAlnfwGFa9VyHymsNqanCsHh3Q8c="
+ },
+ "3.0.2": {
+ "axe.js": "sha256-D24i3Yy35gMxOZNTNZyQLAyL3W3wVvW1wUYakK5v1VI=",
+ "axe.min.js": "sha256-Hsc1oDUNhkVBP4gVUaC9jNm9t0qmLpTJzXW4uzx10bo="
}
}
\ No newline at end of file
diff --git a/test/checks/aria/valid-attr-value.js b/test/checks/aria/valid-attr-value.js
index a975e16b25..2763882d10 100644
--- a/test/checks/aria/valid-attr-value.js
+++ b/test/checks/aria/valid-attr-value.js
@@ -3,6 +3,7 @@ describe('aria-valid-attr-value', function () {
var fixture = document.getElementById('fixture');
var checkContext = axe.testUtils.MockCheckContext();
+ var fixtureSetup = axe.testUtils.fixtureSetup;
afterEach(function () {
fixture.innerHTML = '';
@@ -89,6 +90,17 @@ describe('aria-valid-attr-value', function () {
axe.commons.aria.validateAttrValue = orig;
});
+ it('should allow empty strings rather than idrefs for specific attributes', function () {
+ fixtureSetup(
+ 'Button ' +
+ '
'
+ );
+ var passing = fixture.querySelector('button');
+ var failing = fixture.querySelector('div');
+ assert.isTrue(checks['aria-valid-attr-value'].evaluate.call(checkContext, passing));
+ assert.isFalse(checks['aria-valid-attr-value'].evaluate.call(checkContext, failing));
+ });
+
describe('options', function () {
it('should exclude supplied attributes', function () {
fixture.innerHTML = '
';
diff --git a/test/checks/media/frame-tested.js b/test/checks/media/frame-tested.js
index fa2f9f59fd..3187f6915c 100644
--- a/test/checks/media/frame-tested.js
+++ b/test/checks/media/frame-tested.js
@@ -1,11 +1,8 @@
describe('frame-tested', function () {
'use strict';
- var checkContext = axe.testUtils.MockCheckContext();
- var __respondable;
- var respondableCalls = [];
- var checkEvaluate = checks['frame-tested'].evaluate.bind(checkContext);
- var iframe;
+ var checkContext, __respondable, respondableCalls, iframe;
+ var checkEvaluate = checks['frame-tested'].evaluate
before(function () {
__respondable = axe.utils.respondable;
@@ -16,9 +13,11 @@ describe('frame-tested', function () {
document.querySelector('#fixture').appendChild(iframe);
});
- afterEach(function () {
+ beforeEach(function () {
respondableCalls = [];
- checkContext.reset();
+ checkContext = axe.testUtils.MockCheckContext();
+ // Don't throw on async
+ checkContext._onAsync = function () {};
})
after(function () {
@@ -26,7 +25,7 @@ describe('frame-tested', function () {
});
it('correctly calls axe.utils.respondable', function () {
- checkEvaluate(iframe);
+ checkEvaluate.call(checkContext, iframe);
assert.lengthOf(respondableCalls, 1);
assert.deepEqual(respondableCalls[0].slice(0,4),
@@ -35,30 +34,30 @@ describe('frame-tested', function () {
});
it('passes if the iframe contains axe-core', function (done) {
- checkEvaluate(iframe, { timeout: 20 });
checkContext._onAsync = function (result) {
assert.isTrue(result);
done();
}
+ checkEvaluate.call(checkContext, iframe, { timeout: 20 });
// Respond to the ping
respondableCalls[0][4]();
});
it('fails if the iframe does not contain axe-core, and isViolation is true', function (done) {
- checkEvaluate(iframe, { timeout: 10, isViolation: true });
- // Timeout after 10ms
checkContext._onAsync = function (result) {
assert.isFalse(result);
done();
}
+ // Timeout after 10ms
+ checkEvaluate.call(checkContext, iframe, { timeout: 10, isViolation: true });
});
it('is incomplete if the iframe does not contain axe-core', function (done) {
- checkEvaluate(iframe, { timeout: 10 });
- // Timeout after 10ms
checkContext._onAsync = function (result) {
assert.isUndefined(result);
done();
}
+ // Timeout after 10ms
+ checkEvaluate.call(checkContext, iframe, { timeout: 10 });
});
});
diff --git a/test/checks/navigation/internal-link-present.js b/test/checks/navigation/internal-link-present.js
index c6d372f09b..172dc2dcc2 100644
--- a/test/checks/navigation/internal-link-present.js
+++ b/test/checks/navigation/internal-link-present.js
@@ -9,7 +9,7 @@ describe('internal-link-present', function () {
afterEach(function () {
fixture.innerHTML = '';
- axe._tree = undefined;
+ axe._tree = undefined;
checkContext.reset();
});
@@ -18,6 +18,21 @@ describe('internal-link-present', function () {
assert.isTrue(checks['internal-link-present'].evaluate.apply(checkContext, params));
});
+ it('should return false when a hashbang URL was used', function () {
+ var params = checkSetup('');
+ assert.isFalse(checks['internal-link-present'].evaluate.apply(checkContext, params));
+ });
+
+ it('should return false when a hash route URL was used', function () {
+ var params = checkSetup('');
+ assert.isFalse(checks['internal-link-present'].evaluate.apply(checkContext, params));
+ });
+
+ it('should return false when a hashbang + slash route URL was used', function () {
+ var params = checkSetup('');
+ assert.isFalse(checks['internal-link-present'].evaluate.apply(checkContext, params));
+ });
+
it('should otherwise return false', function () {
var params = checkSetup('');
assert.isFalse(checks['internal-link-present'].evaluate.apply(checkContext, params));
diff --git a/test/core/utils/respondable.js b/test/core/utils/respondable.js
index d13249b72d..f15fd36827 100644
--- a/test/core/utils/respondable.js
+++ b/test/core/utils/respondable.js
@@ -118,7 +118,7 @@ describe('axe.utils.respondable', function () {
event.initEvent('message', true, true);
event.data = JSON.stringify({
_respondable: true,
- _source: 'axe.2.0.0',
+ _source: 'axeAPI.2.0.0',
topic: 'Death star',
message: 'Help us Obi-Wan',
uuid: mockUUID
@@ -133,14 +133,14 @@ describe('axe.utils.respondable', function () {
assert.isTrue(success);
});
- it('should allow messages with _source axe.x.y.z', function () {
+ it('should allow messages with _source axeAPI.x.y.z', function () {
var success = false;
var event = document.createEvent('Event');
// Define that the event name is 'build'.
event.initEvent('message', true, true);
event.data = JSON.stringify({
_respondable: true,
- _source: 'axe.x.y.z',
+ _source: 'axeAPI.x.y.z',
topic: 'Death star',
message: 'Help us Obi-Wan',
uuid: mockUUID
@@ -164,7 +164,7 @@ describe('axe.utils.respondable', function () {
event.initEvent('message', true, true);
event.data = JSON.stringify({
_respondable: true,
- _source: 'axe.2.0.0',
+ _source: 'axeAPI.2.0.0',
topic: 'Death star',
message: 'Help us Obi-Wan',
uuid: mockUUID
@@ -189,7 +189,7 @@ describe('axe.utils.respondable', function () {
event.initEvent('message', true, true);
event.data = JSON.stringify({
_respondable: true,
- _source: 'axe.2.0.0',
+ _source: 'axeAPI.2.0.0',
topic: 'Death star',
message: 'Help us Obi-Wan',
uuid: mockUUID
@@ -311,7 +311,7 @@ describe('axe.utils.respondable', function () {
event.initEvent('message', true, true);
event.data = JSON.stringify({
_respondable: true,
- _source: 'axe.2.0.0',
+ _source: 'axeAPI.2.0.0',
topic: 'Death star',
error: {
name: 'ReferenceError',
@@ -340,7 +340,7 @@ describe('axe.utils.respondable', function () {
event.initEvent('message', true, true);
event.data = JSON.stringify({
_respondable: true,
- _source: 'axe.2.0.0',
+ _source: 'axeAPI.2.0.0',
topic: 'Death star',
error: {
name: 'evil',
diff --git a/test/integration/full/bypass/skip-link.html b/test/integration/full/bypass/skip-link.html
index 3e732b2883..1f1ad24332 100644
--- a/test/integration/full/bypass/skip-link.html
+++ b/test/integration/full/bypass/skip-link.html
@@ -17,7 +17,7 @@
- Skip
+ Skip
Test.
stuff
diff --git a/test/rule-matches/data-table-matches.js b/test/rule-matches/data-table-matches.js
new file mode 100644
index 0000000000..54df9cc9be
--- /dev/null
+++ b/test/rule-matches/data-table-matches.js
@@ -0,0 +1,52 @@
+describe('data-table-matches', function () {
+ 'use strict';
+
+ var fixture = document.getElementById('fixture');
+ var fixtureSetup = axe.testUtils.fixtureSetup;
+ var rule;
+
+ beforeEach(function () {
+ rule = axe._audit.rules.find(function (rule) {
+ return rule.id === 'th-has-data-cells';
+ });
+ });
+
+ afterEach(function () {
+ fixture.innerHTML = '';
+ });
+
+ it('is a function', function () {
+ assert.isFunction(rule.matches);
+ });
+
+ it('should return false if table has role="presentation"', function () {
+ fixtureSetup('' +
+ ' hi ' +
+ ' hi ' +
+ '
');
+
+ var vNode = axe.utils.querySelectorAll(axe._tree[0], 'table')[0];
+ assert.isFalse(rule.matches(vNode.actualNode, vNode));
+ });
+
+ it('should return false if table has role="none"', function () {
+ fixtureSetup('' +
+ ' hi ' +
+ ' hi ' +
+ '
');
+
+ var vNode = axe.utils.querySelectorAll(axe._tree[0], 'table')[0];
+ assert.isFalse(rule.matches(vNode.actualNode, vNode));
+ });
+
+ it('should return true if table is a data table', function () {
+ fixtureSetup('' +
+ ' Table caption ' +
+ ' Heading 1 Heading 2 ' +
+ ' Thing 1 Thing 2 ' +
+ '
');
+
+ var vNode = axe.utils.querySelectorAll(axe._tree[0], 'table')[0];
+ assert.isTrue(rule.matches(vNode.actualNode, vNode));
+ });
+});
\ No newline at end of file
diff --git a/test/rule-matches/layout-table-matches.js b/test/rule-matches/layout-table-matches.js
new file mode 100644
index 0000000000..bf403740b5
--- /dev/null
+++ b/test/rule-matches/layout-table-matches.js
@@ -0,0 +1,52 @@
+describe('layout-table-matches', function () {
+ 'use strict';
+
+ var fixture = document.getElementById('fixture');
+ var fixtureSetup = axe.testUtils.fixtureSetup;
+ var rule;
+
+ beforeEach(function () {
+ rule = axe._audit.rules.find(function (rule) {
+ return rule.id === 'layout-table';
+ });
+ });
+
+ afterEach(function () {
+ fixture.innerHTML = '';
+ });
+
+ it('returns false for element that is not focusable and has presentation role', function () {
+ fixtureSetup('');
+ var target = fixture.querySelector('table');
+
+ assert.isFalse(rule.matches(target));
+ });
+
+ it('returns false for element that is not focusable and has none role', function () {
+ fixtureSetup('');
+ var target = fixture.querySelector('table');
+
+ assert.isFalse(rule.matches(target));
+ });
+
+ it('returns trie for element that is a table without presentation/none role', function () {
+ fixtureSetup('');
+ var target = fixture.querySelector('table');
+
+ assert.isTrue(rule.matches(target));
+ });
+
+ it('returns true for element that is focusable and has none role', function () {
+ fixtureSetup('');
+ var target = fixture.querySelector('table');
+
+ assert.isTrue(rule.matches(target));
+ });
+
+ it('returns true for element that is focusable and has presentation role', function () {
+ fixtureSetup('');
+ var target = fixture.querySelector('table');
+
+ assert.isTrue(rule.matches(target));
+ });
+});
diff --git a/test/rule-matches/skip-link-matches.js b/test/rule-matches/skip-link-matches.js
new file mode 100644
index 0000000000..0de992d5af
--- /dev/null
+++ b/test/rule-matches/skip-link-matches.js
@@ -0,0 +1,46 @@
+describe('skip-link-matches', function () {
+ 'use strict';
+
+ var rule, link;
+ var fixture = document.getElementById('fixture');
+
+ beforeEach(function () {
+ rule = axe._audit.rules.find(function (rule) {
+ return rule.id === 'skip-link';
+ });
+ link = document.createElement('a')
+ });
+
+ afterEach(function () {
+ fixture.innerHTML = '';
+ });
+
+ it('is a function', function () {
+ assert.isFunction(rule.matches);
+ });
+
+ it('returns false if the href attribute does not start with #', function () {
+ link.href = 'foo#bar';
+ assert.isFalse(rule.matches(link));
+ });
+
+ it('returns false if the href attribute is `#`', function () {
+ link.href = '#';
+ assert.isFalse(rule.matches(link));
+ });
+
+ it('returns true if the href attribute starts with #', function () {
+ link.href = '#foo';
+ assert.isTrue(rule.matches(link));
+ });
+
+ it('returns false if the href attribute starts with #!', function () {
+ link.href = '#!foo';
+ assert.isFalse(rule.matches(link));
+ });
+
+ it('returns false if the href attribute starts with #/', function () {
+ link.href = '#/foo';
+ assert.isFalse(rule.matches(link));
+ });
+});
\ No newline at end of file
diff --git a/typings/axe-core/axe-core-tests.ts b/typings/axe-core/axe-core-tests.ts
index a636177e89..a7ef92577d 100644
--- a/typings/axe-core/axe-core-tests.ts
+++ b/typings/axe-core/axe-core-tests.ts
@@ -29,7 +29,7 @@ axe.run({exclude: [$fixture[0]]}, {}, (error: Error, results: axe.AxeResults) =>
console.log(error || results)
})
// additional configuration options
-axe.run(context, {iframes: false, selectors: false, elementRef: false},
+axe.run(context, {iframes: false, selectors: false, elementRef: false},
(error: Error, results: axe.AxeResults) => {
console.log(error || results.passes.length);
});
@@ -45,7 +45,7 @@ axe.run(context, tagConfig, (error: Error, results: axe.AxeResults) => {
})
var includeExcludeTagsRunOnly: axe.RunOnly = {
type: 'tags',
- value: {
+ values: {
include: ['wcag2a', 'wcag2aa'],
exclude: ['experimental']
}