diff --git a/doc/API.md b/doc/API.md
index f357317cd7..bd80acaa9f 100644
--- a/doc/API.md
+++ b/doc/API.md
@@ -179,17 +179,18 @@ User specifies the format of the JSON structure passed to the callback of `axe.r
```js
axe.configure({
- branding: {
- brand: String,
- application: String
- },
- reporter: 'option' | Function,
- checks: [Object],
- rules: [Object],
- standards: Object,
- locale: Object,
- axeVersion: String,
- disableOtherRules: Boolean
+ branding: {
+ brand: String,
+ application: String
+ },
+ reporter: 'option' | Function,
+ checks: [Object],
+ rules: [Object],
+ standards: Object,
+ locale: Object,
+ axeVersion: String,
+ disableOtherRules: Boolean,
+ noHtml: Boolean
});
```
@@ -232,6 +233,7 @@ axe.configure({
- `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`.
- `axeVersion` - Set the compatible version of a custom rule with the current axe version. Compatible versions are all patch and minor updates that are the same as, or newer than those of the `axeVersion` property.
+ - `noHtml` - Disables the HTML output of nodes from rules.
**Returns:** Nothing
diff --git a/lib/core/base/audit.js b/lib/core/base/audit.js
index d9472a8d1e..981f0b7064 100644
--- a/lib/core/base/audit.js
+++ b/lib/core/base/audit.js
@@ -29,11 +29,12 @@ function getDefaultConfiguration(audit) {
config = {};
}
- config.reporter = config.reporter || null;
- config.rules = config.rules || [];
- config.checks = config.checks || [];
- config.data = { checks: {}, rules: {}, ...config.data };
- return config;
+ config.reporter = config.reporter || null;
+ config.noHtml = config.noHtml || false;
+ config.rules = config.rules || [];
+ config.checks = config.checks || [];
+ config.data = { checks: {}, rules: {}, ...config.data };
+ return config;
}
function unpackToObject(collection, audit, method) {
@@ -148,196 +149,197 @@ const mergeFallbackMessage = (a, b) => {
* Constructor which holds configured rules and information about the document under test
*/
class Audit {
- constructor(audit) {
- // defaults
- this.lang = 'en';
- this.defaultConfig = audit;
- this.standards = standards;
- this._init();
- // A copy of the "default" locale. This will be set if the user
- // provides a new locale to `axe.configure()` and used to undo
- // changes in `axe.reset()`.
- this._defaultLocale = null;
- }
- /**
- * Build and set the previous locale. Will noop if a previous
- * locale was already set, as we want the ability to "reset"
- * to the default ("first") configuration.
- */
- _setDefaultLocale() {
- if (this._defaultLocale) {
- return;
- }
- const locale = {
- checks: {},
- rules: {},
- failureSummaries: {},
- incompleteFallbackMessage: '',
- lang: this.lang
- };
- // XXX: unable to use `for-of` here, as doing so would
- // require us to polyfill `Symbol`.
- const checkIDs = Object.keys(this.data.checks);
- for (let i = 0; i < checkIDs.length; i++) {
- const id = checkIDs[i];
- const check = this.data.checks[id];
- const { pass, fail, incomplete } = check.messages;
- locale.checks[id] = {
- pass,
- fail,
- incomplete
- };
- }
- const ruleIDs = Object.keys(this.data.rules);
- for (let i = 0; i < ruleIDs.length; i++) {
- const id = ruleIDs[i];
- const rule = this.data.rules[id];
- const { description, help } = rule;
- locale.rules[id] = { description, help };
- }
- const failureSummaries = Object.keys(this.data.failureSummaries);
- for (let i = 0; i < failureSummaries.length; i++) {
- const type = failureSummaries[i];
- const failureSummary = this.data.failureSummaries[type];
- const { failureMessage } = failureSummary;
- locale.failureSummaries[type] = { failureMessage };
- }
- locale.incompleteFallbackMessage = this.data.incompleteFallbackMessage;
- this._defaultLocale = locale;
- }
- /**
- * Reset the locale to the "default".
- */
- _resetLocale() {
- // If the default locale has not already been set, we can exit early.
- const defaultLocale = this._defaultLocale;
- if (!defaultLocale) {
- return;
- }
- // Apply the default locale
- this.applyLocale(defaultLocale);
- }
- /**
- * Apply locale for the given `checks`.
- */
- _applyCheckLocale(checks) {
- const keys = Object.keys(checks);
- for (let i = 0; i < keys.length; i++) {
- const id = keys[i];
- if (!this.data.checks[id]) {
- throw new Error(`Locale provided for unknown check: "${id}"`);
- }
- this.data.checks[id] = mergeCheckLocale(this.data.checks[id], checks[id]);
- }
- }
- /**
- * Apply locale for the given `rules`.
- */
- _applyRuleLocale(rules) {
- const keys = Object.keys(rules);
- for (let i = 0; i < keys.length; i++) {
- const id = keys[i];
- if (!this.data.rules[id]) {
- throw new Error(`Locale provided for unknown rule: "${id}"`);
- }
- this.data.rules[id] = mergeRuleLocale(this.data.rules[id], rules[id]);
- }
- }
- /**
- * Apply locale for the given failureMessage
- */
- _applyFailureSummaries(messages) {
- const keys = Object.keys(messages);
- for (let i = 0; i < keys.length; i++) {
- const key = keys[i];
- if (!this.data.failureSummaries[key]) {
- throw new Error(`Locale provided for unknown failureMessage: "${key}"`);
- }
- this.data.failureSummaries[key] = mergeFailureMessage(
- this.data.failureSummaries[key],
- messages[key]
- );
- }
- }
- /**
- * Apply the given `locale`.
- *
- * @param {axe.Locale}
- */
- applyLocale(locale) {
- this._setDefaultLocale();
- if (locale.checks) {
- this._applyCheckLocale(locale.checks);
- }
- if (locale.rules) {
- this._applyRuleLocale(locale.rules);
- }
- if (locale.failureSummaries) {
- this._applyFailureSummaries(locale.failureSummaries, 'failureSummaries');
- }
- if (locale.incompleteFallbackMessage) {
- this.data.incompleteFallbackMessage = mergeFallbackMessage(
- this.data.incompleteFallbackMessage,
- locale.incompleteFallbackMessage
- );
- }
- if (locale.lang) {
- this.lang = locale.lang;
- }
- }
- /**
- * Initializes the rules and checks
- */
- _init() {
- var audit = getDefaultConfiguration(this.defaultConfig);
- this.lang = audit.lang || 'en';
- this.reporter = audit.reporter;
- this.commands = {};
- this.rules = [];
- this.checks = {};
- this.brand = 'axe';
- this.application = 'axeAPI';
- this.tagExclude = ['experimental'];
- unpackToObject(audit.rules, this, 'addRule');
- unpackToObject(audit.checks, this, 'addCheck');
- this.data = {};
- this.data.checks = (audit.data && audit.data.checks) || {};
- this.data.rules = (audit.data && audit.data.rules) || {};
- this.data.failureSummaries =
- (audit.data && audit.data.failureSummaries) || {};
- this.data.incompleteFallbackMessage =
- (audit.data && audit.data.incompleteFallbackMessage) || '';
- this._constructHelpUrls(); // create default helpUrls
- }
- /**
- * Adds a new command to the audit
- */
- registerCommand(command) {
- this.commands[command.id] = command.callback;
- }
- /**
- * Adds a new rule to the Audit. If a rule with specified ID already exists, it will be overridden
- * @param {Object} spec Rule specification object
- */
- addRule(spec) {
- if (spec.metadata) {
- this.data.rules[spec.id] = spec.metadata;
- }
- let rule = this.getRule(spec.id);
- if (rule) {
- rule.configure(spec);
- } else {
- this.rules.push(new Rule(spec, this));
- }
- }
- /**
- * Adds a new check to the Audit. If a Check with specified ID already exists, it will be
- * reconfigured
- *
- * @param {Object} spec Check specification object
- */
- addCheck(spec) {
- /*eslint no-eval: 0 */
+ constructor(audit) {
+ // defaults
+ this.lang = 'en';
+ this.defaultConfig = audit;
+ this.standards = standards;
+ this._init();
+ // A copy of the "default" locale. This will be set if the user
+ // provides a new locale to `axe.configure()` and used to undo
+ // changes in `axe.reset()`.
+ this._defaultLocale = null;
+ }
+ /**
+ * Build and set the previous locale. Will noop if a previous
+ * locale was already set, as we want the ability to "reset"
+ * to the default ("first") configuration.
+ */
+ _setDefaultLocale() {
+ if (this._defaultLocale) {
+ return;
+ }
+ const locale = {
+ checks: {},
+ rules: {},
+ failureSummaries: {},
+ incompleteFallbackMessage: '',
+ lang: this.lang
+ };
+ // XXX: unable to use `for-of` here, as doing so would
+ // require us to polyfill `Symbol`.
+ const checkIDs = Object.keys(this.data.checks);
+ for (let i = 0; i < checkIDs.length; i++) {
+ const id = checkIDs[i];
+ const check = this.data.checks[id];
+ const { pass, fail, incomplete } = check.messages;
+ locale.checks[id] = {
+ pass,
+ fail,
+ incomplete
+ };
+ }
+ const ruleIDs = Object.keys(this.data.rules);
+ for (let i = 0; i < ruleIDs.length; i++) {
+ const id = ruleIDs[i];
+ const rule = this.data.rules[id];
+ const { description, help } = rule;
+ locale.rules[id] = { description, help };
+ }
+ const failureSummaries = Object.keys(this.data.failureSummaries);
+ for (let i = 0; i < failureSummaries.length; i++) {
+ const type = failureSummaries[i];
+ const failureSummary = this.data.failureSummaries[type];
+ const { failureMessage } = failureSummary;
+ locale.failureSummaries[type] = { failureMessage };
+ }
+ locale.incompleteFallbackMessage = this.data.incompleteFallbackMessage;
+ this._defaultLocale = locale;
+ }
+ /**
+ * Reset the locale to the "default".
+ */
+ _resetLocale() {
+ // If the default locale has not already been set, we can exit early.
+ const defaultLocale = this._defaultLocale;
+ if (!defaultLocale) {
+ return;
+ }
+ // Apply the default locale
+ this.applyLocale(defaultLocale);
+ }
+ /**
+ * Apply locale for the given `checks`.
+ */
+ _applyCheckLocale(checks) {
+ const keys = Object.keys(checks);
+ for (let i = 0; i < keys.length; i++) {
+ const id = keys[i];
+ if (!this.data.checks[id]) {
+ throw new Error(`Locale provided for unknown check: "${id}"`);
+ }
+ this.data.checks[id] = mergeCheckLocale(this.data.checks[id], checks[id]);
+ }
+ }
+ /**
+ * Apply locale for the given `rules`.
+ */
+ _applyRuleLocale(rules) {
+ const keys = Object.keys(rules);
+ for (let i = 0; i < keys.length; i++) {
+ const id = keys[i];
+ if (!this.data.rules[id]) {
+ throw new Error(`Locale provided for unknown rule: "${id}"`);
+ }
+ this.data.rules[id] = mergeRuleLocale(this.data.rules[id], rules[id]);
+ }
+ }
+ /**
+ * Apply locale for the given failureMessage
+ */
+ _applyFailureSummaries(messages) {
+ const keys = Object.keys(messages);
+ for (let i = 0; i < keys.length; i++) {
+ const key = keys[i];
+ if (!this.data.failureSummaries[key]) {
+ throw new Error(`Locale provided for unknown failureMessage: "${key}"`);
+ }
+ this.data.failureSummaries[key] = mergeFailureMessage(
+ this.data.failureSummaries[key],
+ messages[key]
+ );
+ }
+ }
+ /**
+ * Apply the given `locale`.
+ *
+ * @param {axe.Locale}
+ */
+ applyLocale(locale) {
+ this._setDefaultLocale();
+ if (locale.checks) {
+ this._applyCheckLocale(locale.checks);
+ }
+ if (locale.rules) {
+ this._applyRuleLocale(locale.rules);
+ }
+ if (locale.failureSummaries) {
+ this._applyFailureSummaries(locale.failureSummaries, 'failureSummaries');
+ }
+ if (locale.incompleteFallbackMessage) {
+ this.data.incompleteFallbackMessage = mergeFallbackMessage(
+ this.data.incompleteFallbackMessage,
+ locale.incompleteFallbackMessage
+ );
+ }
+ if (locale.lang) {
+ this.lang = locale.lang;
+ }
+ }
+ /**
+ * Initializes the rules and checks
+ */
+ _init() {
+ var audit = getDefaultConfiguration(this.defaultConfig);
+ this.lang = audit.lang || 'en';
+ this.reporter = audit.reporter;
+ this.commands = {};
+ this.rules = [];
+ this.checks = {};
+ this.brand = 'axe';
+ this.application = 'axeAPI';
+ this.tagExclude = ['experimental'];
+ this.noHtml = audit.noHtml;
+ unpackToObject(audit.rules, this, 'addRule');
+ unpackToObject(audit.checks, this, 'addCheck');
+ this.data = {};
+ this.data.checks = (audit.data && audit.data.checks) || {};
+ this.data.rules = (audit.data && audit.data.rules) || {};
+ this.data.failureSummaries =
+ (audit.data && audit.data.failureSummaries) || {};
+ this.data.incompleteFallbackMessage =
+ (audit.data && audit.data.incompleteFallbackMessage) || '';
+ this._constructHelpUrls(); // create default helpUrls
+ }
+ /**
+ * Adds a new command to the audit
+ */
+ registerCommand(command) {
+ this.commands[command.id] = command.callback;
+ }
+ /**
+ * Adds a new rule to the Audit. If a rule with specified ID already exists, it will be overridden
+ * @param {Object} spec Rule specification object
+ */
+ addRule(spec) {
+ if (spec.metadata) {
+ this.data.rules[spec.id] = spec.metadata;
+ }
+ const rule = this.getRule(spec.id);
+ if (rule) {
+ rule.configure(spec);
+ } else {
+ this.rules.push(new Rule(spec, this));
+ }
+ }
+ /**
+ * Adds a new check to the Audit. If a Check with specified ID already exists, it will be
+ * reconfigured
+ *
+ * @param {Object} spec Check specification object
+ */
+ addCheck(spec) {
+ /*eslint no-eval: 0 */
let metadata = spec.metadata;
if (typeof metadata === 'object') {
diff --git a/lib/core/public/configure.js b/lib/core/public/configure.js
index 66cacf5e15..deae724426 100644
--- a/lib/core/public/configure.js
+++ b/lib/core/public/configure.js
@@ -2,114 +2,118 @@ import { hasReporter } from './reporter';
import { configureStandards } from '../../standards';
function configure(spec) {
- var audit;
-
- audit = axe._audit;
- if (!audit) {
- throw new Error('No audit configured');
- }
-
- if (spec.axeVersion || spec.ver) {
- let specVersion = spec.axeVersion || spec.ver;
- if (!/^\d+\.\d+\.\d+(-canary)?/.test(specVersion)) {
- throw new Error(`Invalid configured version ${specVersion}`);
- }
-
- let [version, canary] = specVersion.split('-');
- let [major, minor, patch] = version.split('.').map(Number);
-
- let [axeVersion, axeCanary] = axe.version.split('-');
- let [axeMajor, axeMinor, axePatch] = axeVersion.split('.').map(Number);
-
- if (
- major !== axeMajor ||
- axeMinor < minor ||
- (axeMinor === minor && axePatch < patch) ||
- (major === axeMajor &&
- minor === axeMinor &&
- patch === axePatch &&
- canary &&
- canary !== axeCanary)
- ) {
- throw new Error(
- `Configured version ${specVersion} is not compatible with current axe version ${axe.version}`
- );
- }
- }
-
- if (
- spec.reporter &&
- (typeof spec.reporter === 'function' || hasReporter(spec.reporter))
- ) {
- audit.reporter = spec.reporter;
- }
-
- if (spec.checks) {
- if (!Array.isArray(spec.checks)) {
- throw new TypeError('Checks property must be an array');
- }
-
- spec.checks.forEach(function(check) {
- if (!check.id) {
- throw new TypeError(
- // eslint-disable-next-line max-len
- `Configured check ${JSON.stringify(
- check
- )} is invalid. Checks must be an object with at least an id property`
- );
- }
-
- audit.addCheck(check);
- });
- }
-
- const modifiedRules = [];
- if (spec.rules) {
- if (!Array.isArray(spec.rules)) {
- throw new TypeError('Rules property must be an array');
- }
-
- spec.rules.forEach(function(rule) {
- if (!rule.id) {
- throw new TypeError(
- // eslint-disable-next-line max-len
- `Configured rule ${JSON.stringify(
- rule
- )} is invalid. Rules must be an object with at least an id property`
- );
- }
-
- modifiedRules.push(rule.id);
- audit.addRule(rule);
- });
- }
-
- if (spec.disableOtherRules) {
- audit.rules.forEach(rule => {
- if (modifiedRules.includes(rule.id) === false) {
- rule.enabled = false;
- }
- });
- }
-
- if (typeof spec.branding !== 'undefined') {
- audit.setBranding(spec.branding);
- } else {
- audit._constructHelpUrls();
- }
-
- if (spec.tagExclude) {
- audit.tagExclude = spec.tagExclude;
- }
-
- // Support runtime localization
- if (spec.locale) {
- audit.applyLocale(spec.locale);
- }
-
- if (spec.standards) {
- configureStandards(spec.standards);
- }
+ var audit;
+
+ audit = axe._audit;
+ if (!audit) {
+ throw new Error('No audit configured');
+ }
+
+ if (spec.axeVersion || spec.ver) {
+ const specVersion = spec.axeVersion || spec.ver;
+ if (!/^\d+\.\d+\.\d+(-canary)?/.test(specVersion)) {
+ throw new Error(`Invalid configured version ${specVersion}`);
+ }
+
+ const [version, canary] = specVersion.split('-');
+ const [major, minor, patch] = version.split('.').map(Number);
+
+ const [axeVersion, axeCanary] = axe.version.split('-');
+ const [axeMajor, axeMinor, axePatch] = axeVersion.split('.').map(Number);
+
+ if (
+ major !== axeMajor ||
+ axeMinor < minor ||
+ (axeMinor === minor && axePatch < patch) ||
+ (major === axeMajor &&
+ minor === axeMinor &&
+ patch === axePatch &&
+ canary &&
+ canary !== axeCanary)
+ ) {
+ throw new Error(
+ `Configured version ${specVersion} is not compatible with current axe version ${axe.version}`
+ );
+ }
+ }
+
+ if (
+ spec.reporter &&
+ (typeof spec.reporter === 'function' || hasReporter(spec.reporter))
+ ) {
+ audit.reporter = spec.reporter;
+ }
+
+ if (spec.checks) {
+ if (!Array.isArray(spec.checks)) {
+ throw new TypeError('Checks property must be an array');
+ }
+
+ spec.checks.forEach(check => {
+ if (!check.id) {
+ throw new TypeError(
+ // eslint-disable-next-line max-len
+ `Configured check ${JSON.stringify(
+ check
+ )} is invalid. Checks must be an object with at least an id property`
+ );
+ }
+
+ audit.addCheck(check);
+ });
+ }
+
+ const modifiedRules = [];
+ if (spec.rules) {
+ if (!Array.isArray(spec.rules)) {
+ throw new TypeError('Rules property must be an array');
+ }
+
+ spec.rules.forEach(rule => {
+ if (!rule.id) {
+ throw new TypeError(
+ // eslint-disable-next-line max-len
+ `Configured rule ${JSON.stringify(
+ rule
+ )} is invalid. Rules must be an object with at least an id property`
+ );
+ }
+
+ modifiedRules.push(rule.id);
+ audit.addRule(rule);
+ });
+ }
+
+ if (spec.disableOtherRules) {
+ audit.rules.forEach(rule => {
+ if (modifiedRules.includes(rule.id) === false) {
+ rule.enabled = false;
+ }
+ });
+ }
+
+ if (typeof spec.branding !== 'undefined') {
+ audit.setBranding(spec.branding);
+ } else {
+ audit._constructHelpUrls();
+ }
+
+ if (spec.tagExclude) {
+ audit.tagExclude = spec.tagExclude;
+ }
+
+ // Support runtime localization
+ if (spec.locale) {
+ audit.applyLocale(spec.locale);
+ }
+
+ if (spec.standards) {
+ configureStandards(spec.standards);
+ }
+
+ if (spec.noHtml) {
+ audit.noHtml = true;
+ }
}
export default configure;
diff --git a/lib/core/utils/dq-element.js b/lib/core/utils/dq-element.js
index 118730deec..21df24ef78 100644
--- a/lib/core/utils/dq-element.js
+++ b/lib/core/utils/dq-element.js
@@ -28,26 +28,32 @@ function getSource(element) {
* @param {Object} spec Properties to use in place of the element when instantiated on Elements from other frames
*/
function DqElement(element, options, spec) {
- this._fromFrame = !!spec;
-
- this.spec = spec || {};
- if (options && options.absolutePaths) {
- this._options = { toRoot: true };
- }
-
- /**
- * The generated HTML source code of the element
- * @type {String}
- */
- this.source =
- this.spec.source !== undefined ? this.spec.source : getSource(element);
-
- /**
- * The element which this object is based off or the containing frame, used for sorting.
- * Excluded in toJSON method.
- * @type {HTMLElement}
- */
- this._element = element;
+ this._fromFrame = !!spec;
+
+ this.spec = spec || {};
+ if (options && options.absolutePaths) {
+ this._options = { toRoot: true };
+ }
+
+ /**
+ * The generated HTML source code of the element
+ * @type {String}
+ */
+ // TODO: es-modules_audit
+ if (axe._audit.noHtml) {
+ this.source = null;
+ } else if (this.spec.source !== undefined) {
+ this.source = this.spec.source;
+ } else {
+ this.source = getSource(element);
+ }
+
+ /**
+ * The element which this object is based off or the containing frame, used for sorting.
+ * Excluded in toJSON method.
+ * @type {HTMLElement}
+ */
+ this._element = element;
}
DqElement.prototype = {
diff --git a/test/core/base/audit.js b/test/core/base/audit.js
index 930532c136..c00f932ac1 100644
--- a/test/core/base/audit.js
+++ b/test/core/base/audit.js
@@ -1,1366 +1,1384 @@
/* global Promise */
describe('Audit', function() {
- 'use strict';
-
- var Audit = axe._thisWillBeDeletedDoNotUse.base.Audit;
- var Rule = axe._thisWillBeDeletedDoNotUse.base.Rule;
- var a, getFlattenedTree;
- var isNotCalled = function(err) {
- throw err || new Error('Reject should not be called');
- };
- var noop = function() {};
-
- var mockChecks = [
- {
- id: 'positive1-check1',
- evaluate: function() {
- return true;
- }
- },
- {
- id: 'positive2-check1',
- evaluate: function() {
- return true;
- }
- },
- {
- id: 'negative1-check1',
- evaluate: function() {
- return true;
- }
- },
- {
- id: 'positive3-check1',
- evaluate: function() {
- return true;
- }
- }
- ];
-
- var mockRules = [
- {
- id: 'positive1',
- selector: 'input',
- tags: ['positive'],
- any: [
- {
- id: 'positive1-check1'
- }
- ]
- },
- {
- id: 'positive2',
- selector: '#monkeys',
- tags: ['positive'],
- any: ['positive2-check1']
- },
- {
- id: 'negative1',
- selector: 'div',
- tags: ['negative'],
- none: ['negative1-check1']
- },
- {
- id: 'positive3',
- selector: 'blink',
- tags: ['positive'],
- any: ['positive3-check1']
- }
- ];
-
- var fixture = document.getElementById('fixture');
-
- var origAuditRun;
- var origAxeUtilsPreload;
-
- beforeEach(function() {
- a = new Audit();
- mockRules.forEach(function(r) {
- a.addRule(r);
- });
- mockChecks.forEach(function(c) {
- a.addCheck(c);
- });
- origAuditRun = a.run;
- });
-
- afterEach(function() {
- fixture.innerHTML = '';
- axe._tree = undefined;
- axe._selectCache = undefined;
- a.run = origAuditRun;
- });
-
- it('should be a function', function() {
- assert.isFunction(Audit);
- });
-
- describe('Audit#_constructHelpUrls', function() {
- it('should create default help URLS', function() {
- var audit = new Audit();
- audit.addRule({
- id: 'target',
- matches: 'function () {return "hello";}',
- selector: 'bob'
- });
- assert.lengthOf(audit.rules, 1);
- assert.equal(audit.data.rules.target, undefined);
- audit._constructHelpUrls();
- assert.deepEqual(audit.data.rules.target, {
- helpUrl:
- 'https://dequeuniversity.com/rules/axe/x.y/target?application=axeAPI'
- });
- });
- it('should use changed branding', function() {
- var audit = new Audit();
- audit.addRule({
- id: 'target',
- matches: 'function () {return "hello";}',
- selector: 'bob'
- });
- assert.lengthOf(audit.rules, 1);
- assert.equal(audit.data.rules.target, undefined);
- audit.brand = 'thing';
- audit._constructHelpUrls();
- assert.deepEqual(audit.data.rules.target, {
- helpUrl:
- 'https://dequeuniversity.com/rules/thing/x.y/target?application=axeAPI'
- });
- });
- it('should use changed application', function() {
- var audit = new Audit();
- audit.addRule({
- id: 'target',
- matches: 'function () {return "hello";}',
- selector: 'bob'
- });
- assert.lengthOf(audit.rules, 1);
- assert.equal(audit.data.rules.target, undefined);
- audit.application = 'thing';
- audit._constructHelpUrls();
- assert.deepEqual(audit.data.rules.target, {
- helpUrl:
- 'https://dequeuniversity.com/rules/axe/x.y/target?application=thing'
- });
- });
-
- it('does not override helpUrls of different products', function() {
- var audit = new Audit();
- audit.addRule({
- id: 'target1',
- matches: 'function () {return "hello";}',
- selector: 'bob',
- metadata: {
- helpUrl:
- 'https://dequeuniversity.com/rules/myproject/x.y/target1?application=axeAPI'
- }
- });
- audit.addRule({
- id: 'target2',
- matches: 'function () {return "hello";}',
- selector: 'bob'
- });
-
- assert.equal(
- audit.data.rules.target1.helpUrl,
- 'https://dequeuniversity.com/rules/myproject/x.y/target1?application=axeAPI'
- );
- assert.isUndefined(audit.data.rules.target2);
-
- assert.lengthOf(audit.rules, 2);
- audit.brand = 'thing';
- audit._constructHelpUrls();
-
- assert.equal(
- audit.data.rules.target1.helpUrl,
- 'https://dequeuniversity.com/rules/myproject/x.y/target1?application=axeAPI'
- );
- assert.equal(
- audit.data.rules.target2.helpUrl,
- 'https://dequeuniversity.com/rules/thing/x.y/target2?application=axeAPI'
- );
- });
- it('understands prerelease type version numbers', function() {
- var tempVersion = axe.version;
- var audit = new Audit();
- audit.addRule({
- id: 'target',
- matches: 'function () {return "hello";}',
- selector: 'bob'
- });
-
- axe.version = '3.2.1-alpha.0';
- audit._constructHelpUrls();
-
- axe.version = tempVersion;
- assert.equal(
- audit.data.rules.target.helpUrl,
- 'https://dequeuniversity.com/rules/axe/3.2/target?application=axeAPI'
- );
- });
- it('sets x.y as version for invalid versions', function() {
- var tempVersion = axe.version;
- var audit = new Audit();
- audit.addRule({
- id: 'target',
- matches: 'function () {return "hello";}',
- selector: 'bob'
- });
-
- axe.version = 'in-3.0-valid';
- audit._constructHelpUrls();
-
- axe.version = tempVersion;
- assert.equal(
- audit.data.rules.target.helpUrl,
- 'https://dequeuniversity.com/rules/axe/x.y/target?application=axeAPI'
- );
- });
- it('matches major release versions', function() {
- var tempVersion = axe.version;
- var audit = new Audit();
- audit.addRule({
- id: 'target',
- matches: 'function () {return "hello";}',
- selector: 'bob'
- });
-
- axe.version = '1.0.0';
- audit._constructHelpUrls();
-
- axe.version = tempVersion;
- assert.equal(
- audit.data.rules.target.helpUrl,
- 'https://dequeuniversity.com/rules/axe/1.0/target?application=axeAPI'
- );
- });
- it('sets the lang query if locale has been set', function() {
- var audit = new Audit();
- audit.addRule({
- id: 'target',
- matches: 'function () {return "hello";}',
- selector: 'bob'
- });
- audit.applyLocale({
- lang: 'de'
- });
- assert.lengthOf(audit.rules, 1);
- assert.equal(audit.data.rules.target, undefined);
- audit._constructHelpUrls();
- assert.deepEqual(audit.data.rules.target, {
- helpUrl:
- 'https://dequeuniversity.com/rules/axe/x.y/target?application=axeAPI&lang=de'
- });
- });
- });
-
- describe('Audit#setBranding', function() {
- it('should change the brand', function() {
- var audit = new Audit();
- assert.equal(audit.brand, 'axe');
- assert.equal(audit.application, 'axeAPI');
- audit.setBranding({
- brand: 'thing'
- });
- assert.equal(audit.brand, 'thing');
- assert.equal(audit.application, 'axeAPI');
- });
- it('should change the application', function() {
- var audit = new Audit();
- assert.equal(audit.brand, 'axe');
- assert.equal(audit.application, 'axeAPI');
- audit.setBranding({
- application: 'thing'
- });
- assert.equal(audit.brand, 'axe');
- assert.equal(audit.application, 'thing');
- });
- it('should call _constructHelpUrls', function() {
- var audit = new Audit();
- audit.addRule({
- id: 'target',
- matches: 'function () {return "hello";}',
- selector: 'bob'
- });
- assert.lengthOf(audit.rules, 1);
- assert.equal(audit.data.rules.target, undefined);
- audit.setBranding({
- application: 'thing'
- });
- assert.deepEqual(audit.data.rules.target, {
- helpUrl:
- 'https://dequeuniversity.com/rules/axe/x.y/target?application=thing'
- });
- });
- it('should call _constructHelpUrls even when nothing changed', function() {
- var audit = new Audit();
- audit.addRule({
- id: 'target',
- matches: 'function () {return "hello";}',
- selector: 'bob'
- });
- assert.lengthOf(audit.rules, 1);
- assert.equal(audit.data.rules.target, undefined);
- audit.setBranding(undefined);
- assert.deepEqual(audit.data.rules.target, {
- helpUrl:
- 'https://dequeuniversity.com/rules/axe/x.y/target?application=axeAPI'
- });
- });
- it('should not replace custom set branding', function() {
- var audit = new Audit();
- audit.addRule({
- id: 'target',
- matches: 'function () {return "hello";}',
- selector: 'bob',
- metadata: {
- helpUrl:
- 'https://dequeuniversity.com/rules/customer-x/x.y/target?application=axeAPI'
- }
- });
- audit.setBranding({
- application: 'thing',
- brand: 'other'
- });
- assert.equal(
- audit.data.rules.target.helpUrl,
- 'https://dequeuniversity.com/rules/customer-x/x.y/target?application=axeAPI'
- );
- });
- });
-
- describe('Audit#addRule', function() {
- it('should override existing rule', function() {
- var audit = new Audit();
- audit.addRule({
- id: 'target',
- matches: 'function () {return "hello";}',
- selector: 'bob'
- });
- assert.lengthOf(audit.rules, 1);
- assert.equal(audit.rules[0].selector, 'bob');
- assert.equal(audit.rules[0].matches(), 'hello');
-
- audit.addRule({
- id: 'target',
- selector: 'fred'
- });
-
- assert.lengthOf(audit.rules, 1);
- assert.equal(audit.rules[0].selector, 'fred');
- assert.equal(audit.rules[0].matches(), 'hello');
- });
- it('should otherwise push new rule', function() {
- var audit = new Audit();
- audit.addRule({
- id: 'target',
- selector: 'bob'
- });
- assert.lengthOf(audit.rules, 1);
- assert.equal(audit.rules[0].id, 'target');
- assert.equal(audit.rules[0].selector, 'bob');
-
- audit.addRule({
- id: 'target2',
- selector: 'fred'
- });
-
- assert.lengthOf(audit.rules, 2);
- assert.equal(audit.rules[1].id, 'target2');
- assert.equal(audit.rules[1].selector, 'fred');
- });
- });
-
- describe('Audit#resetRulesAndChecks', function() {
- it('should override newly created check', function() {
- var audit = new Audit();
- assert.equal(audit.checks.target, undefined);
- audit.addCheck({
- id: 'target',
- options: { value: 'jane' }
- });
- assert.ok(audit.checks.target);
- assert.deepEqual(audit.checks.target.options, { value: 'jane' });
- audit.resetRulesAndChecks();
- assert.equal(audit.checks.target, undefined);
- });
- it('should reset locale', function() {
- var audit = new Audit();
- assert.equal(audit.lang, 'en');
- audit.applyLocale({
- lang: 'de'
- });
- assert.equal(audit.lang, 'de');
- audit.resetRulesAndChecks();
- assert.equal(audit.lang, 'en');
- });
- it('should reset brand', function() {
- var audit = new Audit();
- assert.equal(audit.brand, 'axe');
- audit.setBranding({
- brand: 'test'
- });
- assert.equal(audit.brand, 'test');
- audit.resetRulesAndChecks();
- assert.equal(audit.brand, 'axe');
- });
- it('should reset brand application', function() {
- var audit = new Audit();
- assert.equal(audit.application, 'axeAPI');
- audit.setBranding({
- application: 'test'
- });
- assert.equal(audit.application, 'test');
- audit.resetRulesAndChecks();
- assert.equal(audit.application, 'axeAPI');
- });
- it('should reset brand tagExlcude', function() {
- axe._load({});
- assert.deepEqual(axe._audit.tagExclude, ['experimental']);
- axe.configure({
- tagExclude: ['ninjas']
- });
- axe._audit.resetRulesAndChecks();
- assert.deepEqual(axe._audit.tagExclude, ['experimental']);
- });
- });
-
- describe('Audit#addCheck', function() {
- it('should create a new check', function() {
- var audit = new Audit();
- assert.equal(audit.checks.target, undefined);
- audit.addCheck({
- id: 'target',
- options: { value: 'jane' }
- });
- assert.ok(audit.checks.target);
- assert.deepEqual(audit.checks.target.options, { value: 'jane' });
- });
- it('should configure the metadata, if passed', function() {
- var audit = new Audit();
- assert.equal(audit.checks.target, undefined);
- audit.addCheck({
- id: 'target',
- metadata: { guy: 'bob' }
- });
- assert.ok(audit.checks.target);
- assert.equal(audit.data.checks.target.guy, 'bob');
- });
- it('should reconfigure existing check', function() {
- var audit = new Audit();
- var myTest = function() {};
- audit.addCheck({
- id: 'target',
- evaluate: myTest,
- options: { value: 'jane' }
- });
-
- assert.deepEqual(audit.checks.target.options, { value: 'jane' });
-
- audit.addCheck({
- id: 'target',
- options: { value: 'fred' }
- });
-
- assert.equal(audit.checks.target.evaluate, myTest);
- assert.deepEqual(audit.checks.target.options, { value: 'fred' });
- });
- it('should not turn messages into a function', function() {
- var audit = new Audit();
- var spec = {
- id: 'target',
- evaluate: 'function () { return "blah";}',
- metadata: {
- messages: {
- fail: 'it failed'
- }
- }
- };
- audit.addCheck(spec);
-
- assert.equal(typeof audit.checks.target.evaluate, 'function');
- assert.equal(typeof audit.data.checks.target.messages.fail, 'string');
- assert.equal(audit.data.checks.target.messages.fail, 'it failed');
- });
-
- it('should turn function strings into a function', function() {
- var audit = new Audit();
- var spec = {
- id: 'target',
- evaluate: 'function () { return "blah";}',
- metadata: {
- messages: {
- fail: 'function () {return "it failed";}'
- }
- }
- };
- audit.addCheck(spec);
-
- assert.equal(typeof audit.checks.target.evaluate, 'function');
- assert.equal(typeof audit.data.checks.target.messages.fail, 'function');
- assert.equal(audit.data.checks.target.messages.fail(), 'it failed');
- });
- });
-
- describe('Audit#run', function() {
- it('should run all the rules', function(done) {
- fixture.innerHTML =
- ' ' +
- '
bananas
' +
- ' ' +
- 'FAIL ME ';
-
- a.run(
- { include: [axe.utils.getFlattenedTree(fixture)[0]] },
- {},
- function(results) {
- var expected = [
- {
- id: 'positive1',
- result: 'inapplicable',
- pageLevel: false,
- impact: null,
- nodes: '...other tests cover this...'
- },
- {
- id: 'positive2',
- result: 'inapplicable',
- pageLevel: false,
- impact: null,
- nodes: '...other tests cover this...'
- },
- {
- id: 'negative1',
- result: 'inapplicable',
- pageLevel: false,
- impact: null,
- nodes: '...other tests cover this...'
- },
- {
- id: 'positive3',
- result: 'inapplicable',
- pageLevel: false,
- impact: null,
- nodes: '...other tests cover this...'
- }
- ];
-
- var out = results[0].nodes[0].node.source;
- results.forEach(function(res) {
- // attribute order is a pain in the lower back in IE, so we're not
- // comparing nodes. Check.run and Rule.run do this.
- res.nodes = '...other tests cover this...';
- });
-
- assert.deepEqual(JSON.parse(JSON.stringify(results)), expected);
- assert.match(
- out,
- /^ /
- );
- done();
- },
- isNotCalled
- );
- });
-
- it('should not run rules disabled by the options', function(done) {
- a.run(
- { include: [document] },
- {
- rules: {
- positive3: {
- enabled: false
- }
- }
- },
- function(results) {
- assert.equal(results.length, 3);
- done();
- },
- isNotCalled
- );
- });
-
- it('should ensure audit.run recieves preload options', function(done) {
- fixture.innerHTML = ' ';
-
- var audit = new Audit();
- audit.addRule({
- id: 'preload1',
- selector: '*'
- });
- audit.run = function(context, options, resolve, reject) {
- var randomRule = this.rules[0];
- randomRule.run(
- context,
- options,
- function(ruleResult) {
- ruleResult.OPTIONS_PASSED = options;
- resolve([ruleResult]);
- },
- reject
- );
- };
-
- var preloadOptions = {
- preload: {
- assets: ['cssom']
- }
- };
- audit.run(
- { include: [axe.utils.getFlattenedTree(fixture)[0]] },
- {
- preload: preloadOptions
- },
- function(res) {
- assert.isDefined(res);
-
- assert.lengthOf(res, 1);
- assert.property(res[0], 'OPTIONS_PASSED');
-
- var optionsPassed = res[0].OPTIONS_PASSED;
- assert.property(optionsPassed, 'preload');
- assert.deepEqual(optionsPassed.preload, preloadOptions);
-
- // ensure cache is cleared
- assert.isTrue(typeof axe._selectCache === 'undefined');
-
- done();
- },
- noop
- );
- });
-
- it.skip('should run rules (that do not need preload) and preload assets simultaneously', function(done) {
- /**
- * Note:
- * overriding and resolving both check and preload with a delay,
- * but the invoked timestamp should ensure that they were invoked almost immediately
- */
-
- fixture.innerHTML = '
';
-
- var runStartTime = new Date();
- var preloadInvokedTime = new Date();
- var noPreloadCheckedInvokedTime = new Date();
- var noPreloadRuleCheckEvaluateInvoked = false;
- var preloadOverrideInvoked = false;
-
- // override preload method
- axe.utils.preload = function(options) {
- preloadInvokedTime = new Date();
- preloadOverrideInvoked = true;
-
- return new Promise(function(res, rej) {
- setTimeout(function() {
- res(true);
- }, 2000);
- });
- };
-
- var audit = new Audit();
- // add a rule and check that does not need preload
- audit.addRule({
- id: 'no-preload',
- selector: 'div#div1',
- any: ['no-preload-check'],
- preload: false
- });
- audit.addCheck({
- id: 'no-preload-check',
- evaluate: function(node, options, vNode, context) {
- noPreloadCheckedInvokedTime = new Date();
- noPreloadRuleCheckEvaluateInvoked = true;
- var ready = this.async();
- setTimeout(function() {
- ready(true);
- }, 1000);
- }
- });
-
- // add a rule which needs preload
- audit.addRule({
- id: 'yes-preload',
- selector: 'div#div2',
- preload: true
- });
-
- var preloadOptions = {
- preload: {
- assets: ['cssom']
- }
- };
-
- var allowedDiff = 50;
-
- audit.run(
- { include: [axe.utils.getFlattenedTree(fixture)[0]] },
- {
- preload: preloadOptions
- },
- function(results) {
- assert.isDefined(results);
- // assert that check was invoked for rule(s)
- assert.isTrue(noPreloadRuleCheckEvaluateInvoked);
- // assert preload was invoked
- assert.isTrue(preloadOverrideInvoked);
- // assert that time diff(s)
- // assert that run check invoked immediately
- // choosing 5ms as an arbitary number
- assert.isBelow(
- noPreloadCheckedInvokedTime - runStartTime,
- allowedDiff
- );
- // assert that preload invoked immediately
- assert.isBelow(preloadInvokedTime - runStartTime, allowedDiff);
- // ensure cache is clear
- assert.isTrue(typeof axe._selectCache === 'undefined');
- // done
- done();
- },
- noop
- );
- });
-
- it.skip('should pass assets from preload to rule check that needs assets as context', function(done) {
- fixture.innerHTML = '
';
-
- var yesPreloadRuleCheckEvaluateInvoked = false;
- var preloadOverrideInvoked = false;
-
- var preloadData = {
- data: 'you got it!'
- };
- // override preload method
- axe.utils.preload = function(options) {
- preloadOverrideInvoked = true;
- return Promise.resolve({
- cssom: preloadData
- });
- };
-
- var audit = new Audit();
- // add a rule and check that does not need preload
- audit.addRule({
- id: 'no-preload',
- selector: 'div#div1',
- preload: false
- });
- // add a rule which needs preload
- audit.addRule({
- id: 'yes-preload',
- selector: 'div#div2',
- preload: true,
- any: ['yes-preload-check']
- });
- audit.addCheck({
- id: 'yes-preload-check',
- evaluate: function(node, options, vNode, context) {
- yesPreloadRuleCheckEvaluateInvoked = true;
- this.data(context);
- return true;
- }
- });
-
- var preloadOptions = {
- preload: {
- assets: ['cssom']
- }
- };
- audit.run(
- { include: [axe.utils.getFlattenedTree(fixture)[0]] },
- {
- preload: preloadOptions
- },
- function(results) {
- assert.isDefined(results);
- // assert that check was invoked for rule(s)
- assert.isTrue(yesPreloadRuleCheckEvaluateInvoked);
- // assert preload was invoked
- assert.isTrue(preloadOverrideInvoked);
-
- // assert preload data that was passed to check
- var ruleResult = results.filter(function(r) {
- return (r.id = 'yes-preload' && r.nodes.length > 0);
- })[0];
- var checkResult = ruleResult.nodes[0].any[0];
- assert.isDefined(checkResult.data);
- assert.property(checkResult.data, 'cssom');
- assert.deepEqual(checkResult.data.cssom, preloadData);
- // ensure cache is clear
- assert.isTrue(typeof axe._selectCache === 'undefined');
- // done
- done();
- },
- noop
- );
- });
-
- it.skip('should continue to run rules and return result when preload is rejected', function(done) {
- fixture.innerHTML = '
';
-
- var preloadOverrideInvoked = false;
- var preloadNeededCheckInvoked = false;
- var rejectionMsg =
- 'Boom! Things went terribly wrong! (But this was intended in this test)';
-
- // override preload method
- axe.utils.preload = function(options) {
- preloadOverrideInvoked = true;
- return Promise.reject(rejectionMsg);
- };
-
- var audit = new Audit();
- // add a rule and check that does not need preload
- audit.addRule({
- id: 'no-preload',
- selector: 'div#div1',
- preload: false
- });
- // add a rule which needs preload
- audit.addRule({
- id: 'yes-preload',
- selector: 'div#div2',
- preload: true,
- any: ['yes-preload-check']
- });
- audit.addCheck({
- id: 'yes-preload-check',
- evaluate: function(node, options, vNode, context) {
- preloadNeededCheckInvoked = true;
- this.data(context);
- return true;
- }
- });
-
- var preloadOptions = {
- preload: {
- assets: ['cssom']
- }
- };
- audit.run(
- { include: [axe.utils.getFlattenedTree(fixture)[0]] },
- {
- preload: preloadOptions
- },
- function(results) {
- assert.isDefined(results);
- // assert preload was invoked
- assert.isTrue(preloadOverrideInvoked);
-
- // assert that both rules ran, although preload failed
- assert.lengthOf(results, 2);
-
- // assert that because preload failed
- // cssom was not populated on context of repective check
- assert.isTrue(preloadNeededCheckInvoked);
- var ruleResult = results.filter(function(r) {
- return (r.id = 'yes-preload' && r.nodes.length > 0);
- })[0];
- var checkResult = ruleResult.nodes[0].any[0];
- assert.isDefined(checkResult.data);
- assert.notProperty(checkResult.data, 'cssom');
- // done
- done();
- },
- noop
- );
- });
-
- it('should continue to run rules and return result when axios time(s)out and rejects preload', function(done) {
- fixture.innerHTML = '
';
-
- // there is no stubbing here,
- // the actual axios call is invoked, and timedout immediately as timeout is set to 0.1
-
- var preloadNeededCheckInvoked = false;
- var audit = new Audit();
- // add a rule and check that does not need preload
- audit.addRule({
- id: 'no-preload',
- selector: 'div#div1',
- preload: false
- });
- // add a rule which needs preload
- audit.addRule({
- id: 'yes-preload',
- selector: 'div#div2',
- preload: true,
- any: ['yes-preload-check']
- });
- audit.addCheck({
- id: 'yes-preload-check',
- evaluate: function(node, options, vNode, context) {
- preloadNeededCheckInvoked = true;
- this.data(context);
- return true;
- }
- });
-
- var preloadOptions = {
- preload: {
- assets: ['cssom'],
- timeout: 0.1
- }
- };
- audit.run(
- { include: [axe.utils.getFlattenedTree(fixture)[0]] },
- {
- preload: preloadOptions
- },
- function(results) {
- assert.isDefined(results);
- // assert that both rules ran, although preload failed
- assert.lengthOf(results, 2);
-
- // assert that because preload failed
- // cssom was not populated on context of repective check
- assert.isTrue(preloadNeededCheckInvoked);
- var ruleResult = results.filter(function(r) {
- return (r.id = 'yes-preload' && r.nodes.length > 0);
- })[0];
- var checkResult = ruleResult.nodes[0].any[0];
- assert.isDefined(checkResult.data);
- assert.notProperty(checkResult.data, 'cssom');
- // done
- done();
- },
- noop
- );
- });
-
- it.skip('should assign an empty array to axe._selectCache', function(done) {
- var saved = axe.utils.ruleShouldRun;
- axe.utils.ruleShouldRun = function() {
- assert.equal(axe._selectCache.length, 0);
- return false;
- };
- a.run(
- { include: [document] },
- {},
- function() {
- axe.utils.ruleShouldRun = saved;
- done();
- },
- isNotCalled
- );
- });
-
- it('should clear axe._selectCache', function(done) {
- a.run(
- { include: [document] },
- {
- rules: {}
- },
- function() {
- assert.isTrue(typeof axe._selectCache === 'undefined');
- done();
- },
- isNotCalled
- );
- });
-
- it('should not run rules disabled by the configuration', function(done) {
- var a = new Audit();
- var success = true;
- a.rules.push(
- new Rule({
- id: 'positive1',
- selector: '*',
- enabled: false,
- any: [
- {
- id: 'positive1-check1',
- evaluate: function() {
- success = false;
- }
- }
- ]
- })
- );
- a.run(
- { include: [document] },
- {},
- function() {
- assert.ok(success);
- done();
- },
- isNotCalled
- );
- });
-
- it("should call the rule's run function", function(done) {
- var targetRule = mockRules[mockRules.length - 1],
- rule = axe.utils.findBy(a.rules, 'id', targetRule.id),
- called = false,
- orig;
-
- fixture.innerHTML = 'link ';
- orig = rule.run;
- rule.run = function(node, options, callback) {
- called = true;
- callback({});
- };
- a.run(
- { include: [document] },
- {},
- function() {
- assert.isTrue(called);
- rule.run = orig;
- done();
- },
- isNotCalled
- );
- });
-
- it('should pass the option to the run function', function(done) {
- var targetRule = mockRules[mockRules.length - 1],
- rule = axe.utils.findBy(a.rules, 'id', targetRule.id),
- passed = false,
- orig,
- options;
-
- fixture.innerHTML = 'link ';
- orig = rule.run;
- rule.run = function(node, o, callback) {
- assert.deepEqual(o, options);
- passed = true;
- callback({});
- };
- options = { rules: {} };
- (options.rules[targetRule.id] = {}).data = 'monkeys';
- a.run(
- { include: [document] },
- options,
- function() {
- assert.ok(passed);
- rule.run = orig;
- done();
- },
- isNotCalled
- );
- });
-
- it('should skip pageLevel rules if context is not set to entire page', function() {
- var audit = new Audit();
-
- audit.rules.push(
- new Rule({
- pageLevel: true,
- enabled: true,
- evaluate: function() {
- assert.ok(false, 'Should not run');
- }
- })
- );
-
- audit.run(
- { include: [document.body], page: false },
- {},
- function(results) {
- assert.deepEqual(results, []);
- },
- isNotCalled
- );
- });
-
- it('catches errors and passes them as a cantTell result', function(done) {
- var err = new Error('Launch the super sheep!');
- a.addRule({
- id: 'throw1',
- selector: '*',
- any: [
- {
- id: 'throw1-check1'
- }
- ]
- });
- a.addCheck({
- id: 'throw1-check1',
- evaluate: function() {
- throw err;
- }
- });
- axe._tree = axe.utils.getFlattenedTree(fixture);
- axe._selectorData = axe.utils.getSelectorData(axe._tree);
- a.run(
- { include: [axe._tree[0]] },
- {
- runOnly: {
- type: 'rule',
- values: ['throw1']
- }
- },
- function(results) {
- assert.lengthOf(results, 1);
- assert.equal(results[0].result, 'cantTell');
- assert.equal(results[0].message, err.message);
- assert.equal(results[0].stack, err.stack);
- assert.equal(results[0].error, err);
- done();
- },
- isNotCalled
- );
- });
-
- it('should not halt if errors occur', function(done) {
- a.addRule({
- id: 'throw1',
- selector: '*',
- any: [
- {
- id: 'throw1-check1'
- }
- ]
- });
- a.addCheck({
- id: 'throw1-check1',
- evaluate: function() {
- throw new Error('Launch the super sheep!');
- }
- });
- a.run(
- { include: [axe.utils.getFlattenedTree(fixture)[0]] },
- {
- runOnly: {
- type: 'rule',
- values: ['throw1', 'positive1']
- }
- },
- function() {
- done();
- },
- isNotCalled
- );
- });
-
- it('should run audit.normalizeOptions to ensure valid input', function() {
- fixture.innerHTML =
- ' ' +
- 'bananas
' +
- ' ' +
- 'FAIL ME ';
- var checked = 'options not validated';
-
- a.normalizeOptions = function() {
- checked = 'options validated';
- };
-
- a.run({ include: [fixture] }, {}, noop, isNotCalled);
- assert.equal(checked, 'options validated');
- });
-
- it('should halt if an error occurs when debug is set', function(done) {
- a.addRule({
- id: 'throw1',
- selector: '*',
- any: [
- {
- id: 'throw1-check1'
- }
- ]
- });
- a.addCheck({
- id: 'throw1-check1',
- evaluate: function() {
- throw new Error('Launch the super sheep!');
- }
- });
- a.run(
- { include: [axe.utils.getFlattenedTree(fixture)[0]] },
- {
- debug: true,
- runOnly: {
- type: 'rule',
- values: ['throw1']
- }
- },
- noop,
- function(err) {
- assert.equal(err.message, 'Launch the super sheep!');
- done();
- }
- );
- });
- });
-
- describe('Audit#after', function() {
- it('should run Rule#after on any rule whose result is passed in', function() {
- /*eslint no-unused-vars:0*/
- var audit = new Audit();
- var success = false;
- var options = [{ id: 'hehe', enabled: true, monkeys: 'bananas' }];
- var results = [
- {
- id: 'hehe',
- monkeys: 'bananas'
- }
- ];
- audit.rules.push(
- new Rule({
- id: 'hehe',
- pageLevel: false,
- enabled: false
- })
- );
-
- audit.rules[0].after = function(res, opts) {
- assert.equal(res, results[0]);
- assert.deepEqual(opts, options);
- success = true;
- };
-
- audit.after(results, options);
- });
- });
-
- describe('Audit#normalizeOptions', function() {
- it('returns the options object when it is valid', function() {
- var opt = {
- runOnly: {
- type: 'rule',
- values: ['positive1', 'positive2']
- },
- rules: {
- negative1: { enabled: false }
- }
- };
- assert(a.normalizeOptions(opt), opt);
- });
-
- it('allows `value` as alternative to `values`', function() {
- var opt = {
- runOnly: {
- type: 'rule',
- value: ['positive1', 'positive2']
- }
- };
- var out = a.normalizeOptions(opt);
- assert.deepEqual(out.runOnly.values, ['positive1', 'positive2']);
- assert.isUndefined(out.runOnly.value);
- });
-
- it('allows type: rules as an alternative to type: rule', function() {
- var opt = {
- runOnly: {
- type: 'rules',
- values: ['positive1', 'positive2']
- }
- };
- assert(a.normalizeOptions(opt).runOnly.type, 'rule');
- });
-
- it('allows type: tags as an alternative to type: tag', function() {
- var opt = {
- runOnly: {
- type: 'tags',
- values: ['positive']
- }
- };
- assert(a.normalizeOptions(opt).runOnly.type, 'tag');
- });
-
- it('allows type: undefined as an alternative to type: tag', function() {
- var opt = {
- runOnly: {
- values: ['positive']
- }
- };
- assert(a.normalizeOptions(opt).runOnly.type, 'tag');
- });
-
- it('allows runOnly as an array as an alternative to type: tag', function() {
- var opt = { runOnly: ['positive', 'negative'] };
- var out = a.normalizeOptions(opt);
- assert(out.runOnly.type, 'tag');
- assert.deepEqual(out.runOnly.values, ['positive', 'negative']);
- });
-
- it('allows runOnly as an array as an alternative to type: rule', function() {
- var opt = { runOnly: ['positive1', 'negative1'] };
- var out = a.normalizeOptions(opt);
- assert(out.runOnly.type, 'rule');
- assert.deepEqual(out.runOnly.values, ['positive1', 'negative1']);
- });
-
- it('throws an error if runOnly contains both rules and tags', function() {
- assert.throws(function() {
- a.normalizeOptions({
- runOnly: ['positive', 'negative1']
- });
- });
- });
-
- it('defaults runOnly to type: tag', function() {
- var opt = { runOnly: ['fakeTag'] };
- var out = a.normalizeOptions(opt);
- assert(out.runOnly.type, 'tag');
- assert.deepEqual(out.runOnly.values, ['fakeTag']);
- });
-
- it('throws an error runOnly.values not an array', function() {
- assert.throws(function() {
- a.normalizeOptions({
- runOnly: {
- type: 'rule',
- values: { badProp: 'badValue' }
- }
- });
- });
- });
-
- it('throws an error runOnly.values an empty', function() {
- assert.throws(function() {
- a.normalizeOptions({
- runOnly: {
- type: 'rule',
- values: []
- }
- });
- });
- });
-
- it('throws an error runOnly.type is unknown', function() {
- assert.throws(function() {
- a.normalizeOptions({
- runOnly: {
- type: 'something-else',
- values: ['wcag2aa']
- }
- });
- });
- });
-
- it('throws an error when option.runOnly has an unknown rule', function() {
- assert.throws(function() {
- a.normalizeOptions({
- runOnly: {
- type: 'rule',
- values: ['frakeRule']
- }
- });
- });
- });
-
- it("doesn't throw an error when option.runOnly has an unknown tag", function() {
- assert.doesNotThrow(function() {
- a.normalizeOptions({
- runOnly: {
- type: 'tags',
- values: ['fakeTag']
- }
- });
- });
- });
-
- it('throws an error when option.rules has an unknown rule', function() {
- assert.throws(function() {
- a.normalizeOptions({
- rules: {
- fakeRule: { enabled: false }
- }
- });
- });
- });
- });
+ 'use strict';
+
+ var Audit = axe._thisWillBeDeletedDoNotUse.base.Audit;
+ var Rule = axe._thisWillBeDeletedDoNotUse.base.Rule;
+ var a, getFlattenedTree;
+ var isNotCalled = function(err) {
+ throw err || new Error('Reject should not be called');
+ };
+ var noop = function() {};
+
+ var mockChecks = [
+ {
+ id: 'positive1-check1',
+ evaluate: function() {
+ return true;
+ }
+ },
+ {
+ id: 'positive2-check1',
+ evaluate: function() {
+ return true;
+ }
+ },
+ {
+ id: 'negative1-check1',
+ evaluate: function() {
+ return true;
+ }
+ },
+ {
+ id: 'positive3-check1',
+ evaluate: function() {
+ return true;
+ }
+ }
+ ];
+
+ var mockRules = [
+ {
+ id: 'positive1',
+ selector: 'input',
+ tags: ['positive'],
+ any: [
+ {
+ id: 'positive1-check1'
+ }
+ ]
+ },
+ {
+ id: 'positive2',
+ selector: '#monkeys',
+ tags: ['positive'],
+ any: ['positive2-check1']
+ },
+ {
+ id: 'negative1',
+ selector: 'div',
+ tags: ['negative'],
+ none: ['negative1-check1']
+ },
+ {
+ id: 'positive3',
+ selector: 'blink',
+ tags: ['positive'],
+ any: ['positive3-check1']
+ }
+ ];
+
+ var fixture = document.getElementById('fixture');
+
+ var origAuditRun;
+ var origAxeUtilsPreload;
+
+ beforeEach(function() {
+ a = new Audit();
+ mockRules.forEach(function(r) {
+ a.addRule(r);
+ });
+ mockChecks.forEach(function(c) {
+ a.addCheck(c);
+ });
+ origAuditRun = a.run;
+ });
+
+ afterEach(function() {
+ fixture.innerHTML = '';
+ axe._tree = undefined;
+ axe._selectCache = undefined;
+ a.run = origAuditRun;
+ });
+
+ it('should be a function', function() {
+ assert.isFunction(Audit);
+ });
+
+ describe('defaults', function() {
+ it('should set noHtml', function() {
+ var audit = new Audit();
+ assert.isFalse(audit.noHtml);
+ });
+ });
+
+ describe('Audit#_constructHelpUrls', function() {
+ it('should create default help URLS', function() {
+ var audit = new Audit();
+ audit.addRule({
+ id: 'target',
+ matches: 'function () {return "hello";}',
+ selector: 'bob'
+ });
+ assert.lengthOf(audit.rules, 1);
+ assert.equal(audit.data.rules.target, undefined);
+ audit._constructHelpUrls();
+ assert.deepEqual(audit.data.rules.target, {
+ helpUrl:
+ 'https://dequeuniversity.com/rules/axe/x.y/target?application=axeAPI'
+ });
+ });
+ it('should use changed branding', function() {
+ var audit = new Audit();
+ audit.addRule({
+ id: 'target',
+ matches: 'function () {return "hello";}',
+ selector: 'bob'
+ });
+ assert.lengthOf(audit.rules, 1);
+ assert.equal(audit.data.rules.target, undefined);
+ audit.brand = 'thing';
+ audit._constructHelpUrls();
+ assert.deepEqual(audit.data.rules.target, {
+ helpUrl:
+ 'https://dequeuniversity.com/rules/thing/x.y/target?application=axeAPI'
+ });
+ });
+ it('should use changed application', function() {
+ var audit = new Audit();
+ audit.addRule({
+ id: 'target',
+ matches: 'function () {return "hello";}',
+ selector: 'bob'
+ });
+ assert.lengthOf(audit.rules, 1);
+ assert.equal(audit.data.rules.target, undefined);
+ audit.application = 'thing';
+ audit._constructHelpUrls();
+ assert.deepEqual(audit.data.rules.target, {
+ helpUrl:
+ 'https://dequeuniversity.com/rules/axe/x.y/target?application=thing'
+ });
+ });
+
+ it('does not override helpUrls of different products', function() {
+ var audit = new Audit();
+ audit.addRule({
+ id: 'target1',
+ matches: 'function () {return "hello";}',
+ selector: 'bob',
+ metadata: {
+ helpUrl:
+ 'https://dequeuniversity.com/rules/myproject/x.y/target1?application=axeAPI'
+ }
+ });
+ audit.addRule({
+ id: 'target2',
+ matches: 'function () {return "hello";}',
+ selector: 'bob'
+ });
+
+ assert.equal(
+ audit.data.rules.target1.helpUrl,
+ 'https://dequeuniversity.com/rules/myproject/x.y/target1?application=axeAPI'
+ );
+ assert.isUndefined(audit.data.rules.target2);
+
+ assert.lengthOf(audit.rules, 2);
+ audit.brand = 'thing';
+ audit._constructHelpUrls();
+
+ assert.equal(
+ audit.data.rules.target1.helpUrl,
+ 'https://dequeuniversity.com/rules/myproject/x.y/target1?application=axeAPI'
+ );
+ assert.equal(
+ audit.data.rules.target2.helpUrl,
+ 'https://dequeuniversity.com/rules/thing/x.y/target2?application=axeAPI'
+ );
+ });
+ it('understands prerelease type version numbers', function() {
+ var tempVersion = axe.version;
+ var audit = new Audit();
+ audit.addRule({
+ id: 'target',
+ matches: 'function () {return "hello";}',
+ selector: 'bob'
+ });
+
+ axe.version = '3.2.1-alpha.0';
+ audit._constructHelpUrls();
+
+ axe.version = tempVersion;
+ assert.equal(
+ audit.data.rules.target.helpUrl,
+ 'https://dequeuniversity.com/rules/axe/3.2/target?application=axeAPI'
+ );
+ });
+ it('sets x.y as version for invalid versions', function() {
+ var tempVersion = axe.version;
+ var audit = new Audit();
+ audit.addRule({
+ id: 'target',
+ matches: 'function () {return "hello";}',
+ selector: 'bob'
+ });
+
+ axe.version = 'in-3.0-valid';
+ audit._constructHelpUrls();
+
+ axe.version = tempVersion;
+ assert.equal(
+ audit.data.rules.target.helpUrl,
+ 'https://dequeuniversity.com/rules/axe/x.y/target?application=axeAPI'
+ );
+ });
+ it('matches major release versions', function() {
+ var tempVersion = axe.version;
+ var audit = new Audit();
+ audit.addRule({
+ id: 'target',
+ matches: 'function () {return "hello";}',
+ selector: 'bob'
+ });
+
+ axe.version = '1.0.0';
+ audit._constructHelpUrls();
+
+ axe.version = tempVersion;
+ assert.equal(
+ audit.data.rules.target.helpUrl,
+ 'https://dequeuniversity.com/rules/axe/1.0/target?application=axeAPI'
+ );
+ });
+ it('sets the lang query if locale has been set', function() {
+ var audit = new Audit();
+ audit.addRule({
+ id: 'target',
+ matches: 'function () {return "hello";}',
+ selector: 'bob'
+ });
+ audit.applyLocale({
+ lang: 'de'
+ });
+ assert.lengthOf(audit.rules, 1);
+ assert.equal(audit.data.rules.target, undefined);
+ audit._constructHelpUrls();
+ assert.deepEqual(audit.data.rules.target, {
+ helpUrl:
+ 'https://dequeuniversity.com/rules/axe/x.y/target?application=axeAPI&lang=de'
+ });
+ });
+ });
+
+ describe('Audit#setBranding', function() {
+ it('should change the brand', function() {
+ var audit = new Audit();
+ assert.equal(audit.brand, 'axe');
+ assert.equal(audit.application, 'axeAPI');
+ audit.setBranding({
+ brand: 'thing'
+ });
+ assert.equal(audit.brand, 'thing');
+ assert.equal(audit.application, 'axeAPI');
+ });
+ it('should change the application', function() {
+ var audit = new Audit();
+ assert.equal(audit.brand, 'axe');
+ assert.equal(audit.application, 'axeAPI');
+ audit.setBranding({
+ application: 'thing'
+ });
+ assert.equal(audit.brand, 'axe');
+ assert.equal(audit.application, 'thing');
+ });
+ it('should call _constructHelpUrls', function() {
+ var audit = new Audit();
+ audit.addRule({
+ id: 'target',
+ matches: 'function () {return "hello";}',
+ selector: 'bob'
+ });
+ assert.lengthOf(audit.rules, 1);
+ assert.equal(audit.data.rules.target, undefined);
+ audit.setBranding({
+ application: 'thing'
+ });
+ assert.deepEqual(audit.data.rules.target, {
+ helpUrl:
+ 'https://dequeuniversity.com/rules/axe/x.y/target?application=thing'
+ });
+ });
+ it('should call _constructHelpUrls even when nothing changed', function() {
+ var audit = new Audit();
+ audit.addRule({
+ id: 'target',
+ matches: 'function () {return "hello";}',
+ selector: 'bob'
+ });
+ assert.lengthOf(audit.rules, 1);
+ assert.equal(audit.data.rules.target, undefined);
+ audit.setBranding(undefined);
+ assert.deepEqual(audit.data.rules.target, {
+ helpUrl:
+ 'https://dequeuniversity.com/rules/axe/x.y/target?application=axeAPI'
+ });
+ });
+ it('should not replace custom set branding', function() {
+ var audit = new Audit();
+ audit.addRule({
+ id: 'target',
+ matches: 'function () {return "hello";}',
+ selector: 'bob',
+ metadata: {
+ helpUrl:
+ 'https://dequeuniversity.com/rules/customer-x/x.y/target?application=axeAPI'
+ }
+ });
+ audit.setBranding({
+ application: 'thing',
+ brand: 'other'
+ });
+ assert.equal(
+ audit.data.rules.target.helpUrl,
+ 'https://dequeuniversity.com/rules/customer-x/x.y/target?application=axeAPI'
+ );
+ });
+ });
+
+ describe('Audit#addRule', function() {
+ it('should override existing rule', function() {
+ var audit = new Audit();
+ audit.addRule({
+ id: 'target',
+ matches: 'function () {return "hello";}',
+ selector: 'bob'
+ });
+ assert.lengthOf(audit.rules, 1);
+ assert.equal(audit.rules[0].selector, 'bob');
+ assert.equal(audit.rules[0].matches(), 'hello');
+
+ audit.addRule({
+ id: 'target',
+ selector: 'fred'
+ });
+
+ assert.lengthOf(audit.rules, 1);
+ assert.equal(audit.rules[0].selector, 'fred');
+ assert.equal(audit.rules[0].matches(), 'hello');
+ });
+ it('should otherwise push new rule', function() {
+ var audit = new Audit();
+ audit.addRule({
+ id: 'target',
+ selector: 'bob'
+ });
+ assert.lengthOf(audit.rules, 1);
+ assert.equal(audit.rules[0].id, 'target');
+ assert.equal(audit.rules[0].selector, 'bob');
+
+ audit.addRule({
+ id: 'target2',
+ selector: 'fred'
+ });
+
+ assert.lengthOf(audit.rules, 2);
+ assert.equal(audit.rules[1].id, 'target2');
+ assert.equal(audit.rules[1].selector, 'fred');
+ });
+ });
+
+ describe('Audit#resetRulesAndChecks', function() {
+ it('should override newly created check', function() {
+ var audit = new Audit();
+ assert.equal(audit.checks.target, undefined);
+ audit.addCheck({
+ id: 'target',
+ options: { value: 'jane' }
+ });
+ assert.ok(audit.checks.target);
+ assert.deepEqual(audit.checks.target.options, { value: 'jane' });
+ audit.resetRulesAndChecks();
+ assert.equal(audit.checks.target, undefined);
+ });
+ it('should reset locale', function() {
+ var audit = new Audit();
+ assert.equal(audit.lang, 'en');
+ audit.applyLocale({
+ lang: 'de'
+ });
+ assert.equal(audit.lang, 'de');
+ audit.resetRulesAndChecks();
+ assert.equal(audit.lang, 'en');
+ });
+ it('should reset brand', function() {
+ var audit = new Audit();
+ assert.equal(audit.brand, 'axe');
+ audit.setBranding({
+ brand: 'test'
+ });
+ assert.equal(audit.brand, 'test');
+ audit.resetRulesAndChecks();
+ assert.equal(audit.brand, 'axe');
+ });
+ it('should reset brand application', function() {
+ var audit = new Audit();
+ assert.equal(audit.application, 'axeAPI');
+ audit.setBranding({
+ application: 'test'
+ });
+ assert.equal(audit.application, 'test');
+ audit.resetRulesAndChecks();
+ assert.equal(audit.application, 'axeAPI');
+ });
+ it('should reset brand tagExlcude', function() {
+ axe._load({});
+ assert.deepEqual(axe._audit.tagExclude, ['experimental']);
+ axe.configure({
+ tagExclude: ['ninjas']
+ });
+ axe._audit.resetRulesAndChecks();
+ assert.deepEqual(axe._audit.tagExclude, ['experimental']);
+ });
+
+ it('should reset noHtml', function() {
+ var audit = new Audit();
+ audit.noHtml = true;
+ audit.resetRulesAndChecks();
+ assert.isFalse(audit.noHtml);
+ });
+ });
+
+ describe('Audit#addCheck', function() {
+ it('should create a new check', function() {
+ var audit = new Audit();
+ assert.equal(audit.checks.target, undefined);
+ audit.addCheck({
+ id: 'target',
+ options: { value: 'jane' }
+ });
+ assert.ok(audit.checks.target);
+ assert.deepEqual(audit.checks.target.options, { value: 'jane' });
+ });
+ it('should configure the metadata, if passed', function() {
+ var audit = new Audit();
+ assert.equal(audit.checks.target, undefined);
+ audit.addCheck({
+ id: 'target',
+ metadata: { guy: 'bob' }
+ });
+ assert.ok(audit.checks.target);
+ assert.equal(audit.data.checks.target.guy, 'bob');
+ });
+ it('should reconfigure existing check', function() {
+ var audit = new Audit();
+ var myTest = function() {};
+ audit.addCheck({
+ id: 'target',
+ evaluate: myTest,
+ options: { value: 'jane' }
+ });
+
+ assert.deepEqual(audit.checks.target.options, { value: 'jane' });
+
+ audit.addCheck({
+ id: 'target',
+ options: { value: 'fred' }
+ });
+
+ assert.equal(audit.checks.target.evaluate, myTest);
+ assert.deepEqual(audit.checks.target.options, { value: 'fred' });
+ });
+ it('should not turn messages into a function', function() {
+ var audit = new Audit();
+ var spec = {
+ id: 'target',
+ evaluate: 'function () { return "blah";}',
+ metadata: {
+ messages: {
+ fail: 'it failed'
+ }
+ }
+ };
+ audit.addCheck(spec);
+
+ assert.equal(typeof audit.checks.target.evaluate, 'function');
+ assert.equal(typeof audit.data.checks.target.messages.fail, 'string');
+ assert.equal(audit.data.checks.target.messages.fail, 'it failed');
+ });
+
+ it('should turn function strings into a function', function() {
+ var audit = new Audit();
+ var spec = {
+ id: 'target',
+ evaluate: 'function () { return "blah";}',
+ metadata: {
+ messages: {
+ fail: 'function () {return "it failed";}'
+ }
+ }
+ };
+ audit.addCheck(spec);
+
+ assert.equal(typeof audit.checks.target.evaluate, 'function');
+ assert.equal(typeof audit.data.checks.target.messages.fail, 'function');
+ assert.equal(audit.data.checks.target.messages.fail(), 'it failed');
+ });
+ });
+
+ describe('Audit#run', function() {
+ it('should run all the rules', function(done) {
+ fixture.innerHTML =
+ ' ' +
+ 'bananas
' +
+ ' ' +
+ 'FAIL ME ';
+
+ a.run(
+ { include: [axe.utils.getFlattenedTree(fixture)[0]] },
+ {},
+ function(results) {
+ var expected = [
+ {
+ id: 'positive1',
+ result: 'inapplicable',
+ pageLevel: false,
+ impact: null,
+ nodes: '...other tests cover this...'
+ },
+ {
+ id: 'positive2',
+ result: 'inapplicable',
+ pageLevel: false,
+ impact: null,
+ nodes: '...other tests cover this...'
+ },
+ {
+ id: 'negative1',
+ result: 'inapplicable',
+ pageLevel: false,
+ impact: null,
+ nodes: '...other tests cover this...'
+ },
+ {
+ id: 'positive3',
+ result: 'inapplicable',
+ pageLevel: false,
+ impact: null,
+ nodes: '...other tests cover this...'
+ }
+ ];
+
+ var out = results[0].nodes[0].node.source;
+ results.forEach(function(res) {
+ // attribute order is a pain in the lower back in IE, so we're not
+ // comparing nodes. Check.run and Rule.run do this.
+ res.nodes = '...other tests cover this...';
+ });
+
+ assert.deepEqual(JSON.parse(JSON.stringify(results)), expected);
+ assert.match(
+ out,
+ /^ /
+ );
+ done();
+ },
+ isNotCalled
+ );
+ });
+
+ it('should not run rules disabled by the options', function(done) {
+ a.run(
+ { include: [document] },
+ {
+ rules: {
+ positive3: {
+ enabled: false
+ }
+ }
+ },
+ function(results) {
+ assert.equal(results.length, 3);
+ done();
+ },
+ isNotCalled
+ );
+ });
+
+ it('should ensure audit.run recieves preload options', function(done) {
+ fixture.innerHTML = ' ';
+
+ var audit = new Audit();
+ audit.addRule({
+ id: 'preload1',
+ selector: '*'
+ });
+ audit.run = function(context, options, resolve, reject) {
+ var randomRule = this.rules[0];
+ randomRule.run(
+ context,
+ options,
+ function(ruleResult) {
+ ruleResult.OPTIONS_PASSED = options;
+ resolve([ruleResult]);
+ },
+ reject
+ );
+ };
+
+ var preloadOptions = {
+ preload: {
+ assets: ['cssom']
+ }
+ };
+ audit.run(
+ { include: [axe.utils.getFlattenedTree(fixture)[0]] },
+ {
+ preload: preloadOptions
+ },
+ function(res) {
+ assert.isDefined(res);
+
+ assert.lengthOf(res, 1);
+ assert.property(res[0], 'OPTIONS_PASSED');
+
+ var optionsPassed = res[0].OPTIONS_PASSED;
+ assert.property(optionsPassed, 'preload');
+ assert.deepEqual(optionsPassed.preload, preloadOptions);
+
+ // ensure cache is cleared
+ assert.isTrue(typeof axe._selectCache === 'undefined');
+
+ done();
+ },
+ noop
+ );
+ });
+
+ it.skip('should run rules (that do not need preload) and preload assets simultaneously', function(done) {
+ /**
+ * Note:
+ * overriding and resolving both check and preload with a delay,
+ * but the invoked timestamp should ensure that they were invoked almost immediately
+ */
+
+ fixture.innerHTML = '
';
+
+ var runStartTime = new Date();
+ var preloadInvokedTime = new Date();
+ var noPreloadCheckedInvokedTime = new Date();
+ var noPreloadRuleCheckEvaluateInvoked = false;
+ var preloadOverrideInvoked = false;
+
+ // override preload method
+ axe.utils.preload = function(options) {
+ preloadInvokedTime = new Date();
+ preloadOverrideInvoked = true;
+
+ return new Promise(function(res, rej) {
+ setTimeout(function() {
+ res(true);
+ }, 2000);
+ });
+ };
+
+ var audit = new Audit();
+ // add a rule and check that does not need preload
+ audit.addRule({
+ id: 'no-preload',
+ selector: 'div#div1',
+ any: ['no-preload-check'],
+ preload: false
+ });
+ audit.addCheck({
+ id: 'no-preload-check',
+ evaluate: function(node, options, vNode, context) {
+ noPreloadCheckedInvokedTime = new Date();
+ noPreloadRuleCheckEvaluateInvoked = true;
+ var ready = this.async();
+ setTimeout(function() {
+ ready(true);
+ }, 1000);
+ }
+ });
+
+ // add a rule which needs preload
+ audit.addRule({
+ id: 'yes-preload',
+ selector: 'div#div2',
+ preload: true
+ });
+
+ var preloadOptions = {
+ preload: {
+ assets: ['cssom']
+ }
+ };
+
+ var allowedDiff = 50;
+
+ audit.run(
+ { include: [axe.utils.getFlattenedTree(fixture)[0]] },
+ {
+ preload: preloadOptions
+ },
+ function(results) {
+ assert.isDefined(results);
+ // assert that check was invoked for rule(s)
+ assert.isTrue(noPreloadRuleCheckEvaluateInvoked);
+ // assert preload was invoked
+ assert.isTrue(preloadOverrideInvoked);
+ // assert that time diff(s)
+ // assert that run check invoked immediately
+ // choosing 5ms as an arbitary number
+ assert.isBelow(
+ noPreloadCheckedInvokedTime - runStartTime,
+ allowedDiff
+ );
+ // assert that preload invoked immediately
+ assert.isBelow(preloadInvokedTime - runStartTime, allowedDiff);
+ // ensure cache is clear
+ assert.isTrue(typeof axe._selectCache === 'undefined');
+ // done
+ done();
+ },
+ noop
+ );
+ });
+
+ it.skip('should pass assets from preload to rule check that needs assets as context', function(done) {
+ fixture.innerHTML = '
';
+
+ var yesPreloadRuleCheckEvaluateInvoked = false;
+ var preloadOverrideInvoked = false;
+
+ var preloadData = {
+ data: 'you got it!'
+ };
+ // override preload method
+ axe.utils.preload = function(options) {
+ preloadOverrideInvoked = true;
+ return Promise.resolve({
+ cssom: preloadData
+ });
+ };
+
+ var audit = new Audit();
+ // add a rule and check that does not need preload
+ audit.addRule({
+ id: 'no-preload',
+ selector: 'div#div1',
+ preload: false
+ });
+ // add a rule which needs preload
+ audit.addRule({
+ id: 'yes-preload',
+ selector: 'div#div2',
+ preload: true,
+ any: ['yes-preload-check']
+ });
+ audit.addCheck({
+ id: 'yes-preload-check',
+ evaluate: function(node, options, vNode, context) {
+ yesPreloadRuleCheckEvaluateInvoked = true;
+ this.data(context);
+ return true;
+ }
+ });
+
+ var preloadOptions = {
+ preload: {
+ assets: ['cssom']
+ }
+ };
+ audit.run(
+ { include: [axe.utils.getFlattenedTree(fixture)[0]] },
+ {
+ preload: preloadOptions
+ },
+ function(results) {
+ assert.isDefined(results);
+ // assert that check was invoked for rule(s)
+ assert.isTrue(yesPreloadRuleCheckEvaluateInvoked);
+ // assert preload was invoked
+ assert.isTrue(preloadOverrideInvoked);
+
+ // assert preload data that was passed to check
+ var ruleResult = results.filter(function(r) {
+ return (r.id = 'yes-preload' && r.nodes.length > 0);
+ })[0];
+ var checkResult = ruleResult.nodes[0].any[0];
+ assert.isDefined(checkResult.data);
+ assert.property(checkResult.data, 'cssom');
+ assert.deepEqual(checkResult.data.cssom, preloadData);
+ // ensure cache is clear
+ assert.isTrue(typeof axe._selectCache === 'undefined');
+ // done
+ done();
+ },
+ noop
+ );
+ });
+
+ it.skip('should continue to run rules and return result when preload is rejected', function(done) {
+ fixture.innerHTML = '
';
+
+ var preloadOverrideInvoked = false;
+ var preloadNeededCheckInvoked = false;
+ var rejectionMsg =
+ 'Boom! Things went terribly wrong! (But this was intended in this test)';
+
+ // override preload method
+ axe.utils.preload = function(options) {
+ preloadOverrideInvoked = true;
+ return Promise.reject(rejectionMsg);
+ };
+
+ var audit = new Audit();
+ // add a rule and check that does not need preload
+ audit.addRule({
+ id: 'no-preload',
+ selector: 'div#div1',
+ preload: false
+ });
+ // add a rule which needs preload
+ audit.addRule({
+ id: 'yes-preload',
+ selector: 'div#div2',
+ preload: true,
+ any: ['yes-preload-check']
+ });
+ audit.addCheck({
+ id: 'yes-preload-check',
+ evaluate: function(node, options, vNode, context) {
+ preloadNeededCheckInvoked = true;
+ this.data(context);
+ return true;
+ }
+ });
+
+ var preloadOptions = {
+ preload: {
+ assets: ['cssom']
+ }
+ };
+ audit.run(
+ { include: [axe.utils.getFlattenedTree(fixture)[0]] },
+ {
+ preload: preloadOptions
+ },
+ function(results) {
+ assert.isDefined(results);
+ // assert preload was invoked
+ assert.isTrue(preloadOverrideInvoked);
+
+ // assert that both rules ran, although preload failed
+ assert.lengthOf(results, 2);
+
+ // assert that because preload failed
+ // cssom was not populated on context of repective check
+ assert.isTrue(preloadNeededCheckInvoked);
+ var ruleResult = results.filter(function(r) {
+ return (r.id = 'yes-preload' && r.nodes.length > 0);
+ })[0];
+ var checkResult = ruleResult.nodes[0].any[0];
+ assert.isDefined(checkResult.data);
+ assert.notProperty(checkResult.data, 'cssom');
+ // done
+ done();
+ },
+ noop
+ );
+ });
+
+ it('should continue to run rules and return result when axios time(s)out and rejects preload', function(done) {
+ fixture.innerHTML = '
';
+
+ // there is no stubbing here,
+ // the actual axios call is invoked, and timedout immediately as timeout is set to 0.1
+
+ var preloadNeededCheckInvoked = false;
+ var audit = new Audit();
+ // add a rule and check that does not need preload
+ audit.addRule({
+ id: 'no-preload',
+ selector: 'div#div1',
+ preload: false
+ });
+ // add a rule which needs preload
+ audit.addRule({
+ id: 'yes-preload',
+ selector: 'div#div2',
+ preload: true,
+ any: ['yes-preload-check']
+ });
+ audit.addCheck({
+ id: 'yes-preload-check',
+ evaluate: function(node, options, vNode, context) {
+ preloadNeededCheckInvoked = true;
+ this.data(context);
+ return true;
+ }
+ });
+
+ var preloadOptions = {
+ preload: {
+ assets: ['cssom'],
+ timeout: 0.1
+ }
+ };
+ audit.run(
+ { include: [axe.utils.getFlattenedTree(fixture)[0]] },
+ {
+ preload: preloadOptions
+ },
+ function(results) {
+ assert.isDefined(results);
+ // assert that both rules ran, although preload failed
+ assert.lengthOf(results, 2);
+
+ // assert that because preload failed
+ // cssom was not populated on context of repective check
+ assert.isTrue(preloadNeededCheckInvoked);
+ var ruleResult = results.filter(function(r) {
+ return (r.id = 'yes-preload' && r.nodes.length > 0);
+ })[0];
+ var checkResult = ruleResult.nodes[0].any[0];
+ assert.isDefined(checkResult.data);
+ assert.notProperty(checkResult.data, 'cssom');
+ // done
+ done();
+ },
+ noop
+ );
+ });
+
+ it.skip('should assign an empty array to axe._selectCache', function(done) {
+ var saved = axe.utils.ruleShouldRun;
+ axe.utils.ruleShouldRun = function() {
+ assert.equal(axe._selectCache.length, 0);
+ return false;
+ };
+ a.run(
+ { include: [document] },
+ {},
+ function() {
+ axe.utils.ruleShouldRun = saved;
+ done();
+ },
+ isNotCalled
+ );
+ });
+
+ it('should clear axe._selectCache', function(done) {
+ a.run(
+ { include: [document] },
+ {
+ rules: {}
+ },
+ function() {
+ assert.isTrue(typeof axe._selectCache === 'undefined');
+ done();
+ },
+ isNotCalled
+ );
+ });
+
+ it('should not run rules disabled by the configuration', function(done) {
+ var a = new Audit();
+ var success = true;
+ a.rules.push(
+ new Rule({
+ id: 'positive1',
+ selector: '*',
+ enabled: false,
+ any: [
+ {
+ id: 'positive1-check1',
+ evaluate: function() {
+ success = false;
+ }
+ }
+ ]
+ })
+ );
+ a.run(
+ { include: [document] },
+ {},
+ function() {
+ assert.ok(success);
+ done();
+ },
+ isNotCalled
+ );
+ });
+
+ it("should call the rule's run function", function(done) {
+ var targetRule = mockRules[mockRules.length - 1],
+ rule = axe.utils.findBy(a.rules, 'id', targetRule.id),
+ called = false,
+ orig;
+
+ fixture.innerHTML = 'link ';
+ orig = rule.run;
+ rule.run = function(node, options, callback) {
+ called = true;
+ callback({});
+ };
+ a.run(
+ { include: [document] },
+ {},
+ function() {
+ assert.isTrue(called);
+ rule.run = orig;
+ done();
+ },
+ isNotCalled
+ );
+ });
+
+ it('should pass the option to the run function', function(done) {
+ var targetRule = mockRules[mockRules.length - 1],
+ rule = axe.utils.findBy(a.rules, 'id', targetRule.id),
+ passed = false,
+ orig,
+ options;
+
+ fixture.innerHTML = 'link ';
+ orig = rule.run;
+ rule.run = function(node, o, callback) {
+ assert.deepEqual(o, options);
+ passed = true;
+ callback({});
+ };
+ options = { rules: {} };
+ (options.rules[targetRule.id] = {}).data = 'monkeys';
+ a.run(
+ { include: [document] },
+ options,
+ function() {
+ assert.ok(passed);
+ rule.run = orig;
+ done();
+ },
+ isNotCalled
+ );
+ });
+
+ it('should skip pageLevel rules if context is not set to entire page', function() {
+ var audit = new Audit();
+
+ audit.rules.push(
+ new Rule({
+ pageLevel: true,
+ enabled: true,
+ evaluate: function() {
+ assert.ok(false, 'Should not run');
+ }
+ })
+ );
+
+ audit.run(
+ { include: [document.body], page: false },
+ {},
+ function(results) {
+ assert.deepEqual(results, []);
+ },
+ isNotCalled
+ );
+ });
+
+ it('catches errors and passes them as a cantTell result', function(done) {
+ var err = new Error('Launch the super sheep!');
+ a.addRule({
+ id: 'throw1',
+ selector: '*',
+ any: [
+ {
+ id: 'throw1-check1'
+ }
+ ]
+ });
+ a.addCheck({
+ id: 'throw1-check1',
+ evaluate: function() {
+ throw err;
+ }
+ });
+ axe._tree = axe.utils.getFlattenedTree(fixture);
+ axe._selectorData = axe.utils.getSelectorData(axe._tree);
+ a.run(
+ { include: [axe._tree[0]] },
+ {
+ runOnly: {
+ type: 'rule',
+ values: ['throw1']
+ }
+ },
+ function(results) {
+ assert.lengthOf(results, 1);
+ assert.equal(results[0].result, 'cantTell');
+ assert.equal(results[0].message, err.message);
+ assert.equal(results[0].stack, err.stack);
+ assert.equal(results[0].error, err);
+ done();
+ },
+ isNotCalled
+ );
+ });
+
+ it('should not halt if errors occur', function(done) {
+ a.addRule({
+ id: 'throw1',
+ selector: '*',
+ any: [
+ {
+ id: 'throw1-check1'
+ }
+ ]
+ });
+ a.addCheck({
+ id: 'throw1-check1',
+ evaluate: function() {
+ throw new Error('Launch the super sheep!');
+ }
+ });
+ a.run(
+ { include: [axe.utils.getFlattenedTree(fixture)[0]] },
+ {
+ runOnly: {
+ type: 'rule',
+ values: ['throw1', 'positive1']
+ }
+ },
+ function() {
+ done();
+ },
+ isNotCalled
+ );
+ });
+
+ it('should run audit.normalizeOptions to ensure valid input', function() {
+ fixture.innerHTML =
+ ' ' +
+ 'bananas
' +
+ ' ' +
+ 'FAIL ME ';
+ var checked = 'options not validated';
+
+ a.normalizeOptions = function() {
+ checked = 'options validated';
+ };
+
+ a.run({ include: [fixture] }, {}, noop, isNotCalled);
+ assert.equal(checked, 'options validated');
+ });
+
+ it('should halt if an error occurs when debug is set', function(done) {
+ a.addRule({
+ id: 'throw1',
+ selector: '*',
+ any: [
+ {
+ id: 'throw1-check1'
+ }
+ ]
+ });
+ a.addCheck({
+ id: 'throw1-check1',
+ evaluate: function() {
+ throw new Error('Launch the super sheep!');
+ }
+ });
+
+ // check error node requires _selectorCache to be setup
+ axe.setup();
+
+ a.run(
+ { include: [axe.utils.getFlattenedTree(fixture)[0]] },
+ {
+ debug: true,
+ runOnly: {
+ type: 'rule',
+ values: ['throw1']
+ }
+ },
+ noop,
+ function(err) {
+ assert.equal(err.message, 'Launch the super sheep!');
+ done();
+ }
+ );
+ });
+ });
+
+ describe('Audit#after', function() {
+ it('should run Rule#after on any rule whose result is passed in', function() {
+ /*eslint no-unused-vars:0*/
+ var audit = new Audit();
+ var success = false;
+ var options = [{ id: 'hehe', enabled: true, monkeys: 'bananas' }];
+ var results = [
+ {
+ id: 'hehe',
+ monkeys: 'bananas'
+ }
+ ];
+ audit.rules.push(
+ new Rule({
+ id: 'hehe',
+ pageLevel: false,
+ enabled: false
+ })
+ );
+
+ audit.rules[0].after = function(res, opts) {
+ assert.equal(res, results[0]);
+ assert.deepEqual(opts, options);
+ success = true;
+ };
+
+ audit.after(results, options);
+ });
+ });
+
+ describe('Audit#normalizeOptions', function() {
+ it('returns the options object when it is valid', function() {
+ var opt = {
+ runOnly: {
+ type: 'rule',
+ values: ['positive1', 'positive2']
+ },
+ rules: {
+ negative1: { enabled: false }
+ }
+ };
+ assert(a.normalizeOptions(opt), opt);
+ });
+
+ it('allows `value` as alternative to `values`', function() {
+ var opt = {
+ runOnly: {
+ type: 'rule',
+ value: ['positive1', 'positive2']
+ }
+ };
+ var out = a.normalizeOptions(opt);
+ assert.deepEqual(out.runOnly.values, ['positive1', 'positive2']);
+ assert.isUndefined(out.runOnly.value);
+ });
+
+ it('allows type: rules as an alternative to type: rule', function() {
+ var opt = {
+ runOnly: {
+ type: 'rules',
+ values: ['positive1', 'positive2']
+ }
+ };
+ assert(a.normalizeOptions(opt).runOnly.type, 'rule');
+ });
+
+ it('allows type: tags as an alternative to type: tag', function() {
+ var opt = {
+ runOnly: {
+ type: 'tags',
+ values: ['positive']
+ }
+ };
+ assert(a.normalizeOptions(opt).runOnly.type, 'tag');
+ });
+
+ it('allows type: undefined as an alternative to type: tag', function() {
+ var opt = {
+ runOnly: {
+ values: ['positive']
+ }
+ };
+ assert(a.normalizeOptions(opt).runOnly.type, 'tag');
+ });
+
+ it('allows runOnly as an array as an alternative to type: tag', function() {
+ var opt = { runOnly: ['positive', 'negative'] };
+ var out = a.normalizeOptions(opt);
+ assert(out.runOnly.type, 'tag');
+ assert.deepEqual(out.runOnly.values, ['positive', 'negative']);
+ });
+
+ it('allows runOnly as an array as an alternative to type: rule', function() {
+ var opt = { runOnly: ['positive1', 'negative1'] };
+ var out = a.normalizeOptions(opt);
+ assert(out.runOnly.type, 'rule');
+ assert.deepEqual(out.runOnly.values, ['positive1', 'negative1']);
+ });
+
+ it('throws an error if runOnly contains both rules and tags', function() {
+ assert.throws(function() {
+ a.normalizeOptions({
+ runOnly: ['positive', 'negative1']
+ });
+ });
+ });
+
+ it('defaults runOnly to type: tag', function() {
+ var opt = { runOnly: ['fakeTag'] };
+ var out = a.normalizeOptions(opt);
+ assert(out.runOnly.type, 'tag');
+ assert.deepEqual(out.runOnly.values, ['fakeTag']);
+ });
+
+ it('throws an error runOnly.values not an array', function() {
+ assert.throws(function() {
+ a.normalizeOptions({
+ runOnly: {
+ type: 'rule',
+ values: { badProp: 'badValue' }
+ }
+ });
+ });
+ });
+
+ it('throws an error runOnly.values an empty', function() {
+ assert.throws(function() {
+ a.normalizeOptions({
+ runOnly: {
+ type: 'rule',
+ values: []
+ }
+ });
+ });
+ });
+
+ it('throws an error runOnly.type is unknown', function() {
+ assert.throws(function() {
+ a.normalizeOptions({
+ runOnly: {
+ type: 'something-else',
+ values: ['wcag2aa']
+ }
+ });
+ });
+ });
+
+ it('throws an error when option.runOnly has an unknown rule', function() {
+ assert.throws(function() {
+ a.normalizeOptions({
+ runOnly: {
+ type: 'rule',
+ values: ['frakeRule']
+ }
+ });
+ });
+ });
+
+ it("doesn't throw an error when option.runOnly has an unknown tag", function() {
+ assert.doesNotThrow(function() {
+ a.normalizeOptions({
+ runOnly: {
+ type: 'tags',
+ values: ['fakeTag']
+ }
+ });
+ });
+ });
+
+ it('throws an error when option.rules has an unknown rule', function() {
+ assert.throws(function() {
+ a.normalizeOptions({
+ rules: {
+ fakeRule: { enabled: false }
+ }
+ });
+ });
+ });
+ });
});
diff --git a/test/core/public/configure.js b/test/core/public/configure.js
index 6335e4e372..0123cd77f9 100644
--- a/test/core/public/configure.js
+++ b/test/core/public/configure.js
@@ -1,1014 +1,1022 @@
describe('axe.configure', function() {
- 'use strict';
- // var Rule = axe._thisWillBeDeletedDoNotUse.base.Rule;
- // var Check = axe._thisWillBeDeletedDoNotUse.base.Check;
- var fixture = document.getElementById('fixture');
- var axeVersion = axe.version;
-
- afterEach(function() {
- fixture.innerHTML = '';
- axe.version = axeVersion;
- });
-
- beforeEach(function() {
- axe._audit = null;
- });
-
- it('should throw if audit is not configured', function() {
- assert.throws(
- function() {
- axe.configure({});
- },
- Error,
- /^No audit configured/
- );
- });
-
- it("should override an audit's reporter - string", function() {
- axe._load({});
- assert.isNull(axe._audit.reporter);
-
- axe.configure({ reporter: 'v1' });
- assert.equal(axe._audit.reporter, 'v1');
- });
-
- it('should not allow setting to an un-registered reporter', function() {
- axe._load({ reporter: 'v1' });
- axe.configure({ reporter: 'no-exist-evar-plz' });
- assert.equal(axe._audit.reporter, 'v1');
- });
-
- it('should allow for addition of rules', function() {
- axe._load({});
- axe.configure({
- rules: [
- {
- id: 'bob',
- metadata: {
- joe: 'joe'
- }
- }
- ]
- });
-
- assert.lengthOf(axe._audit.rules, 1);
- // TODO: this does not work yet thanks to webpack
- // assert.instanceOf(axe._audit.rules[0], Rule);
- assert.equal(axe._audit.rules[0].id, 'bob');
- assert.deepEqual(axe._audit.data.rules.bob.joe, 'joe');
- });
-
- it('should throw error if rules property is invalid', function() {
- assert.throws(function() {
- axe.configure({ rules: 'hello' }),
- TypeError,
- /^Rules property must be an array/;
- });
- });
-
- it('should throw error if rule is invalid', function() {
- assert.throws(function() {
- axe.configure({ rules: ['hello'] }),
- TypeError,
- /Configured rule "hello" is invalid/;
- });
- });
-
- it('should throw error if rule does not have an id', function() {
- assert.throws(function() {
- axe.configure({ rules: [{ foo: 'bar' }] }),
- TypeError,
- /Configured rule "{foo:\"bar\"}" is invalid/;
- });
- });
-
- it('should call setBranding when passed options', function() {
- axe._load({});
- axe.configure({
- rules: [
- {
- id: 'bob',
- selector: 'pass'
- }
- ],
- branding: {}
- });
- assert.lengthOf(axe._audit.rules, 1);
- assert.equal(
- axe._audit.data.rules.bob.helpUrl,
- 'https://dequeuniversity.com/rules/axe/x.y/bob?application=axeAPI'
- );
- axe.configure({
- branding: {
- application: 'thing',
- brand: 'thung'
- }
- });
- assert.equal(
- axe._audit.data.rules.bob.helpUrl,
- 'https://dequeuniversity.com/rules/thung/x.y/bob?application=thing'
- );
- });
-
- it('sets branding on newly configured rules', function() {
- axe._load({});
- axe.configure({
- branding: {
- application: 'thing',
- brand: 'thung'
- }
- });
- axe.configure({
- rules: [
- {
- id: 'bob',
- selector: 'pass'
- }
- ]
- });
-
- assert.equal(
- axe._audit.data.rules.bob.helpUrl,
- 'https://dequeuniversity.com/rules/thung/x.y/bob?application=thing'
- );
- });
-
- it('should allow for overwriting of rules', function() {
- axe._load({
- data: {
- rules: {
- bob: 'not-joe'
- }
- },
- rules: {
- id: 'bob',
- selector: 'fail'
- }
- });
- axe.configure({
- rules: [
- {
- id: 'bob',
- selector: 'pass',
- metadata: {
- joe: 'joe'
- }
- }
- ]
- });
-
- assert.lengthOf(axe._audit.rules, 1);
- // assert.instanceOf(axe._audit.rules[0], Rule);
- assert.equal(axe._audit.rules[0].id, 'bob');
- assert.equal(axe._audit.rules[0].selector, 'pass');
- assert.equal(axe._audit.data.rules.bob.joe, 'joe');
- });
-
- it('should allow for the addition of checks', function() {
- axe._load({});
- axe.configure({
- checks: [
- {
- id: 'bob',
- options: { value: true },
- metadata: {
- joe: 'joe'
- }
- }
- ]
- });
-
- // assert.instanceOf(axe._audit.checks.bob, Check);
- assert.equal(axe._audit.checks.bob.id, 'bob');
- assert.isTrue(axe._audit.checks.bob.options.value);
- assert.equal(axe._audit.data.checks.bob.joe, 'joe');
- });
-
- it('should throw error if checks property is invalid', function() {
- assert.throws(function() {
- axe.configure({ checks: 'hello' }),
- TypeError,
- /^Checks property must be an array/;
- });
- });
-
- it('should throw error if check is invalid', function() {
- assert.throws(function() {
- axe.configure({ checks: ['hello'] }),
- TypeError,
- /Configured check "hello" is invalid/;
- });
- });
-
- it('should throw error if check does not have an id', function() {
- assert.throws(function() {
- axe.configure({ checks: [{ foo: 'bar' }] }),
- TypeError,
- /Configured check "{foo:\"bar\"}" is invalid/;
- });
- });
-
- it('should allow for the overwriting of checks', function() {
- axe._load({
- data: {
- checks: {
- bob: 'not-joe'
- }
- },
- checks: [
- {
- id: 'bob',
- options: { value: false }
- }
- ]
- });
- axe.configure({
- checks: [
- {
- id: 'bob',
- options: { value: true },
- metadata: {
- joe: 'joe'
- }
- }
- ]
- });
-
- // assert.instanceOf(axe._audit.checks.bob, Check);
- assert.equal(axe._audit.checks.bob.id, 'bob');
- assert.isTrue(axe._audit.checks.bob.options.value);
- assert.equal(axe._audit.data.checks.bob.joe, 'joe');
- });
-
- it('should create an execution context for check messages', function() {
- axe._load({});
- axe.configure({
- checks: [
- {
- id: 'bob',
- metadata: {
- messages: {
- pass: "function () { return 'Bob' + ' John';}",
- fail: 'Bob Pete'
- }
- }
- }
- ]
- });
-
- assert.isFunction(axe._audit.data.checks.bob.messages.pass);
- assert.isString(axe._audit.data.checks.bob.messages.fail);
- assert.equal(axe._audit.data.checks.bob.messages.pass(), 'Bob John');
- assert.equal(axe._audit.data.checks.bob.messages.fail, 'Bob Pete');
- });
-
- it('overrides the default value of audit.tagExclude', function() {
- axe._load({});
- assert.deepEqual(axe._audit.tagExclude, ['experimental']);
-
- axe.configure({
- tagExclude: ['ninjas']
- });
- assert.deepEqual(axe._audit.tagExclude, ['ninjas']);
- });
-
- it('disables all untouched rules with disableOtherRules', function() {
- axe._load({
- rules: [{ id: 'captain-america' }, { id: 'thor' }, { id: 'spider-man' }]
- });
- axe.configure({
- disableOtherRules: true,
- rules: [{ id: 'captain-america' }, { id: 'black-panther' }]
- });
-
- assert.lengthOf(axe._audit.rules, 4);
- assert.equal(axe._audit.rules[0].id, 'captain-america');
- assert.equal(axe._audit.rules[0].enabled, true);
- assert.equal(axe._audit.rules[1].id, 'thor');
- assert.equal(axe._audit.rules[1].enabled, false);
- assert.equal(axe._audit.rules[2].id, 'spider-man');
- assert.equal(axe._audit.rules[2].enabled, false);
- assert.equal(axe._audit.rules[3].id, 'black-panther');
- assert.equal(axe._audit.rules[3].enabled, true);
- });
-
- describe('given a locale object', function() {
- beforeEach(function() {
- axe._load({});
-
- axe.configure({
- rules: [
- {
- id: 'greeting',
- selector: 'div',
- excludeHidden: false,
- tags: ['foo', 'bar'],
- metadata: {
- description: 'This is a rule that rules',
- help: 'ABCDEFGHIKLMNOPQRSTVXYZ'
- }
- }
- ],
- checks: [
- {
- id: 'banana',
- evaluate: function() {},
- metadata: {
- impact: 'srsly serious',
- messages: {
- pass: 'yay',
- fail: 'boo',
- incomplete: {
- foo: 'a',
- bar: 'b',
- baz: 'c'
- }
- }
- }
- }
- ]
- });
- });
-
- it('should update check and rule metadata', function() {
- axe.configure({
- locale: {
- lang: 'lol',
- rules: {
- greeting: {
- description: 'hello',
- help: 'hi'
- }
- },
- checks: {
- banana: {
- pass: 'pizza',
- fail: 'icecream',
- incomplete: {
- foo: 'meat',
- bar: 'fruit',
- baz: 'vegetables'
- }
- }
- }
- }
- });
-
- var audit = axe._audit;
- var localeData = audit.data;
-
- assert.equal(localeData.rules.greeting.help, 'hi');
- assert.equal(localeData.rules.greeting.description, 'hello');
- assert.equal(localeData.checks.banana.messages.pass, 'pizza');
- assert.equal(localeData.checks.banana.messages.fail, 'icecream');
- assert.deepEqual(localeData.checks.banana.messages.incomplete, {
- foo: 'meat',
- bar: 'fruit',
- baz: 'vegetables'
- });
- });
-
- it('should merge locales (favoring "new")', function() {
- axe.configure({
- locale: {
- lang: 'lol',
- rules: { greeting: { description: 'hello' } },
- checks: {
- banana: {
- fail: 'icecream'
- }
- }
- }
- });
-
- var audit = axe._audit;
- var localeData = audit.data;
-
- assert.equal(localeData.rules.greeting.help, 'ABCDEFGHIKLMNOPQRSTVXYZ');
- assert.equal(localeData.rules.greeting.description, 'hello');
- assert.equal(localeData.checks.banana.messages.pass, 'yay');
- assert.equal(localeData.checks.banana.messages.fail, 'icecream');
- assert.deepEqual(localeData.checks.banana.messages.incomplete, {
- foo: 'a',
- bar: 'b',
- baz: 'c'
- });
- });
-
- it('sets the lang property', function() {
- axe.configure({
- locale: {
- lang: 'lol',
- rules: { greeting: { description: 'hello' } },
- checks: {
- banana: {
- fail: 'icecream'
- }
- }
- }
- });
-
- assert.equal(axe._audit.lang, 'lol');
- });
-
- it('should call doT.compile if a messages uses doT syntax', function() {
- axe.configure({
- locale: {
- lang: 'lol',
- rules: { greeting: { description: 'hello' } },
- checks: {
- banana: {
- fail: 'icecream {{=it.data.value}}'
- }
- }
- }
- });
-
- var audit = axe._audit;
- var localeData = audit.data;
-
- assert.isTrue(
- typeof localeData.checks.banana.messages.fail === 'function'
- );
- });
-
- it('should leave the messages as a string if it does not use doT syntax', function() {
- axe.configure({
- locale: {
- lang: 'lol',
- rules: { greeting: { description: 'hello' } },
- checks: {
- banana: {
- fail: 'icecream ${data.value}'
- }
- }
- }
- });
-
- var audit = axe._audit;
- var localeData = audit.data;
-
- assert.isTrue(typeof localeData.checks.banana.messages.fail === 'string');
- });
-
- it('should update failure messages', function() {
- axe._load({
- data: {
- failureSummaries: {
- any: {
- failureMessage: function() {
- return 'failed any';
- }
- },
- none: {
- failureMessage: function() {
- return 'failed none';
- }
- }
- },
- incompleteFallbackMessage: function() {
- return 'failed incomplete';
- }
- }
- });
-
- axe.configure({
- locale: {
- lang: 'lol',
- failureSummaries: {
- any: {
- failureMessage: 'foo'
- },
- none: {
- failureMessage: 'bar'
- }
- },
- incompleteFallbackMessage: 'baz'
- }
- });
-
- var audit = axe._audit;
- var localeData = audit.data;
-
- assert.equal(localeData.failureSummaries.any.failureMessage, 'foo');
- assert.equal(localeData.failureSummaries.none.failureMessage, 'bar');
- assert.equal(localeData.incompleteFallbackMessage, 'baz');
- });
-
- it('should merge failure messages', function() {
- axe._load({
- data: {
- failureSummaries: {
- any: {
- failureMessage: function() {
- return 'failed any';
- }
- },
- none: {
- failureMessage: function() {
- return 'failed none';
- }
- }
- },
- incompleteFallbackMessage: function() {
- return 'failed incomplete';
- }
- }
- });
-
- axe.configure({
- locale: {
- lang: 'lol',
- failureSummaries: {
- any: {
- failureMessage: 'foo'
- }
- }
- }
- });
-
- var audit = axe._audit;
- var localeData = audit.data;
-
- assert.equal(localeData.failureSummaries.any.failureMessage, 'foo');
- assert.equal(
- localeData.failureSummaries.none.failureMessage(),
- 'failed none'
- );
- assert.equal(localeData.incompleteFallbackMessage(), 'failed incomplete');
- });
-
- describe('only given checks', function() {
- it('should not error', function() {
- assert.doesNotThrow(function() {
- axe.configure({
- locale: {
- lang: 'lol',
- checks: {
- banana: {
- fail: 'icecream',
- incomplete: {
- baz: 'vegetables'
- }
- }
- }
- }
- });
- });
- });
- });
-
- describe('only given rules', function() {
- it('should not error', function() {
- assert.doesNotThrow(function() {
- axe.configure({
- locale: {
- rules: { greeting: { help: 'foo', description: 'bar' } }
- }
- });
- });
- });
- });
-
- describe('check incomplete messages', function() {
- beforeEach(function() {
- axe.configure({
- checks: [
- {
- id: 'panda',
- evaluate: function() {},
- metadata: {
- impact: 'yep',
- messages: {
- pass: 'p',
- fail: 'f',
- incomplete: 'i'
- }
- }
- }
- ]
- });
- });
-
- it('should support strings', function() {
- axe.configure({
- locale: {
- checks: {
- panda: {
- incomplete: 'radio'
- }
- }
- }
- });
-
- assert.equal(axe._audit.data.checks.panda.messages.incomplete, 'radio');
- });
-
- it('should shallow-merge objects', function() {
- axe.configure({
- locale: {
- lang: 'lol',
- checks: {
- banana: {
- incomplete: {
- baz: 'vegetables'
- }
- }
- }
- }
- });
-
- assert.deepEqual(axe._audit.data.checks.banana.messages.incomplete, {
- foo: 'a',
- bar: 'b',
- baz: 'vegetables'
- });
- });
- });
-
- // This test ensures we do not drop additional properties added to
- // checks. See https://github.com/dequelabs/axe-core/pull/1036/files#r207738673
- // for reasoning.
- it('should keep existing properties on check data', function() {
- axe.configure({
- checks: [
- {
- id: 'banana',
- metadata: {
- impact: 'potato',
- foo: 'bar',
- messages: {
- pass: 'pass',
- fail: 'fail',
- incomplete: 'incomplete'
- }
- }
- }
- ]
- });
-
- axe.configure({
- locale: {
- lang: 'lol',
- checks: {
- banana: {
- pass: 'yay banana'
- }
- }
- }
- });
-
- var banana = axe._audit.data.checks.banana;
- assert.equal(banana.impact, 'potato');
- assert.equal(banana.foo, 'bar');
- assert.equal(banana.messages.pass, 'yay banana');
- });
-
- it('should error when provided an unknown rule id', function() {
- assert.throws(function() {
- axe.configure({
- locale: {
- rules: { nope: { help: 'helpme' } }
- }
- });
- }, /unknown rule: "nope"/);
- });
-
- it('should error when provided an unknown check id', function() {
- assert.throws(function() {
- axe.configure({
- locale: {
- checks: { nope: { pass: 'helpme' } }
- }
- });
- }, /unknown check: "nope"/);
- });
-
- it('should error when provided an unknown failure summary', function() {
- assert.throws(function() {
- axe.configure({
- locale: {
- failureSummaries: {
- nope: { failureMessage: 'helpme' }
- }
- }
- });
- });
- });
-
- it('should set default locale', function() {
- assert.isNull(axe._audit._defaultLocale);
- axe.configure({
- locale: {
- lang: 'lol',
- checks: {
- banana: {
- pass: 'yay banana'
- }
- }
- }
- });
- assert.ok(axe._audit._defaultLocale);
- });
-
- describe('also given metadata', function() {
- it('should favor the locale', function() {
- axe.configure({
- locale: {
- lang: 'lol',
- rules: {
- greeting: {
- help: 'hi'
- }
- }
- },
- rules: [
- {
- id: 'greeting',
- metadata: {
- help: 'potato'
- }
- }
- ]
- });
-
- var audit = axe._audit;
- var localeData = audit.data;
-
- assert.equal(localeData.rules.greeting.help, 'hi');
- });
- });
-
- describe('after locale has been set', function() {
- describe('the provided messages', function() {
- it('should allow for doT templating', function() {
- axe.configure({
- locale: {
- lang: 'foo',
- rules: {
- greeting: {
- help: 'foo: {{=it.data}}.'
- }
- }
- }
- });
-
- var greeting = axe._audit.data.rules.greeting;
- var value = greeting.help({
- data: 'bar'
- });
- assert.equal(value, 'foo: bar.');
- });
- });
- });
- });
-
- describe('given an axeVersion property', function() {
- beforeEach(function() {
- axe._load({});
- axe.version = '1.2.3';
- });
-
- it('should not throw if version matches axe.version', function() {
- assert.doesNotThrow(function fn() {
- axe.configure({
- axeVersion: '1.2.3'
- });
-
- axe.version = '1.2.3-canary.2664bae';
- axe.configure({
- axeVersion: '1.2.3-canary.2664bae'
- });
- });
- });
-
- it('should not throw if patch version is less than axe.version', function() {
- assert.doesNotThrow(function fn() {
- axe.configure({
- axeVersion: '1.2.0'
- });
- });
- });
-
- it('should not throw if minor version is less than axe.version', function() {
- assert.doesNotThrow(function fn() {
- axe.configure({
- axeVersion: '1.1.9'
- });
- });
- });
-
- it('should not throw if versions match and axe has a canary version', function() {
- axe.version = '1.2.3-canary.2664bae';
- assert.doesNotThrow(function fn() {
- axe.configure({
- axeVersion: '1.2.3'
- });
- });
- });
-
- it('should throw if invalid version', function() {
- assert.throws(function fn() {
- axe.configure({
- axeVersion: '2'
- });
- }, 'Invalid configured version 2');
-
- assert.throws(function fn() {
- axe.configure({
- axeVersion: '2..'
- });
- }, 'Invalid configured version 2..');
- });
-
- it('should throw if major version is different than axe.version', function() {
- assert.throws(function fn() {
- axe.configure(
- {
- axeVersion: '2.0.0'
- },
- /^Configured version/
- );
- });
- assert.throws(function fn() {
- axe.configure(
- {
- axeVersion: '0.1.2'
- },
- /^Configured version/
- );
- });
- });
-
- it('should throw if minor version is greater than axe.version', function() {
- assert.throws(function fn() {
- axe.configure(
- {
- axeVersion: '1.3.0'
- },
- /^Configured version/
- );
- });
- });
-
- it('should throw if patch version is greater than axe.version', function() {
- assert.throws(function fn() {
- axe.configure(
- {
- axeVersion: '1.2.9'
- },
- /^Configured version/
- );
- });
- });
-
- it('should throw if versions match and axeVersion has a canary version', function() {
- assert.throws(function fn() {
- axe.configure(
- {
- axeVersion: '1.2.3-canary.2664bae'
- },
- /^Configured version/
- );
- });
- });
-
- it('should throw if versions match and both have a canary version', function() {
- axe.version = '1.2.3-canary.2664bae';
- assert.throws(function fn() {
- axe.configure(
- {
- axeVersion: '1.2.3-canary.a5d727c'
- },
- /^Configured version/
- );
- });
- });
-
- it('should accept ver property as fallback', function() {
- assert.throws(function fn() {
- axe.configure(
- {
- ver: '1.3.0'
- },
- /^Configured version/
- );
- });
- });
-
- it('should accept axeVersion over ver property', function() {
- assert.throws(function fn() {
- axe.configure(
- {
- ver: '0.1.2',
- axeVersion: '1.3.0'
- },
- /^Configured version 1\.3\.0/
- );
- });
- });
- });
-
- describe('given a standards object', function() {
- beforeEach(function() {
- axe._load({});
- });
-
- describe('ariaAttrs', function() {
- it('should allow creating new attr', function() {
- axe.configure({
- standards: {
- ariaAttrs: {
- newAttr: {
- type: 'string'
- }
- }
- }
- });
-
- var ariaAttr = axe._audit.standards.ariaAttrs.newAttr;
- assert.equal(ariaAttr.type, 'string');
- });
-
- it('should override existing attr', function() {
- axe.configure({
- standards: {
- ariaAttrs: {
- newAttr: {
- type: 'string'
- }
- }
- }
- });
-
- axe.configure({
- standards: {
- ariaAttrs: {
- newAttr: {
- type: 'mntoken',
- values: ['foo', 'bar']
- }
- }
- }
- });
-
- var ariaAttr = axe._audit.standards.ariaAttrs.newAttr;
- assert.equal(ariaAttr.type, 'mntoken');
- assert.deepEqual(ariaAttr.values, ['foo', 'bar']);
- });
-
- it('should merge existing attr', function() {
- axe.configure({
- standards: {
- ariaAttrs: {
- newAttr: {
- type: 'mntoken',
- values: ['foo', 'bar']
- }
- }
- }
- });
-
- axe.configure({
- standards: {
- ariaAttrs: {
- newAttr: {
- type: 'mntokens'
- }
- }
- }
- });
-
- var ariaAttr = axe._audit.standards.ariaAttrs.newAttr;
- assert.equal(ariaAttr.type, 'mntokens');
- assert.deepEqual(ariaAttr.values, ['foo', 'bar']);
- });
-
- it('should override and not merge array', function() {
- axe.configure({
- standards: {
- ariaAttrs: {
- newAttr: {
- type: 'mntoken',
- values: ['foo', 'bar']
- }
- }
- }
- });
-
- axe.configure({
- standards: {
- ariaAttrs: {
- newAttr: {
- values: ['baz']
- }
- }
- }
- });
-
- var ariaAttr = axe._audit.standards.ariaAttrs.newAttr;
- assert.deepEqual(ariaAttr.values, ['baz']);
- });
- });
- });
+ 'use strict';
+ // var Rule = axe._thisWillBeDeletedDoNotUse.base.Rule;
+ // var Check = axe._thisWillBeDeletedDoNotUse.base.Check;
+ var fixture = document.getElementById('fixture');
+ var axeVersion = axe.version;
+
+ afterEach(function() {
+ fixture.innerHTML = '';
+ axe.version = axeVersion;
+ });
+
+ beforeEach(function() {
+ axe._audit = null;
+ });
+
+ it('should throw if audit is not configured', function() {
+ assert.throws(
+ function() {
+ axe.configure({});
+ },
+ Error,
+ /^No audit configured/
+ );
+ });
+
+ it("should override an audit's reporter - string", function() {
+ axe._load({});
+ assert.isNull(axe._audit.reporter);
+
+ axe.configure({ reporter: 'v1' });
+ assert.equal(axe._audit.reporter, 'v1');
+ });
+
+ it('should not allow setting to an un-registered reporter', function() {
+ axe._load({ reporter: 'v1' });
+ axe.configure({ reporter: 'no-exist-evar-plz' });
+ assert.equal(axe._audit.reporter, 'v1');
+ });
+
+ it('should allow for addition of rules', function() {
+ axe._load({});
+ axe.configure({
+ rules: [
+ {
+ id: 'bob',
+ metadata: {
+ joe: 'joe'
+ }
+ }
+ ]
+ });
+
+ assert.lengthOf(axe._audit.rules, 1);
+ // TODO: this does not work yet thanks to webpack
+ // assert.instanceOf(axe._audit.rules[0], Rule);
+ assert.equal(axe._audit.rules[0].id, 'bob');
+ assert.deepEqual(axe._audit.data.rules.bob.joe, 'joe');
+ });
+
+ it('should throw error if rules property is invalid', function() {
+ assert.throws(function() {
+ axe.configure({ rules: 'hello' }),
+ TypeError,
+ /^Rules property must be an array/;
+ });
+ });
+
+ it('should throw error if rule is invalid', function() {
+ assert.throws(function() {
+ axe.configure({ rules: ['hello'] }),
+ TypeError,
+ /Configured rule "hello" is invalid/;
+ });
+ });
+
+ it('should throw error if rule does not have an id', function() {
+ assert.throws(function() {
+ axe.configure({ rules: [{ foo: 'bar' }] }),
+ TypeError,
+ /Configured rule "{foo:\"bar\"}" is invalid/;
+ });
+ });
+
+ it('should call setBranding when passed options', function() {
+ axe._load({});
+ axe.configure({
+ rules: [
+ {
+ id: 'bob',
+ selector: 'pass'
+ }
+ ],
+ branding: {}
+ });
+ assert.lengthOf(axe._audit.rules, 1);
+ assert.equal(
+ axe._audit.data.rules.bob.helpUrl,
+ 'https://dequeuniversity.com/rules/axe/x.y/bob?application=axeAPI'
+ );
+ axe.configure({
+ branding: {
+ application: 'thing',
+ brand: 'thung'
+ }
+ });
+ assert.equal(
+ axe._audit.data.rules.bob.helpUrl,
+ 'https://dequeuniversity.com/rules/thung/x.y/bob?application=thing'
+ );
+ });
+
+ it('sets branding on newly configured rules', function() {
+ axe._load({});
+ axe.configure({
+ branding: {
+ application: 'thing',
+ brand: 'thung'
+ }
+ });
+ axe.configure({
+ rules: [
+ {
+ id: 'bob',
+ selector: 'pass'
+ }
+ ]
+ });
+
+ assert.equal(
+ axe._audit.data.rules.bob.helpUrl,
+ 'https://dequeuniversity.com/rules/thung/x.y/bob?application=thing'
+ );
+ });
+
+ it('should allow for overwriting of rules', function() {
+ axe._load({
+ data: {
+ rules: {
+ bob: 'not-joe'
+ }
+ },
+ rules: {
+ id: 'bob',
+ selector: 'fail'
+ }
+ });
+ axe.configure({
+ rules: [
+ {
+ id: 'bob',
+ selector: 'pass',
+ metadata: {
+ joe: 'joe'
+ }
+ }
+ ]
+ });
+
+ assert.lengthOf(axe._audit.rules, 1);
+ // assert.instanceOf(axe._audit.rules[0], Rule);
+ assert.equal(axe._audit.rules[0].id, 'bob');
+ assert.equal(axe._audit.rules[0].selector, 'pass');
+ assert.equal(axe._audit.data.rules.bob.joe, 'joe');
+ });
+
+ it('should allow for the addition of checks', function() {
+ axe._load({});
+ axe.configure({
+ checks: [
+ {
+ id: 'bob',
+ options: { value: true },
+ metadata: {
+ joe: 'joe'
+ }
+ }
+ ]
+ });
+
+ // assert.instanceOf(axe._audit.checks.bob, Check);
+ assert.equal(axe._audit.checks.bob.id, 'bob');
+ assert.isTrue(axe._audit.checks.bob.options.value);
+ assert.equal(axe._audit.data.checks.bob.joe, 'joe');
+ });
+
+ it('should throw error if checks property is invalid', function() {
+ assert.throws(function() {
+ axe.configure({ checks: 'hello' }),
+ TypeError,
+ /^Checks property must be an array/;
+ });
+ });
+
+ it('should throw error if check is invalid', function() {
+ assert.throws(function() {
+ axe.configure({ checks: ['hello'] }),
+ TypeError,
+ /Configured check "hello" is invalid/;
+ });
+ });
+
+ it('should throw error if check does not have an id', function() {
+ assert.throws(function() {
+ axe.configure({ checks: [{ foo: 'bar' }] }),
+ TypeError,
+ /Configured check "{foo:\"bar\"}" is invalid/;
+ });
+ });
+
+ it('should allow for the overwriting of checks', function() {
+ axe._load({
+ data: {
+ checks: {
+ bob: 'not-joe'
+ }
+ },
+ checks: [
+ {
+ id: 'bob',
+ options: { value: false }
+ }
+ ]
+ });
+ axe.configure({
+ checks: [
+ {
+ id: 'bob',
+ options: { value: true },
+ metadata: {
+ joe: 'joe'
+ }
+ }
+ ]
+ });
+
+ // assert.instanceOf(axe._audit.checks.bob, Check);
+ assert.equal(axe._audit.checks.bob.id, 'bob');
+ assert.isTrue(axe._audit.checks.bob.options.value);
+ assert.equal(axe._audit.data.checks.bob.joe, 'joe');
+ });
+
+ it('should create an execution context for check messages', function() {
+ axe._load({});
+ axe.configure({
+ checks: [
+ {
+ id: 'bob',
+ metadata: {
+ messages: {
+ pass: "function () { return 'Bob' + ' John';}",
+ fail: 'Bob Pete'
+ }
+ }
+ }
+ ]
+ });
+
+ assert.isFunction(axe._audit.data.checks.bob.messages.pass);
+ assert.isString(axe._audit.data.checks.bob.messages.fail);
+ assert.equal(axe._audit.data.checks.bob.messages.pass(), 'Bob John');
+ assert.equal(axe._audit.data.checks.bob.messages.fail, 'Bob Pete');
+ });
+
+ it('overrides the default value of audit.tagExclude', function() {
+ axe._load({});
+ assert.deepEqual(axe._audit.tagExclude, ['experimental']);
+
+ axe.configure({
+ tagExclude: ['ninjas']
+ });
+ assert.deepEqual(axe._audit.tagExclude, ['ninjas']);
+ });
+
+ it('disables all untouched rules with disableOtherRules', function() {
+ axe._load({
+ rules: [{ id: 'captain-america' }, { id: 'thor' }, { id: 'spider-man' }]
+ });
+ axe.configure({
+ disableOtherRules: true,
+ rules: [{ id: 'captain-america' }, { id: 'black-panther' }]
+ });
+
+ assert.lengthOf(axe._audit.rules, 4);
+ assert.equal(axe._audit.rules[0].id, 'captain-america');
+ assert.equal(axe._audit.rules[0].enabled, true);
+ assert.equal(axe._audit.rules[1].id, 'thor');
+ assert.equal(axe._audit.rules[1].enabled, false);
+ assert.equal(axe._audit.rules[2].id, 'spider-man');
+ assert.equal(axe._audit.rules[2].enabled, false);
+ assert.equal(axe._audit.rules[3].id, 'black-panther');
+ assert.equal(axe._audit.rules[3].enabled, true);
+ });
+
+ it("should allow overriding an audit's noHtml", function() {
+ axe._load({});
+ assert.isFalse(axe._audit.noHtml);
+
+ axe.configure({ noHtml: true });
+ assert.isTrue(axe._audit.noHtml);
+ });
+
+ describe('given a locale object', function() {
+ beforeEach(function() {
+ axe._load({});
+
+ axe.configure({
+ rules: [
+ {
+ id: 'greeting',
+ selector: 'div',
+ excludeHidden: false,
+ tags: ['foo', 'bar'],
+ metadata: {
+ description: 'This is a rule that rules',
+ help: 'ABCDEFGHIKLMNOPQRSTVXYZ'
+ }
+ }
+ ],
+ checks: [
+ {
+ id: 'banana',
+ evaluate: function() {},
+ metadata: {
+ impact: 'srsly serious',
+ messages: {
+ pass: 'yay',
+ fail: 'boo',
+ incomplete: {
+ foo: 'a',
+ bar: 'b',
+ baz: 'c'
+ }
+ }
+ }
+ }
+ ]
+ });
+ });
+
+ it('should update check and rule metadata', function() {
+ axe.configure({
+ locale: {
+ lang: 'lol',
+ rules: {
+ greeting: {
+ description: 'hello',
+ help: 'hi'
+ }
+ },
+ checks: {
+ banana: {
+ pass: 'pizza',
+ fail: 'icecream',
+ incomplete: {
+ foo: 'meat',
+ bar: 'fruit',
+ baz: 'vegetables'
+ }
+ }
+ }
+ }
+ });
+
+ var audit = axe._audit;
+ var localeData = audit.data;
+
+ assert.equal(localeData.rules.greeting.help, 'hi');
+ assert.equal(localeData.rules.greeting.description, 'hello');
+ assert.equal(localeData.checks.banana.messages.pass, 'pizza');
+ assert.equal(localeData.checks.banana.messages.fail, 'icecream');
+ assert.deepEqual(localeData.checks.banana.messages.incomplete, {
+ foo: 'meat',
+ bar: 'fruit',
+ baz: 'vegetables'
+ });
+ });
+
+ it('should merge locales (favoring "new")', function() {
+ axe.configure({
+ locale: {
+ lang: 'lol',
+ rules: { greeting: { description: 'hello' } },
+ checks: {
+ banana: {
+ fail: 'icecream'
+ }
+ }
+ }
+ });
+
+ var audit = axe._audit;
+ var localeData = audit.data;
+
+ assert.equal(localeData.rules.greeting.help, 'ABCDEFGHIKLMNOPQRSTVXYZ');
+ assert.equal(localeData.rules.greeting.description, 'hello');
+ assert.equal(localeData.checks.banana.messages.pass, 'yay');
+ assert.equal(localeData.checks.banana.messages.fail, 'icecream');
+ assert.deepEqual(localeData.checks.banana.messages.incomplete, {
+ foo: 'a',
+ bar: 'b',
+ baz: 'c'
+ });
+ });
+
+ it('sets the lang property', function() {
+ axe.configure({
+ locale: {
+ lang: 'lol',
+ rules: { greeting: { description: 'hello' } },
+ checks: {
+ banana: {
+ fail: 'icecream'
+ }
+ }
+ }
+ });
+
+ assert.equal(axe._audit.lang, 'lol');
+ });
+
+ it('should call doT.compile if a messages uses doT syntax', function() {
+ axe.configure({
+ locale: {
+ lang: 'lol',
+ rules: { greeting: { description: 'hello' } },
+ checks: {
+ banana: {
+ fail: 'icecream {{=it.data.value}}'
+ }
+ }
+ }
+ });
+
+ var audit = axe._audit;
+ var localeData = audit.data;
+
+ assert.isTrue(
+ typeof localeData.checks.banana.messages.fail === 'function'
+ );
+ });
+
+ it('should leave the messages as a string if it does not use doT syntax', function() {
+ axe.configure({
+ locale: {
+ lang: 'lol',
+ rules: { greeting: { description: 'hello' } },
+ checks: {
+ banana: {
+ fail: 'icecream ${data.value}'
+ }
+ }
+ }
+ });
+
+ var audit = axe._audit;
+ var localeData = audit.data;
+
+ assert.isTrue(typeof localeData.checks.banana.messages.fail === 'string');
+ });
+
+ it('should update failure messages', function() {
+ axe._load({
+ data: {
+ failureSummaries: {
+ any: {
+ failureMessage: function() {
+ return 'failed any';
+ }
+ },
+ none: {
+ failureMessage: function() {
+ return 'failed none';
+ }
+ }
+ },
+ incompleteFallbackMessage: function() {
+ return 'failed incomplete';
+ }
+ }
+ });
+
+ axe.configure({
+ locale: {
+ lang: 'lol',
+ failureSummaries: {
+ any: {
+ failureMessage: 'foo'
+ },
+ none: {
+ failureMessage: 'bar'
+ }
+ },
+ incompleteFallbackMessage: 'baz'
+ }
+ });
+
+ var audit = axe._audit;
+ var localeData = audit.data;
+
+ assert.equal(localeData.failureSummaries.any.failureMessage, 'foo');
+ assert.equal(localeData.failureSummaries.none.failureMessage, 'bar');
+ assert.equal(localeData.incompleteFallbackMessage, 'baz');
+ });
+
+ it('should merge failure messages', function() {
+ axe._load({
+ data: {
+ failureSummaries: {
+ any: {
+ failureMessage: function() {
+ return 'failed any';
+ }
+ },
+ none: {
+ failureMessage: function() {
+ return 'failed none';
+ }
+ }
+ },
+ incompleteFallbackMessage: function() {
+ return 'failed incomplete';
+ }
+ }
+ });
+
+ axe.configure({
+ locale: {
+ lang: 'lol',
+ failureSummaries: {
+ any: {
+ failureMessage: 'foo'
+ }
+ }
+ }
+ });
+
+ var audit = axe._audit;
+ var localeData = audit.data;
+
+ assert.equal(localeData.failureSummaries.any.failureMessage, 'foo');
+ assert.equal(
+ localeData.failureSummaries.none.failureMessage(),
+ 'failed none'
+ );
+ assert.equal(localeData.incompleteFallbackMessage(), 'failed incomplete');
+ });
+
+ describe('only given checks', function() {
+ it('should not error', function() {
+ assert.doesNotThrow(function() {
+ axe.configure({
+ locale: {
+ lang: 'lol',
+ checks: {
+ banana: {
+ fail: 'icecream',
+ incomplete: {
+ baz: 'vegetables'
+ }
+ }
+ }
+ }
+ });
+ });
+ });
+ });
+
+ describe('only given rules', function() {
+ it('should not error', function() {
+ assert.doesNotThrow(function() {
+ axe.configure({
+ locale: {
+ rules: { greeting: { help: 'foo', description: 'bar' } }
+ }
+ });
+ });
+ });
+ });
+
+ describe('check incomplete messages', function() {
+ beforeEach(function() {
+ axe.configure({
+ checks: [
+ {
+ id: 'panda',
+ evaluate: function() {},
+ metadata: {
+ impact: 'yep',
+ messages: {
+ pass: 'p',
+ fail: 'f',
+ incomplete: 'i'
+ }
+ }
+ }
+ ]
+ });
+ });
+
+ it('should support strings', function() {
+ axe.configure({
+ locale: {
+ checks: {
+ panda: {
+ incomplete: 'radio'
+ }
+ }
+ }
+ });
+
+ assert.equal(axe._audit.data.checks.panda.messages.incomplete, 'radio');
+ });
+
+ it('should shallow-merge objects', function() {
+ axe.configure({
+ locale: {
+ lang: 'lol',
+ checks: {
+ banana: {
+ incomplete: {
+ baz: 'vegetables'
+ }
+ }
+ }
+ }
+ });
+
+ assert.deepEqual(axe._audit.data.checks.banana.messages.incomplete, {
+ foo: 'a',
+ bar: 'b',
+ baz: 'vegetables'
+ });
+ });
+ });
+
+ // This test ensures we do not drop additional properties added to
+ // checks. See https://github.com/dequelabs/axe-core/pull/1036/files#r207738673
+ // for reasoning.
+ it('should keep existing properties on check data', function() {
+ axe.configure({
+ checks: [
+ {
+ id: 'banana',
+ metadata: {
+ impact: 'potato',
+ foo: 'bar',
+ messages: {
+ pass: 'pass',
+ fail: 'fail',
+ incomplete: 'incomplete'
+ }
+ }
+ }
+ ]
+ });
+
+ axe.configure({
+ locale: {
+ lang: 'lol',
+ checks: {
+ banana: {
+ pass: 'yay banana'
+ }
+ }
+ }
+ });
+
+ var banana = axe._audit.data.checks.banana;
+ assert.equal(banana.impact, 'potato');
+ assert.equal(banana.foo, 'bar');
+ assert.equal(banana.messages.pass, 'yay banana');
+ });
+
+ it('should error when provided an unknown rule id', function() {
+ assert.throws(function() {
+ axe.configure({
+ locale: {
+ rules: { nope: { help: 'helpme' } }
+ }
+ });
+ }, /unknown rule: "nope"/);
+ });
+
+ it('should error when provided an unknown check id', function() {
+ assert.throws(function() {
+ axe.configure({
+ locale: {
+ checks: { nope: { pass: 'helpme' } }
+ }
+ });
+ }, /unknown check: "nope"/);
+ });
+
+ it('should error when provided an unknown failure summary', function() {
+ assert.throws(function() {
+ axe.configure({
+ locale: {
+ failureSummaries: {
+ nope: { failureMessage: 'helpme' }
+ }
+ }
+ });
+ });
+ });
+
+ it('should set default locale', function() {
+ assert.isNull(axe._audit._defaultLocale);
+ axe.configure({
+ locale: {
+ lang: 'lol',
+ checks: {
+ banana: {
+ pass: 'yay banana'
+ }
+ }
+ }
+ });
+ assert.ok(axe._audit._defaultLocale);
+ });
+
+ describe('also given metadata', function() {
+ it('should favor the locale', function() {
+ axe.configure({
+ locale: {
+ lang: 'lol',
+ rules: {
+ greeting: {
+ help: 'hi'
+ }
+ }
+ },
+ rules: [
+ {
+ id: 'greeting',
+ metadata: {
+ help: 'potato'
+ }
+ }
+ ]
+ });
+
+ var audit = axe._audit;
+ var localeData = audit.data;
+
+ assert.equal(localeData.rules.greeting.help, 'hi');
+ });
+ });
+
+ describe('after locale has been set', function() {
+ describe('the provided messages', function() {
+ it('should allow for doT templating', function() {
+ axe.configure({
+ locale: {
+ lang: 'foo',
+ rules: {
+ greeting: {
+ help: 'foo: {{=it.data}}.'
+ }
+ }
+ }
+ });
+
+ var greeting = axe._audit.data.rules.greeting;
+ var value = greeting.help({
+ data: 'bar'
+ });
+ assert.equal(value, 'foo: bar.');
+ });
+ });
+ });
+ });
+
+ describe('given an axeVersion property', function() {
+ beforeEach(function() {
+ axe._load({});
+ axe.version = '1.2.3';
+ });
+
+ it('should not throw if version matches axe.version', function() {
+ assert.doesNotThrow(function fn() {
+ axe.configure({
+ axeVersion: '1.2.3'
+ });
+
+ axe.version = '1.2.3-canary.2664bae';
+ axe.configure({
+ axeVersion: '1.2.3-canary.2664bae'
+ });
+ });
+ });
+
+ it('should not throw if patch version is less than axe.version', function() {
+ assert.doesNotThrow(function fn() {
+ axe.configure({
+ axeVersion: '1.2.0'
+ });
+ });
+ });
+
+ it('should not throw if minor version is less than axe.version', function() {
+ assert.doesNotThrow(function fn() {
+ axe.configure({
+ axeVersion: '1.1.9'
+ });
+ });
+ });
+
+ it('should not throw if versions match and axe has a canary version', function() {
+ axe.version = '1.2.3-canary.2664bae';
+ assert.doesNotThrow(function fn() {
+ axe.configure({
+ axeVersion: '1.2.3'
+ });
+ });
+ });
+
+ it('should throw if invalid version', function() {
+ assert.throws(function fn() {
+ axe.configure({
+ axeVersion: '2'
+ });
+ }, 'Invalid configured version 2');
+
+ assert.throws(function fn() {
+ axe.configure({
+ axeVersion: '2..'
+ });
+ }, 'Invalid configured version 2..');
+ });
+
+ it('should throw if major version is different than axe.version', function() {
+ assert.throws(function fn() {
+ axe.configure(
+ {
+ axeVersion: '2.0.0'
+ },
+ /^Configured version/
+ );
+ });
+ assert.throws(function fn() {
+ axe.configure(
+ {
+ axeVersion: '0.1.2'
+ },
+ /^Configured version/
+ );
+ });
+ });
+
+ it('should throw if minor version is greater than axe.version', function() {
+ assert.throws(function fn() {
+ axe.configure(
+ {
+ axeVersion: '1.3.0'
+ },
+ /^Configured version/
+ );
+ });
+ });
+
+ it('should throw if patch version is greater than axe.version', function() {
+ assert.throws(function fn() {
+ axe.configure(
+ {
+ axeVersion: '1.2.9'
+ },
+ /^Configured version/
+ );
+ });
+ });
+
+ it('should throw if versions match and axeVersion has a canary version', function() {
+ assert.throws(function fn() {
+ axe.configure(
+ {
+ axeVersion: '1.2.3-canary.2664bae'
+ },
+ /^Configured version/
+ );
+ });
+ });
+
+ it('should throw if versions match and both have a canary version', function() {
+ axe.version = '1.2.3-canary.2664bae';
+ assert.throws(function fn() {
+ axe.configure(
+ {
+ axeVersion: '1.2.3-canary.a5d727c'
+ },
+ /^Configured version/
+ );
+ });
+ });
+
+ it('should accept ver property as fallback', function() {
+ assert.throws(function fn() {
+ axe.configure(
+ {
+ ver: '1.3.0'
+ },
+ /^Configured version/
+ );
+ });
+ });
+
+ it('should accept axeVersion over ver property', function() {
+ assert.throws(function fn() {
+ axe.configure(
+ {
+ ver: '0.1.2',
+ axeVersion: '1.3.0'
+ },
+ /^Configured version 1\.3\.0/
+ );
+ });
+ });
+ });
+
+ describe('given a standards object', function() {
+ beforeEach(function() {
+ axe._load({});
+ });
+
+ describe('ariaAttrs', function() {
+ it('should allow creating new attr', function() {
+ axe.configure({
+ standards: {
+ ariaAttrs: {
+ newAttr: {
+ type: 'string'
+ }
+ }
+ }
+ });
+
+ var ariaAttr = axe._audit.standards.ariaAttrs.newAttr;
+ assert.equal(ariaAttr.type, 'string');
+ });
+
+ it('should override existing attr', function() {
+ axe.configure({
+ standards: {
+ ariaAttrs: {
+ newAttr: {
+ type: 'string'
+ }
+ }
+ }
+ });
+
+ axe.configure({
+ standards: {
+ ariaAttrs: {
+ newAttr: {
+ type: 'mntoken',
+ values: ['foo', 'bar']
+ }
+ }
+ }
+ });
+
+ var ariaAttr = axe._audit.standards.ariaAttrs.newAttr;
+ assert.equal(ariaAttr.type, 'mntoken');
+ assert.deepEqual(ariaAttr.values, ['foo', 'bar']);
+ });
+
+ it('should merge existing attr', function() {
+ axe.configure({
+ standards: {
+ ariaAttrs: {
+ newAttr: {
+ type: 'mntoken',
+ values: ['foo', 'bar']
+ }
+ }
+ }
+ });
+
+ axe.configure({
+ standards: {
+ ariaAttrs: {
+ newAttr: {
+ type: 'mntokens'
+ }
+ }
+ }
+ });
+
+ var ariaAttr = axe._audit.standards.ariaAttrs.newAttr;
+ assert.equal(ariaAttr.type, 'mntokens');
+ assert.deepEqual(ariaAttr.values, ['foo', 'bar']);
+ });
+
+ it('should override and not merge array', function() {
+ axe.configure({
+ standards: {
+ ariaAttrs: {
+ newAttr: {
+ type: 'mntoken',
+ values: ['foo', 'bar']
+ }
+ }
+ }
+ });
+
+ axe.configure({
+ standards: {
+ ariaAttrs: {
+ newAttr: {
+ values: ['baz']
+ }
+ }
+ }
+ });
+
+ var ariaAttr = axe._audit.standards.ariaAttrs.newAttr;
+ assert.deepEqual(ariaAttr.values, ['baz']);
+ });
+ });
+ });
});
diff --git a/test/core/utils/dq-element.js b/test/core/utils/dq-element.js
index a3f39ad9a5..fee058d451 100644
--- a/test/core/utils/dq-element.js
+++ b/test/core/utils/dq-element.js
@@ -1,212 +1,230 @@
describe('DqElement', function() {
- 'use strict';
-
- var DqElement = axe.utils.DqElement;
- var fixture = document.getElementById('fixture');
- var fixtureSetup = axe.testUtils.fixtureSetup;
-
- afterEach(function() {
- fixture.innerHTML = '';
- axe._tree = undefined;
- axe._selectorData = undefined;
- });
-
- it('should be a function', function() {
- assert.isFunction(DqElement);
- });
-
- it('should be exposed to utils', function() {
- assert.equal(axe.utils.DqElement, DqElement);
- });
-
- it('should take a node as a parameter and return an object', function() {
- var node = document.createElement('div');
- var result = new DqElement(node);
-
- assert.isObject(result);
- });
- describe('element', function() {
- it('should store reference to the element', function() {
- var div = document.createElement('div');
- var dqEl = new DqElement(div);
- assert.equal(dqEl.element, div);
- });
-
- it('should not be present in stringified version', function() {
- var div = document.createElement('div');
- fixtureSetup();
-
- var dqEl = new DqElement(div);
-
- assert.isUndefined(JSON.parse(JSON.stringify(dqEl)).element);
- });
- });
-
- describe('source', function() {
- it('should include the outerHTML of the element', function() {
- fixture.innerHTML = 'Hello!
';
-
- var result = new DqElement(fixture.firstChild);
- assert.equal(result.source, fixture.firstChild.outerHTML);
- });
-
- it('should work with SVG elements', function() {
- fixture.innerHTML = ' ';
-
- var result = new DqElement(fixture.firstChild);
- assert.isString(result.source);
- });
- it('should work with MathML', function() {
- fixture.innerHTML =
- 'x 2 ';
-
- var result = new DqElement(fixture.firstChild);
- assert.isString(result.source);
- });
-
- it('should truncate large elements', function() {
- var div = '';
- for (var i = 0; i < 300; i++) {
- div += i;
- }
- div += '
';
- fixture.innerHTML = div;
-
- var result = new DqElement(fixture.firstChild);
- assert.equal(result.source.length, ''.length);
- });
-
- it('should use spec object over passed element', function() {
- fixture.innerHTML = '
Hello!
';
- var result = new DqElement(
- fixture.firstChild,
- {},
- {
- source: 'woot'
- }
- );
- assert.equal(result.source, 'woot');
- });
- });
-
- describe('selector', function() {
- it('should prefer selector from spec object', function() {
- fixture.innerHTML = '
Hello!
';
- var result = new DqElement(
- fixture.firstChild,
- {},
- {
- selector: 'woot'
- }
- );
- assert.equal(result.selector, 'woot');
- });
- });
-
- describe('ancestry', function() {
- it('should prefer selector from spec object', function() {
- fixture.innerHTML = '
Hello!
';
- var result = new DqElement(
- fixture.firstChild,
- {},
- {
- ancestry: 'woot'
- }
- );
- assert.equal(result.ancestry, 'woot');
- });
- });
-
- describe('xpath', function() {
- it('should prefer selector from spec object', function() {
- fixture.innerHTML = '
Hello!
';
- var result = new DqElement(
- fixture.firstChild,
- {},
- {
- xpath: 'woot'
- }
- );
- assert.equal(result.xpath, 'woot');
- });
- });
-
- describe('absolutePaths', function() {
- it('creates a path all the way to root', function() {
- fixtureSetup('
Hello!
');
-
- var result = new DqElement(fixture.firstChild, {
- absolutePaths: true
- });
- assert.include(result.selector[0], 'html > ');
- assert.include(result.selector[0], '#fixture > ');
- assert.include(result.selector[0], '#foo');
- });
- });
-
- describe('toJSON', function() {
- it('should only stringify selector and source', function() {
- var expected = {
- selector: 'foo > bar > joe',
- source: '
',
- xpath: '/foo/bar/joe',
- ancestry: 'foo > bar > joe'
- };
- var result = new DqElement('joe', {}, expected);
-
- assert.deepEqual(JSON.stringify(result), JSON.stringify(expected));
- });
- });
-
- describe('fromFrame', function() {
- var dqMain, dqIframe;
- beforeEach(function() {
- var main = document.createElement('main');
- main.id = 'main';
- dqMain = new DqElement(
- main,
- {},
- {
- selector: ['#main'],
- ancestry: ['html > body > main'],
- xpath: ['/main']
- }
- );
-
- var iframe = document.createElement('iframe');
- iframe.id = 'iframe';
- dqIframe = new DqElement(
- iframe,
- {},
- {
- selector: ['#iframe'],
- ancestry: ['html > body > iframe'],
- xpath: ['/iframe']
- }
- );
- });
-
- it('returns a new DqElement', function() {
- assert.instanceOf(DqElement.fromFrame(dqMain, {}, dqIframe), DqElement);
- });
-
- it('sets options for DqElement', function() {
- var options = { absolutePaths: true };
- var dqElm = DqElement.fromFrame(dqMain, options, dqIframe);
- assert.isTrue(dqElm._options.toRoot);
- });
-
- it('merges node and frame selectors', function() {
- var dqElm = DqElement.fromFrame(dqMain, {}, dqIframe);
- assert.deepEqual(dqElm.selector, [
- dqIframe.selector[0],
- dqMain.selector[0]
- ]);
- assert.deepEqual(dqElm.ancestry, [
- dqIframe.ancestry[0],
- dqMain.ancestry[0]
- ]);
- assert.deepEqual(dqElm.xpath, [dqIframe.xpath[0], dqMain.xpath[0]]);
- });
- });
+ 'use strict';
+
+ var DqElement = axe.utils.DqElement;
+ var fixture = document.getElementById('fixture');
+ var fixtureSetup = axe.testUtils.fixtureSetup;
+
+ afterEach(function() {
+ axe.reset();
+ });
+
+ it('should be a function', function() {
+ assert.isFunction(DqElement);
+ });
+
+ it('should be exposed to utils', function() {
+ assert.equal(axe.utils.DqElement, DqElement);
+ });
+
+ it('should take a node as a parameter and return an object', function() {
+ var node = document.createElement('div');
+ var result = new DqElement(node);
+
+ assert.isObject(result);
+ });
+ describe('element', function() {
+ it('should store reference to the element', function() {
+ var div = document.createElement('div');
+ var dqEl = new DqElement(div);
+ assert.equal(dqEl.element, div);
+ });
+
+ it('should not be present in stringified version', function() {
+ var div = document.createElement('div');
+ fixtureSetup();
+
+ var dqEl = new DqElement(div);
+
+ assert.isUndefined(JSON.parse(JSON.stringify(dqEl)).element);
+ });
+ });
+
+ describe('source', function() {
+ it('should include the outerHTML of the element', function() {
+ fixture.innerHTML = 'Hello!
';
+
+ var result = new DqElement(fixture.firstChild);
+ assert.equal(result.source, fixture.firstChild.outerHTML);
+ });
+
+ it('should work with SVG elements', function() {
+ fixture.innerHTML = ' ';
+
+ var result = new DqElement(fixture.firstChild);
+ assert.isString(result.source);
+ });
+ it('should work with MathML', function() {
+ fixture.innerHTML =
+ 'x 2 ';
+
+ var result = new DqElement(fixture.firstChild);
+ assert.isString(result.source);
+ });
+
+ it('should truncate large elements', function() {
+ var div = '';
+ for (var i = 0; i < 300; i++) {
+ div += i;
+ }
+ div += '
';
+ fixture.innerHTML = div;
+
+ var result = new DqElement(fixture.firstChild);
+ assert.equal(result.source.length, ''.length);
+ });
+
+ it('should use spec object over passed element', function() {
+ fixture.innerHTML = '
Hello!
';
+ var result = new DqElement(
+ fixture.firstChild,
+ {},
+ {
+ source: 'woot'
+ }
+ );
+ assert.equal(result.source, 'woot');
+ });
+
+ it('should return null if audit.noHtml is set', function() {
+ axe.configure({ noHtml: true });
+ fixture.innerHTML = '
Hello!
';
+ var result = new DqElement(fixture.firstChild);
+ assert.isNull(result.source);
+ });
+
+ it('should not use spec object over passed element if audit.noHtml is set', function() {
+ axe.configure({ noHtml: true });
+ fixture.innerHTML = '
Hello!
';
+ var result = new DqElement(
+ fixture.firstChild,
+ {},
+ {
+ source: 'woot'
+ }
+ );
+ assert.isNull(result.source);
+ });
+ });
+
+ describe('selector', function() {
+ it('should prefer selector from spec object', function() {
+ fixture.innerHTML = '
Hello!
';
+ var result = new DqElement(
+ fixture.firstChild,
+ {},
+ {
+ selector: 'woot'
+ }
+ );
+ assert.equal(result.selector, 'woot');
+ });
+ });
+
+ describe('ancestry', function() {
+ it('should prefer selector from spec object', function() {
+ fixture.innerHTML = '
Hello!
';
+ var result = new DqElement(
+ fixture.firstChild,
+ {},
+ {
+ ancestry: 'woot'
+ }
+ );
+ assert.equal(result.ancestry, 'woot');
+ });
+ });
+
+ describe('xpath', function() {
+ it('should prefer selector from spec object', function() {
+ fixture.innerHTML = '
Hello!
';
+ var result = new DqElement(
+ fixture.firstChild,
+ {},
+ {
+ xpath: 'woot'
+ }
+ );
+ assert.equal(result.xpath, 'woot');
+ });
+ });
+
+ describe('absolutePaths', function() {
+ it('creates a path all the way to root', function() {
+ fixtureSetup('
Hello!
');
+
+ var result = new DqElement(fixture.firstChild, {
+ absolutePaths: true
+ });
+ assert.include(result.selector[0], 'html > ');
+ assert.include(result.selector[0], '#fixture > ');
+ assert.include(result.selector[0], '#foo');
+ });
+ });
+
+ describe('toJSON', function() {
+ it('should only stringify selector and source', function() {
+ var expected = {
+ selector: 'foo > bar > joe',
+ source: '
',
+ xpath: '/foo/bar/joe',
+ ancestry: 'foo > bar > joe'
+ };
+ var result = new DqElement('joe', {}, expected);
+
+ assert.deepEqual(JSON.stringify(result), JSON.stringify(expected));
+ });
+ });
+
+ describe('fromFrame', function() {
+ var dqMain, dqIframe;
+ beforeEach(function() {
+ var main = document.createElement('main');
+ main.id = 'main';
+ dqMain = new DqElement(
+ main,
+ {},
+ {
+ selector: ['#main'],
+ ancestry: ['html > body > main'],
+ xpath: ['/main']
+ }
+ );
+
+ var iframe = document.createElement('iframe');
+ iframe.id = 'iframe';
+ dqIframe = new DqElement(
+ iframe,
+ {},
+ {
+ selector: ['#iframe'],
+ ancestry: ['html > body > iframe'],
+ xpath: ['/iframe']
+ }
+ );
+ });
+
+ it('returns a new DqElement', function() {
+ assert.instanceOf(DqElement.fromFrame(dqMain, {}, dqIframe), DqElement);
+ });
+
+ it('sets options for DqElement', function() {
+ var options = { absolutePaths: true };
+ var dqElm = DqElement.fromFrame(dqMain, options, dqIframe);
+ assert.isTrue(dqElm._options.toRoot);
+ });
+
+ it('merges node and frame selectors', function() {
+ var dqElm = DqElement.fromFrame(dqMain, {}, dqIframe);
+ assert.deepEqual(dqElm.selector, [
+ dqIframe.selector[0],
+ dqMain.selector[0]
+ ]);
+ assert.deepEqual(dqElm.ancestry, [
+ dqIframe.ancestry[0],
+ dqMain.ancestry[0]
+ ]);
+ assert.deepEqual(dqElm.xpath, [dqIframe.xpath[0], dqMain.xpath[0]]);
+ });
+ });
});
diff --git a/test/integration/full/configure-options/configure-options.js b/test/integration/full/configure-options/configure-options.js
index 44ec4fbdf9..0ab804655a 100644
--- a/test/integration/full/configure-options/configure-options.js
+++ b/test/integration/full/configure-options/configure-options.js
@@ -1,16 +1,18 @@
describe('Configure Options', function() {
'use strict';
- afterEach(function() {
- axe.reset();
- });
+ var target = document.querySelector('#target');
+
+ afterEach(function() {
+ axe.reset();
+ target.innerHTML = '';
+ });
- describe('Check', function() {
- var target = document.querySelector('#target');
- describe('aria-allowed-attr', function() {
- it('should allow an attribute supplied in options', function(done) {
- target.setAttribute('role', 'separator');
- target.setAttribute('aria-valuenow', '0');
+ describe('Check', function() {
+ describe('aria-allowed-attr', function() {
+ it('should allow an attribute supplied in options', function(done) {
+ target.setAttribute('role', 'separator');
+ target.setAttribute('aria-valuenow', '0');
axe.configure({
checks: [
@@ -150,11 +152,122 @@ describe('Configure Options', function() {
assert.lengthOf(results.passes, 1, 'passes');
assert.equal(results.passes[0].id, 'html-has-lang');
- assert.lengthOf(results.violations, 0, 'violations');
- assert.lengthOf(results.incomplete, 0, 'incomplete');
- assert.lengthOf(results.inapplicable, 0, 'inapplicable');
- done();
- });
- });
- });
+ assert.lengthOf(results.violations, 0, 'violations');
+ assert.lengthOf(results.incomplete, 0, 'incomplete');
+ assert.lengthOf(results.inapplicable, 0, 'inapplicable');
+ done();
+ });
+ });
+ });
+
+ describe('noHtml', function() {
+ it('prevents html property on nodes', function(done) {
+ target.setAttribute('role', 'slider');
+ axe.configure({
+ noHtml: true,
+ checks: [
+ {
+ id: 'aria-required-attr',
+ options: { slider: ['aria-snuggles'] }
+ }
+ ]
+ });
+ axe.run(
+ '#target',
+ {
+ runOnly: {
+ type: 'rule',
+ values: ['aria-required-attr']
+ }
+ },
+ function(error, results) {
+ try {
+ assert.isNull(results.violations[0].nodes[0].html);
+ done();
+ } catch (e) {
+ done(e);
+ }
+ }
+ );
+ });
+
+ it('prevents html property on nodes from iframes', function(done) {
+ axe.configure({
+ noHtml: true,
+ rules: [
+ {
+ id: 'div#target',
+ // purposefully don't match so the first result is from
+ // the iframe
+ selector: 'foo'
+ }
+ ]
+ });
+
+ var iframe = document.createElement('iframe');
+ iframe.src = '/test/mock/frames/context.html';
+ iframe.onload = function() {
+ axe.run(
+ '#target',
+ {
+ runOnly: {
+ type: 'rule',
+ values: ['div#target']
+ }
+ },
+ function(error, results) {
+ try {
+ assert.deepEqual(results.passes[0].nodes[0].target, [
+ 'iframe',
+ '#target'
+ ]);
+ assert.isNull(results.passes[0].nodes[0].html);
+ done();
+ } catch (e) {
+ done(e);
+ }
+ }
+ );
+ };
+ target.appendChild(iframe);
+ });
+
+ it('prevents html property in postMesage', function(done) {
+ axe.configure({
+ noHtml: true,
+ rules: [
+ {
+ id: 'div#target',
+ // purposefully don't match so the first result is from
+ // the iframe
+ selector: 'foo'
+ }
+ ]
+ });
+
+ var iframe = document.createElement('iframe');
+ iframe.src = '/test/mock/frames/noHtml-config.html';
+ iframe.onload = function() {
+ axe.run('#target', {
+ runOnly: {
+ type: 'rule',
+ values: ['div#target']
+ }
+ });
+ };
+ target.appendChild(iframe);
+
+ window.addEventListener('message', function(e) {
+ var data = JSON.parse(e.data);
+ if (Array.isArray(data.message)) {
+ try {
+ assert.isNull(data.message[0].nodes[0].node.source);
+ done();
+ } catch (e) {
+ done(e);
+ }
+ }
+ });
+ });
+ });
});
diff --git a/test/mock/frames/noHtml-config.html b/test/mock/frames/noHtml-config.html
new file mode 100644
index 0000000000..b6fe994736
--- /dev/null
+++ b/test/mock/frames/noHtml-config.html
@@ -0,0 +1,52 @@
+
+
+
+ Context Fixture
+
+
+
+
+
+
+
+