Skip to content

Commit

Permalink
Merge branch 'develop' into patch-1
Browse files Browse the repository at this point in the history
* develop:
  feat(reporter): adds the rawEnv reporter which wraps raw and env data (dequelabs#1556)
  chore(i18n): Update Japanese locale (dequelabs#1549)
  fix(utils): make cache global instead of only setup in axe.run (dequelabs#1535)
  fix(aria-required-attr): don't require aria-valuemin/max (dequelabs#1529)
  docs: fixed small errors (dequelabs#1545)
  fix: check if property exists in cache of flattenedTree (dequelabs#1536)
  chore: update standard-version dep (dequelabs#1555)
  • Loading branch information
stephenmathieson committed May 10, 2019
2 parents 7ce3859 + ed15ed3 commit 72dfa47
Show file tree
Hide file tree
Showing 22 changed files with 418 additions and 51 deletions.
6 changes: 3 additions & 3 deletions doc/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ axe.configure({
- `any` - array(optional, default `[]`). This is a list of checks that, if none "pass", will generate a violation.
- `all` - array(optional, default `[]`). This is a list of checks that, if any "fails", will generate a violation.
- `none` - array(optional, default `[]`). This is a list of checks that, if any "pass", will generate a violation.
- `tags` - array(optional, default `[]`). A list if the tags that "classify" the rule. In practice, you must supply some valid tags or the default evaluation will not invoke the rule. The convention is to include the standard (WCAG 2 and/or section 508), the WCAG 2 level, Section 508 paragraph, and the WCAG 2 success criteria. Tags are constructed by converting all letters to lower case, removing spaces and periods and concatinating the result. E.g. WCAG 2 A success criteria 1.1.1 would become ["wcag2a", "wcag111"]
- `tags` - array(optional, default `[]`). A list if the tags that "classify" the rule. In practice, you must supply some valid tags or the default evaluation will not invoke the rule. The convention is to include the standard (WCAG 2 and/or section 508), the WCAG 2 level, Section 508 paragraph, and the WCAG 2 success criteria. Tags are constructed by converting all letters to lower case, removing spaces and periods and concatenating the result. E.g. WCAG 2 A success criteria 1.1.1 would become ["wcag2a", "wcag111"]
- `matches` - string(optional, default `*`). A filtering [CSS selector](./developer-guide.md#supported-css-selectors) that will exclude elements that do not match the CSS selector.
- `disableOtherRules` - Disables all rules not included in the `rules` property.
- `locale` - A locale object to apply (at runtime) to all rules and checks, in the same shape as `/locales/*.json`.
Expand Down Expand Up @@ -493,7 +493,7 @@ The `assets` attribute expects an array of preload(able) constraints to be fetch

| Asset Type | Description |
| :--------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cssom` | This asset type preloads all CSS Stylesheets rulesets specified in the page. The stylessheets can be an external cross-domain resource, a relative stylesheet or an inline style with in the head tag of the document. If the stylesheet is an external cross-domain a network request is made. An object representing the CSS Rules from each stylesheet is made available to the checks evaluate function as `preloadedAssets` at run-time |
| `cssom` | This asset type preloads all CSS Stylesheets rulesets specified in the page. The stylesheets can be an external cross-domain resource, a relative stylesheet or an inline style with in the head tag of the document. If the stylesheet is an external cross-domain a network request is made. An object representing the CSS Rules from each stylesheet is made available to the checks evaluate function as `preloadedAssets` at run-time |

The `timeout` attribute in the object configuration is `optional` and has a fallback default value (10000ms). The `timeout` is essential for any network dependent assets that are preloaded, where-in if a given request takes longer than the specified/ default value, the operation is aborted.

Expand Down Expand Up @@ -745,7 +745,7 @@ The top-level document or shadow DOM document fragment

#### axe.commons.dom.findUp

Recusively walk up the DOM, checking for a node which matches a selector. Warning: this should be used sparingly for performance reasons.
Recursively walk up the DOM, checking for a node which matches a selector. Warning: this should be used sparingly for performance reasons.

##### Synopsis

Expand Down
2 changes: 1 addition & 1 deletion doc/accessibility-supported.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ In order to adhere to the manifesto and at the same time be useful to developers

## Accessibility supported

Boiled-down, accessibility supported means that in order for a technique to be valid, it must work on all viable platforms for all assitive technology that are widely used and freely available (paraphrased).
Boiled-down, accessibility supported means that in order for a technique to be valid, it must work on all viable platforms for all assistive technology that are widely used and freely available (paraphrased).

We currently test the following AT combinations for support

Expand Down
2 changes: 1 addition & 1 deletion doc/code-submission-guidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ changes in the pull request, so the git log stays lean. We particularly want to
You can use git's interactive rebase to manipulate, merge, and rename commits in your local
history. If these steps are followed, a force push shouldn't be necessary.

**Do not force push to develop or master under any circulstances.**
**Do not force push to develop or master under any circumstances.**

To interactively rebase all of your commits on top of the latest in develop, run:

Expand Down
4 changes: 2 additions & 2 deletions doc/developer-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ will make it easier to work with the virtual DOM.

#### Description

Recursvely return an array containing the virtual DOM tree for the node specified, excluding comment nodes
Recursively return an array containing the virtual DOM tree for the node specified, excluding comment nodes
and shadow DOM nodes `<content>` and `<slot>`. This method will return a flattened tree containing both
light and shadow DOM, if applicable.

Expand Down Expand Up @@ -392,7 +392,7 @@ None

#### Returns

An object containg the data, relatedNodes, and a way to reset them.
An object containing the data, relatedNodes, and a way to reset them.

```js
{
Expand Down
4 changes: 2 additions & 2 deletions doc/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,11 @@ The plugin takes this information and sends the same instructions to its impleme

The plugin waits for the commands in the iframes to complete and then executes its instances' action function within the current document.

In the above implementation, the axe promise utility `axe.utils.queue()` is used to coordinate the asynchronous handling of communication accross iframes.
In the above implementation, the axe promise utility `axe.utils.queue()` is used to coordinate the asynchronous handling of communication across iframes.

The command handler callback runs the plugin's run function within each iframe. This essentially operates like a recursive call to the run function for the plugin within each iframe.

Once all the iframes' run functions have been executed, the callback is called. This essentially operates as a recursive "return" up the iframe heirarchy until at the top document, the actual callback function is executed. This can be leveraged to pass data back up the iframe hierarchy back to the caller (but this is a more advanced topic).
Once all the iframes' run functions have been executed, the callback is called. This essentially operates as a recursive "return" up the iframe hierarchy until at the top document, the actual callback function is executed. This can be leveraged to pass data back up the iframe hierarchy back to the caller (but this is a more advanced topic).

#### Basic plugin instance

Expand Down
4 changes: 2 additions & 2 deletions doc/rule-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,11 @@ Axe-core handles shadow DOM and cross-domain iframe rules very well - as long as

The rule callbacks all receive both a `node` and a `virtualNode` argument (in addition to the `options` argument). `node` points to the DOM Node that is to be evaluated, whereas `virtualNode` points to the node in the flattened tree (the hierarchy that shadow DOM creates that is used for parent child relationships across shadow DOM boundaries).

If your rule looks at any hierarchical context (parents or children) then you need to operate on the `virtualNode` for those operations. Calls to `isHidden` and the accessible name calculation calls will all evaluate the hierarchy. The commons and utils functions will all fetch the `virtualNode` from the flattened tree if you use the `node` implementation (and then simply call the virtualNode one) and are there for backwards compatibility. This backwards compatibility comes at a perfomance cost that can be avoided by simply using the `virtualNode` to start with. We will ask you to change this during PR review, so you might as well just start out by using the `virtualNode`.
If your rule looks at any hierarchical context (parents or children) then you need to operate on the `virtualNode` for those operations. Calls to `isHidden` and the accessible name calculation calls will all evaluate the hierarchy. The commons and utils functions will all fetch the `virtualNode` from the flattened tree if you use the `node` implementation (and then simply call the virtualNode one) and are there for backwards compatibility. This backwards compatibility comes at a performance cost that can be avoided by simply using the `virtualNode` to start with. We will ask you to change this during PR review, so you might as well just start out by using the `virtualNode`.

## iframes

Rules that evaluate the structure and/or the number of elements on the entire page (for example the heading nesting rule, or the landmark rules) will need to do this evaluation across iframe boundaries. What this means is that the check function instead of determining pass/fail/incomplete, perfomas a data gathering function. This data is then passed up the iframe hierarchy to the top window where it is passed into the `after` function, which does the evaluation of the gathered data and determines pass/fail.
Rules that evaluate the structure and/or the number of elements on the entire page (for example the heading nesting rule, or the landmark rules) will need to do this evaluation across iframe boundaries. What this means is that the check function instead of determining pass/fail/incomplete, performs a data gathering function. This data is then passed up the iframe hierarchy to the top window where it is passed into the `after` function, which does the evaluation of the gathered data and determines pass/fail.

## Rules of Thumb for Rule Code Reviewers

Expand Down
23 changes: 14 additions & 9 deletions lib/commons/aria/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1682,13 +1682,14 @@ lookupTable.role = {
scrollbar: {
type: 'widget',
attributes: {
required: [
'aria-controls',
'aria-valuenow',
required: ['aria-controls', 'aria-valuenow'],
allowed: [
'aria-valuetext',
'aria-orientation',
'aria-errormessage',
'aria-valuemax',
'aria-valuemin'
],
allowed: ['aria-valuetext', 'aria-orientation', 'aria-errormessage']
]
},
owned: null,
nameFrom: ['author'],
Expand Down Expand Up @@ -1775,9 +1776,11 @@ lookupTable.role = {
'aria-valuetext',
'aria-orientation',
'aria-readonly',
'aria-errormessage'
'aria-errormessage',
'aria-valuemax',
'aria-valuemin'
],
required: ['aria-valuenow', 'aria-valuemax', 'aria-valuemin']
required: ['aria-valuenow']
},
owned: null,
nameFrom: ['author'],
Expand All @@ -1792,9 +1795,11 @@ lookupTable.role = {
'aria-valuetext',
'aria-required',
'aria-readonly',
'aria-errormessage'
'aria-errormessage',
'aria-valuemax',
'aria-valuemin'
],
required: ['aria-valuenow', 'aria-valuemax', 'aria-valuemin']
required: ['aria-valuenow']
},
owned: null,
nameFrom: ['author'],
Expand Down
10 changes: 5 additions & 5 deletions lib/commons/aria/is-accessible-ref.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ const idRefsRegex = /^idrefs?$/;
function cacheIdRefs(node, refAttrs) {
if (node.hasAttribute) {
if (node.nodeName.toUpperCase() === 'LABEL' && node.hasAttribute('for')) {
axe._cache.idRefs[node.getAttribute('for')] = true;
axe._cache.get('idRefs')[node.getAttribute('for')] = true;
}

refAttrs
.filter(attr => node.hasAttribute(attr))
.forEach(attr => {
const attrValue = node.getAttribute(attr);
axe.utils.tokenList(attrValue).forEach(id => {
axe._cache.idRefs[id] = true;
axe._cache.get('idRefs')[id] = true;
});
});
}
Expand All @@ -35,8 +35,8 @@ aria.isAccessibleRef = function isAccessibleRef(node) {

// because axe.commons is not available in axe.utils, we can't do
// this caching when we build up the virtual tree
if (!axe._cache.idRefs) {
axe._cache.idRefs = {};
if (!axe._cache.get('idRefs')) {
axe._cache.set('idRefs', {});
// Get all idref(s) attributes on the lookup table
const refAttrs = Object.keys(aria.lookupTable.attributes).filter(attr => {
const { type } = aria.lookupTable.attributes[attr];
Expand All @@ -46,5 +46,5 @@ aria.isAccessibleRef = function isAccessibleRef(node) {
cacheIdRefs(root, refAttrs);
}

return axe._cache.idRefs[id] === true;
return axe._cache.get('idRefs')[id] === true;
};
6 changes: 3 additions & 3 deletions lib/commons/dom/is-skip-link.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ dom.isSkipLink = function(element) {
}

let firstPageLink;
if (typeof axe._cache.firstPageLink !== 'undefined') {
firstPageLink = axe._cache.firstPageLink;
if (typeof axe._cache.get('firstPageLink') !== 'undefined') {
firstPageLink = axe._cache.get('firstPageLink');
} else {
// define a skip link as any anchor element whose href starts with `#...`
// and which precedes the first anchor element whose href doesn't start
Expand All @@ -29,7 +29,7 @@ dom.isSkipLink = function(element) {
)[0];

// null will signify no first page link
axe._cache.firstPageLink = firstPageLink || null;
axe._cache.set('firstPageLink', firstPageLink || null);
}

// if there are no page links then all all links will need to be
Expand Down
33 changes: 33 additions & 0 deletions lib/core/base/cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
(function() {
'use strict';
let _cache = {};

const cache = {
/**
* Set an item in the cache.
* @param {String} key - Name of the key.
* @param {*} value - Value to store.
*/
set(key, value) {
_cache[key] = value;
},

/**
* Retrieve an item from the cache.
* @param {String} key - Name of the key the value was stored as.
* @returns {*} The item stored
*/
get(key) {
return _cache[key];
},

/**
* Clear the cache.
*/
clear() {
_cache = {};
}
};

axe._cache = cache;
})();
6 changes: 1 addition & 5 deletions lib/core/public/run-rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

// Clean up after resolve / reject
function cleanup() {
axe._cache = undefined;
axe._cache.clear();
axe._tree = undefined;
axe._selectorData = undefined;
}
Expand All @@ -19,10 +19,6 @@ function cleanup() {
function runRules(context, options, resolve, reject) {
'use strict';
try {
axe._cache = {
nodeMap: new WeakMap()
};

context = new Context(context);
axe._tree = context.flatTree;
axe._selectorData = axe.utils.getSelectorData(context.flatTree);
Expand Down
12 changes: 12 additions & 0 deletions lib/core/reporters/raw-env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*global helpers */
axe.addReporter('rawEnv', function(results, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
function rawCallback(raw) {
const env = helpers.getEnvironmentData();
callback({ raw, env });
}
axe.getReporter('raw')(results, options, rawCallback);
});
25 changes: 19 additions & 6 deletions lib/core/utils/flattened-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,21 @@ function virtualDOMfromNode(node, shadowId) {
actualNode: node,
_isHidden: null, // will be populated by axe.utils.isHidden
get isFocusable() {
if (!vNodeCache._isFocusable) {
if (!vNodeCache.hasOwnProperty('_isFocusable')) {
vNodeCache._isFocusable = axe.commons.dom.isFocusable(node);
}
return vNodeCache._isFocusable;
},
get tabbableElements() {
if (!vNodeCache._tabbableElements) {
if (!vNodeCache.hasOwnProperty('_tabbableElements')) {
vNodeCache._tabbableElements = axe.commons.dom.getTabbableElements(
this
);
}
return vNodeCache._tabbableElements;
}
};
axe._cache.nodeMap.set(node, vNode);
axe._cache.get('nodeMap').set(node, vNode);
return vNode;
}

Expand Down Expand Up @@ -78,11 +78,11 @@ function getSlotChildren(node) {
* @param {String} shadowId, optional ID of the shadow DOM that is the closest shadow
* ancestor of the node
*/
axe.utils.getFlattenedTree = function(node, shadowId) {
function flattenTree(node, shadowId) {
// using a closure here and therefore cannot easily refactor toreduce the statements
var retVal, realArray, nodeName;
function reduceShadowDOM(res, child) {
var replacements = axe.utils.getFlattenedTree(child, shadowId);
var replacements = flattenTree(child, shadowId);
if (replacements) {
res = res.concat(replacements);
}
Expand Down Expand Up @@ -144,6 +144,19 @@ axe.utils.getFlattenedTree = function(node, shadowId) {
return undefined;
}
}
}

/**
* Recursvely returns an array of the virtual DOM nodes at this level
* excluding comment nodes and the shadow DOM nodes <content> and <slot>
*
* @param {Node} node the current node
* @param {String} shadowId, optional ID of the shadow DOM that is the closest shadow
* ancestor of the node
*/
axe.utils.getFlattenedTree = function(node, shadowId) {
axe._cache.set('nodeMap', new WeakMap());
return flattenTree(node, shadowId);
};

/**
Expand All @@ -154,5 +167,5 @@ axe.utils.getFlattenedTree = function(node, shadowId) {
*/
axe.utils.getNodeFromTree = function(vNode, node) {
const el = node || vNode;
return axe._cache.nodeMap.get(el);
return axe._cache.get('nodeMap') ? axe._cache.get('nodeMap').get(el) : null;
};
8 changes: 8 additions & 0 deletions locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@
"description": "autocomplete属性が正しく、かつフォームフィールドに対して適切であることを確認します",
"help": "autocomplete属性は正しく使用しなければなりません"
},
"avoid-inline-spacing": {
"description": "style属性で指定されたテキストの間隔は、カスタムスタイルシートにより調整可能であることを確認します",
"help": "インラインのテキスト間隔設定はカスタムスタイルシートによって調整可能でなければなりません"
},
"blink": {
"description": "<blink>要素が使用されていないことを確認します",
"help": "<blink>要素は廃止されており、使用するべきではありません"
Expand Down Expand Up @@ -617,6 +621,10 @@
"pass": "aria-labelledby属性が存在し、スクリーン・リーダーに認識可能な要素を参照しています",
"fail": "aria-labelledby属性が存在しない、存在しない要素を参照している、または空の要素を参照しています"
},
"avoid-inline-spacing": {
"pass": "テキストの間隔に影響する '!important' のついたインラインスタイルは指定されていません",
"fail": "ほとんどのブラウザーで上書きすることはサポートされていないため、インラインスタイル {{=it.data.join(', ')}}, から '!important' を削除します"
},
"button-has-visible-text": {
"pass": "要素にスクリーン・リーダーが認識可能な内部テキストが存在しています",
"fail": "要素にスクリーン・リーダーが認識可能な内部テキストが存在していません"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
"revalidator": "~0.3.1",
"selenium-webdriver": "~3.6.0",
"sri-toolbox": "^0.2.0",
"standard-version": "^5.0.0",
"standard-version": "^6.0.0",
"typescript": "^2.9.2",
"uglify-js": "^3.4.4",
"weakmap-polyfill": "^2.0.0"
Expand Down
6 changes: 1 addition & 5 deletions test/checks/aria/required-attr.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,7 @@ describe('aria-required-attr', function() {
assert.isFalse(
checks['aria-required-attr'].evaluate.call(checkContext, node)
);
assert.deepEqual(checkContext._data, [
'aria-valuenow',
'aria-valuemax',
'aria-valuemin'
]);
assert.deepEqual(checkContext._data, ['aria-valuenow']);
});

it('should return true if there is no role', function() {
Expand Down
Loading

0 comments on commit 72dfa47

Please sign in to comment.