Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shadow DOM docs #471

Merged
merged 15 commits into from
Aug 8, 2017
7 changes: 7 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ That having been said, we prefer:
5. Use of 'use strict'
6. Variables declared at the top of functions

### Shadow DOM

For any proposed changes to rules, checks, commons, or other APIs to be accepted
in axe-core,your code must support Shadow DOM. See [API.md](./doc/API.md) and the
[developer guide](./doc/developer-guide.md) for documentation on the available methods
and test utilities.

### Testing

We expect all code to be 100% covered by tests. We don't have or want code coverage metrics but we will review tests and suggest changes when we think the test(s) do(es) not adequately exercise the code/code changes.
Expand Down
287 changes: 283 additions & 4 deletions doc/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,18 @@
1. [API Name: axe.registerPlugin](#api-name-axeregisterplugin)
1. [API Name: axe.cleanup](#api-name-axecleanup)
1. [API Name: axe.a11yCheck](#api-name-axea11ycheck)
1. [Section 3: Example Reference](#section-3-example-reference)
1. [Virtual DOM Utilities](#virtual-dom-utilities)
1. [API Name: axe.utils.querySelectorAll](#api-name-axeutilsqueryselectorall)
1. [Common Functions](#common-functions)
1. [Section 3: Core APIs](#section-3-core-apis)
1. [API Name: axe.utils.getFlattenedTree](#api-name-axeutilsgetflattenedtree)
1. [API Name: axe.utils.getNodeFromTree](#api-name-axeutilsgetnodefromtree)
1. [Section 4: Test Utilities](#section-4-test-utilities)
1. [Test Util Name: axe.testUtils.MockCheckContext](#test-util-name-axetestutilsmockcheckcontext)
1. [Test Util Name: axe.testUtils.shadowSupport](#test-util-name-axetestutilsshadowsupport)
1. [Test Util Name: axe.testUtils.fixtureSetup](#test-util-name-axetestutilsfixturesetup)
1. [Test Util Name: axe.testUtils.checkSetup](#test-util-name-axetestutilschecksetup)
1. [Section 5: Example Reference](#section-5-example-reference)

## Section 1: Introduction

Expand Down Expand Up @@ -450,7 +461,7 @@ This will either be null or an object which is an instance of Error. If you are

#### Results Object

The callback function passed in as the third parameter of `axe.a11yCheck` runs on the results object. This object has four components – a `passes` array, a `violations` array, an `incomplete` array and an `inapplicable` array.
The callback function passed in as the third parameter of `axe.run` runs on the results object. This object has four components – a `passes` array, a `violations` array, an `incomplete` array and an `inapplicable` array.

The `passes` array keeps track of all the passed tests, along with detailed information on each one. This leads to more efficient testing, especially when used in conjunction with manual testing, as the user can easily find out what tests have already been passed.

Expand Down Expand Up @@ -483,7 +494,7 @@ Each object returned in these arrays have the following properties:
* `helpUrl` - URL that provides more information about the specifics of the violation. Links to a page on the Deque University site.
* `id` - Unique identifier for the rule; [see the list of rules](rule-descriptions.md)
* `impact` - How serious the violation is. Can be one of "minor", "moderate", "serious", or "critical" if the Rule failed or `null` if the check passed
* `tags` - Array of tags that this rule is assigned. These tags can be used in the option structure to select which rules are run ([see `axe.a11yCheck` parameters below for more information](#a11ycheck-parameters)).
* `tags` - Array of tags that this rule is assigned. These tags can be used in the option structure to select which rules are run ([see `axe.run` parameters for more information](#parameters-axerun)).
* `nodes` - Array of all elements the Rule tested
* `html` - Snippet of HTML of the Element
* `impact` - How serious the violation is. Can be one of "minor", "moderate", "serious", or "critical" if the test failed or `null` if the check passed
Expand Down Expand Up @@ -608,6 +619,274 @@ In axe-core v1 the main method for axe was `axe.a11yCheck()`. This method was re
- .a11yCheck requires a context object, and so will not fall back to the document root.
- .a11yCheck does not return a Promise.

## Section 3: Example Reference
### Virtual DOM Utilities

Note: If you’re writing rules or checks, you’ll have both the `node` and `virtualNode` passed in.
But if you need to query the flattened tree, the documented function below should help. See the
[developer guide](./developer-guide.md) for more information.

#### API Name: axe.utils.querySelectorAll

##### Description

A querySelectorAll implementation that works on the virtual DOM and Shadow DOM by manually walking the flattened tree instead of relying on DOM API methods which don’t step into Shadow DOM.

Note: while there is no `axe.utils.querySelector` method, you can reproduce that behavior by accessing the first item returned in the array.

##### Synopsis

```javascript
axe.utils.querySelectorAll(virtualNode, 'a[href]');
```

##### Parameters

* `virtualNode` – object, the flattened DOM tree to query against. `axe._tree` is available for this purpose during an audit; see below.
* `selector` – string, the CSS selector to use as a filter. For the most part, this should work seamlessly with `document.querySelectorAll`.

##### Returns

An Array of filtered HTML nodes.


### Common Functions

#### axe.commons.dom.getComposedParent

Get an element's parent in the flattened tree

##### Synopsis

```javascript
axe.commons.dom.getComposedParent(node)
```

##### Parameters
* `element` – HTMLElement. The element for which you want to find a parent

##### Returns

A DOMNode for the parent


#### axe.commons.dom.getRootNode

Return the document or document fragment (shadow DOM)

##### Synopsis

```javascript
axe.commons.dom.getRootNode(node)
```

##### Parameters
* `element` – HTMLElement. The element for which you want to find the root node

##### Returns

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.

##### Synopsis

```javascript
axe.commons.dom.findUp(node, '.selector')
```

##### Parameters
* `element` – HTMLElement. The starting element
* `selector` – String. The target selector for the HTMLElement

##### Returns

Either the matching HTMLElement or `null` if there was no match.

## Section 3: Core APIs

As mentioned above, you shouldn’t need the Shadow DOM APIs below unless you’re working on the axe-core
engine: rules and checks already have `virtualNode` objects passed in.

### API Name: axe.utils.getFlattenedTree

#### Description

Recursvely 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.

#### Synopsis

```javascript
var element = document.body;
axe.utils.getFlattenedTree(element, shadowId)
```

#### Parameters
- `node` – HTMLElement. The current HTML node for which you want a flattened DOM tree.
- `shadowId` – string(optional). ID of the shadow DOM that is the closest shadow ancestor of the node
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A caller should always call this with the shadowID undefined as this parameter is intended only to be used by the recursive calls

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not quite following. Can't we set it as undefined if it doesn't get passed in? This may also be moot if we drop documentation of this function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nbd - I was just suggesting adding a note to say that it is not intended to be directly used by an external caller


#### Returns

An array of objects, where each object is a virtualNode:

```javascript
[{
actualNode: body,
children: [virtualNodes],
shadowId: undefined
}]
```

### API Name: axe.utils.getNodeFromTree

#### Description

Recursively return a single node from a virtual DOM tree. This is commonly used in rules and checks where the node is readily available without querying the DOM.

#### Synopsis

```javascript
axe.utils.getNodeFromTree(axe._tree[0], node);
```

#### Parameters

- `vNode` – object. The flattened DOM tree to fetch a virtual node from
- `node` – HTMLElement. The HTML DOM node for which you need a virtual representation

#### Returns

A virtualNode object:

```javascript
{
actualNode: div,
children: [virtualNodes],
shadowId: undefined
}
```

## Section 4: Test Utilities

All tests must support Shadow DOM, so we created some test utilities to make this easier.

### Test Util Name: MockCheckContext

Create a check context for mocking and resetting data and relatedNodes in tests.

#### Synopsis

```javascript
describe('region', function () {
var fixture = document.getElementById('fixture');

var checkContext = new axe.testUtils.MockCheckContext();

afterEach(function () {
fixture.innerHTML = '';
checkContext.reset();
});

it('should return true when all content is inside the region', function () {
assert.isTrue(checks.region.evaluate.apply(checkContext, checkArgs));
assert.equal(checkContext._relatedNodes.length, 0);
});
});
```

#### Parameters

None

#### Returns

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

```javascript
{
data: (){},
relatedNodes: (){},
reset: (){}
}
```

### Test Util Name: shadowSupport

Provide an API for determining Shadow DOM v0 and v1 support in tests. PhantomJS doesn't have Shadow DOM support, while some browsers do.

#### Synopsis

```javascript
(axe.testUtils.shadowSupport.v1 ? it : xit)('should test Shadow tree content', function () {
// The rest of the shadow DOM test
});
```

#### Parameters

None

#### Returns

An object containing booleans for the following Shadow DOM supports: `v0`, `v1`, or `undefined`.

### Test Util Name: fixtureSetup

Method for injecting content into a fixture and caching the flattened DOM tree (light and Shadow DOM together).

#### Synopsis

```javascript
it('should return true if there is only one ' + type + ' element with the same name', function () {
axe.testUtils.fixtureSetup('<input type="' + type + '" id="target" name="uniqueyname">' +
'<input type="' + type + '" name="differentname">');

var node = fixture.querySelector('#target');
assert.isTrue(check.evaluate.call(checkContext, node));
});
```

#### Parameters

* `content` – Node|String. Stuff to go into the fixture (html or DOM node)

#### Returns

An HTML Element for the fixture

### Test Util Name: checkSetup

Create check arguments.

#### Synopsis

```javascript
it('should return true when all content is inside the region', function () {
var checkArgs = checkSetup('<div id="target"><div role="main"><a href="a.html#mainheader">Click Here</a><div><h1 id="mainheader" tabindex="0">Introduction</h1></div></div></div>');

assert.isTrue(checks.region.evaluate.apply(checkContext, checkArgs));
assert.equal(checkContext._relatedNodes.length, 0);
});
```

#### Parameters

* `content` – String|Node. Stuff to go into the fixture (html or node)
* `options` – Object. Options argument for the check (optional, default: {})
* `target` – String. Target for the check, CSS selector (default: '#target')

### Returns

An array with the DOM Node, options and virtualNode

```javascript
[node, options, virtualNode]
```

## Section 5: Example Reference

This package contains examples for [jasmine](examples/jasmine), [mocha](examples/mocha), [phantomjs](examples/phantomjs), [qunit](examples/qunit), [selenium using javascript](examples/selenium), and [generating HTML from the violations array](examples/html-handlebars.md). Each of these examples is in the [doc/examples](examples) folder. In each folder, there is a README.md file which contains specific information about each example.
Loading