Skip to content

Commit

Permalink
feat(audit): allow runOnly option to accept an array of rules (#1889)
Browse files Browse the repository at this point in the history
* feat(audit): allow runOnly option to accept an array of rules

* remove dup

* fix axe.d.ts
  • Loading branch information
straker committed Nov 12, 2019
1 parent b68aa19 commit 38d6a3f
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 12 deletions.
2 changes: 1 addition & 1 deletion axe.d.ts
Expand Up @@ -41,7 +41,7 @@ declare namespace axe {
values: TagValue[] | string[];
}
interface RunOptions {
runOnly?: RunOnly;
runOnly?: RunOnly | TagValue[] | string[];
rules?: Object;
iframes?: boolean;
elementRef?: boolean;
Expand Down
10 changes: 10 additions & 0 deletions doc/API.md
Expand Up @@ -475,6 +475,16 @@ axe.run(

This example will only run the rules with the id of `ruleId1`, `ruleId2`, and `ruleId3`. No other rule will run.

Alternatively, runOnly can be passed an array of rules:

```js
axe.run({
runOnly: ['ruleId1', 'ruleId2', 'ruleId3'];
}, (err, results) => {
// ...
})
```

3. Run all enabled Rules except for a list of rules

The default operation for axe.run is to run all rules except for rules with the "experimental" tag. If certain rules should be disabled from being run, specify `options` as:
Expand Down
42 changes: 31 additions & 11 deletions lib/core/base/audit.js
Expand Up @@ -629,13 +629,37 @@ Audit.prototype.normalizeOptions = function(options) {
'use strict';
var audit = this;

const tags = [];
const ruleIds = [];
audit.rules.forEach(rule => {
ruleIds.push(rule.id);
rule.tags.forEach(tag => {
if (!tags.includes(tag)) {
tags.push(tag);
}
});
});

// Validate runOnly
if (typeof options.runOnly === 'object') {
if (Array.isArray(options.runOnly)) {
options.runOnly = {
type: 'tag',
values: options.runOnly
};
const hasTag = options.runOnly.find(value => tags.includes(value));
const hasRule = options.runOnly.find(value => ruleIds.includes(value));

if (hasTag && hasRule) {
throw new Error('runOnly cannot be both rules and tags');
}
if (hasRule) {
options.runOnly = {
type: 'rule',
values: options.runOnly
};
} else {
options.runOnly = {
type: 'tag',
values: options.runOnly
};
}
}
const only = options.runOnly;
if (only.value && !only.values) {
Expand All @@ -651,19 +675,15 @@ Audit.prototype.normalizeOptions = function(options) {
if (['rule', 'rules'].includes(only.type)) {
only.type = 'rule';
only.values.forEach(function(ruleId) {
if (!audit.getRule(ruleId)) {
if (!ruleIds.includes(ruleId)) {
throw new Error('unknown rule `' + ruleId + '` in options.runOnly');
}
});

// Validate 'tags' (e.g. anything not 'rule')
} else if (['tag', 'tags', undefined].includes(only.type)) {
only.type = 'tag';
const unmatchedTags = audit.rules.reduce((unmatchedTags, rule) => {
return unmatchedTags.length
? unmatchedTags.filter(tag => !rule.tags.includes(tag))
: unmatchedTags;
}, only.values);
const unmatchedTags = only.values.filter(tag => !tags.includes(tag));

if (unmatchedTags.length !== 0) {
axe.log('Could not find tags `' + unmatchedTags.join('`, `') + '`');
Expand All @@ -675,7 +695,7 @@ Audit.prototype.normalizeOptions = function(options) {

if (typeof options.rules === 'object') {
Object.keys(options.rules).forEach(function(ruleId) {
if (!audit.getRule(ruleId)) {
if (!ruleIds.includes(ruleId)) {
throw new Error('unknown rule `' + ruleId + '` in options.rules');
}
});
Expand Down
22 changes: 22 additions & 0 deletions test/core/base/audit.js
Expand Up @@ -1265,6 +1265,28 @@ describe('Audit', function() {
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({
Expand Down
18 changes: 18 additions & 0 deletions typings/axe-core/axe-core-tests.ts
Expand Up @@ -69,6 +69,24 @@ axe.run(
console.log(error || results);
}
);
axe.run(
context,
{
runOnly: ['wcag2a', 'wcag2aa']
},
(error: Error, results: axe.AxeResults) => {
console.log(error || results);
}
);
axe.run(
context,
{
runOnly: ['color-contrast', 'heading-order']
},
(error: Error, results: axe.AxeResults) => {
console.log(error || results);
}
);

var someRulesConfig = {
rules: {
Expand Down

0 comments on commit 38d6a3f

Please sign in to comment.