Skip to content

Commit

Permalink
feat(object-alt,accessible-text): object-alt rule and accessible text…
Browse files Browse the repository at this point in the history
… to work with serial virtual nodes with children

* feat(object-alt,accessible-text,native-element-type): object-alt rule and accessible text to work with serial virtual nodes with children. deprecate native-element-type

* remove #text from html-elm

* nodeValue

* fix tests

* fix test

* remove file

* rename
  • Loading branch information
straker authored Jul 17, 2020
1 parent 0b1b0ef commit e8e17e4
Show file tree
Hide file tree
Showing 19 changed files with 335 additions and 98 deletions.
20 changes: 10 additions & 10 deletions lib/commons/aria/get-owned-virtual.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@ import idrefs from '../dom/idrefs';
* @param {VirtualNode} element
* @return {VirtualNode[]} Owned elements
*/
function getOwnedVirtual({ actualNode, children }) {
if (!actualNode || !children) {
function getOwnedVirtual(virtualNode) {
const { actualNode, children } = virtualNode;
if (!children) {
throw new Error('getOwnedVirtual requires a virtual node');
}
// TODO: Check that the element has a role
// TODO: Descend into children with role=presentation|none
// TODO: Exclude descendents owned by other elements
if (virtualNode.hasAttr('aria-owns')) {
const owns = idrefs(actualNode, 'aria-owns')
.filter(element => !!element)
.map(element => axe.utils.getNodeFromTree(element));
return [...children, ...owns];
}

return idrefs(actualNode, 'aria-owns').reduce((ownedElms, element) => {
if (element) {
// TODO: es-module-utils.getNodeFromTree
const virtualNode = axe.utils.getNodeFromTree(element);
ownedElms.push(virtualNode);
}
return ownedElms;
}, children);
return [...children];
}

export default getOwnedVirtual;
5 changes: 1 addition & 4 deletions lib/commons/aria/lookup-table.js
Original file line number Diff line number Diff line change
Expand Up @@ -2178,10 +2178,7 @@ lookupTable.elementsAllowedNoRole = [
{
nodeName: 'select',
condition: vNode => {
// TODO: this is a ridiculous hack since webpack is making these two
// separate functions
// TODO: es-module-AbstractVirtualNode
if (!axe._isAbstractNode(vNode)) {
if (!(vNode instanceof axe.AbstractVirtualNode)) {
vNode = axe.utils.getNodeFromTree(vNode);
}

Expand Down
5 changes: 5 additions & 0 deletions lib/commons/standards/get-element-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import matchesFn from '../../commons/matches';
function getElementSpec(vNode) {
const standard = standards.htmlElms[vNode.props.nodeName];

// invalid element name (could be an svg or custom element name)
if (!standard) {
return {};
}

if (!standard.variant) {
return standard;
}
Expand Down
21 changes: 15 additions & 6 deletions lib/commons/text/accessible-text-virtual.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function accessibleTextVirtual(virtualNode, context = {}) {
nativeTextAlternative, // Step 2D
formControlValue, // Step 2E
subtreeText, // Step 2F + Step 2H
textNodeContent, // Step 2G (order with 2H does not matter)
textNodeValue, // Step 2G (order with 2H does not matter)
titleText // Step 2I
];

Expand All @@ -56,15 +56,15 @@ function accessibleTextVirtual(virtualNode, context = {}) {
}

/**
* Return the textContent of a node
* Return the nodeValue of a node
* @param {VirtualNode} element
* @return {String} textContent value
* @return {String} nodeValue value
*/
function textNodeContent({ actualNode }) {
if (actualNode.nodeType !== 3) {
function textNodeValue(virtualNode) {
if (virtualNode.props.nodeType !== 3) {
return '';
}
return actualNode.textContent;
return virtualNode.props.nodeValue;
}

/**
Expand All @@ -75,6 +75,10 @@ function textNodeContent({ actualNode }) {
* @return {Boolean}
*/
function shouldIgnoreHidden({ actualNode }, context) {
if (!actualNode) {
return false;
}

if (
// If the parent isn't ignored, the text node should not be either
actualNode.nodeType !== 1 ||
Expand All @@ -98,6 +102,11 @@ function prepareContext(virtualNode, context) {
if (!context.startNode) {
context = { startNode: virtualNode, ...context };
}

if (!actualNode) {
return context;
}

/**
* When `aria-labelledby` directly references a `hidden` element
* the element needs to be included in the accessible name.
Expand Down
2 changes: 1 addition & 1 deletion lib/commons/text/form-control-value.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const formControlValueMethods = {
function formControlValue(virtualNode, context = {}) {
const { actualNode } = virtualNode;
const unsupportedRoles = unsupported.accessibleNameFromFieldValue || [];
const role = getRole(actualNode);
const role = getRole(virtualNode);

if (
// For the targeted node, the accessible name is never the value:
Expand Down
13 changes: 4 additions & 9 deletions lib/commons/text/native-text-alternative.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import getRole from '../aria/get-role';
import matches from '../matches/matches';
import nativeElementType from './native-element-type';
import getElementSpec from '../standards/get-element-spec';
import nativeTextMethods from './native-text-methods';

/**
Expand All @@ -13,7 +12,7 @@ import nativeTextMethods from './native-text-methods';
function nativeTextAlternative(virtualNode, context = {}) {
const { actualNode } = virtualNode;
if (
actualNode.nodeType !== 1 ||
virtualNode.props.nodeType !== 1 ||
['presentation', 'none'].includes(getRole(virtualNode))
) {
return '';
Expand All @@ -38,12 +37,8 @@ function nativeTextAlternative(virtualNode, context = {}) {
* @return {Function[]} Array of native accessible name computation methods
*/
function findTextMethods(virtualNode) {
const nativeType = nativeElementType.find(type => {
return matches(virtualNode, type.matches);
});

// Use concat because namingMethods can be a string or an array of strings
const methods = nativeType ? [].concat(nativeType.namingMethods) : [];
const elmSpec = getElementSpec(virtualNode);
const methods = elmSpec.namingMethods || [];

return methods.map(methodName => nativeTextMethods[methodName]);
}
Expand Down
82 changes: 41 additions & 41 deletions lib/commons/text/subtree-text.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,50 +29,50 @@ function subtreeText(virtualNode, context = {}) {
// TODO: Could do with an "HTML" lookup table, similar to ARIA,
// where this sort of stuff can live.
const phrasingElements = [
'A',
'EM',
'STRONG',
'SMALL',
'MARK',
'ABBR',
'DFN',
'I',
'B',
'S',
'U',
'CODE',
'VAR',
'SAMP',
'KBD',
'SUP',
'SUB',
'Q',
'CITE',
'SPAN',
'BDO',
'BDI',
'WBR',
'INS',
'DEL',
'MAP',
'AREA',
'NOSCRIPT',
'RUBY',
'BUTTON',
'LABEL',
'OUTPUT',
'DATALIST',
'KEYGEN',
'PROGRESS',
'COMMAND',
'CANVAS',
'TIME',
'METER',
'#TEXT'
'#text',
'a',
'abbr',
'area',
'b',
'bdi',
'bdo',
'button',
'canvas',
'cite',
'code',
'command',
'datalist',
'del',
'dfn',
'em',
'i',
'ins',
'kbd',
'keygen',
'label',
'map',
'mark',
'meter',
'noscript',
'output',
'progress',
'q',
'ruby',
's',
'samp',
'small',
'span',
'strong',
'sub',
'sup',
'time',
'u',
'var',
'wbr'
];

function appendAccessibleText(contentText, virtualNode, context) {
const nodeName = virtualNode.actualNode.nodeName.toUpperCase();
const nodeName = virtualNode.props.nodeName;
let contentTextAdd = accessibleTextVirtual(virtualNode, context);
if (!contentTextAdd) {
return contentText;
Expand Down
1 change: 0 additions & 1 deletion lib/core/base/virtual-node/abstract-virtual-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ const whitespaceRegex = /[\t\r\n\f]/g;

class AbstractVirtualNode {
constructor() {
this.children = [];
this.parent = null;
}

Expand Down
4 changes: 2 additions & 2 deletions lib/core/base/virtual-node/serial-virtual-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ class SerialVirtualNode extends AbstractVirtualNode {
function normaliseProps(serialNode) {
let { nodeName, nodeType = 1 } = serialNode;
assert(
nodeType === 1,
`nodeType has to be undefined or 1, got '${nodeType}'`
typeof nodeType === 'number',
`nodeType has to be a number, got '${nodeType}'`
);
assert(
typeof nodeName === 'string',
Expand Down
5 changes: 3 additions & 2 deletions lib/core/base/virtual-node/virtual-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,15 @@ class VirtualNode extends AbstractVirtualNode {
// abstract Node properties so we can run axe in DOM-less environments.
// add to the prototype so memory is shared across all virtual nodes
get props() {
const { nodeType, nodeName, id, multiple } = this.actualNode;
const { nodeType, nodeName, id, multiple, nodeValue } = this.actualNode;

return {
nodeType,
nodeName: this._isXHTML ? nodeName : nodeName.toLowerCase(),
id,
type: this._type,
multiple
multiple,
nodeValue
};
}

Expand Down
5 changes: 1 addition & 4 deletions lib/core/public/run-virtual-rule.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ function runVirtualRule(ruleId, vNode, options = {}) {
options.reporter = options.reporter || axe._audit.reporter || 'v1';
axe._selectorData = {};

// TODO: this is a ridiculous hack since webpack is making these two
// separate functions
// TODO: es-module-AbstractVirtualNode
if (!axe._isAbstractNode(vNode)) {
if (!(vNode instanceof axe.AbstractVirtualNode)) {
vNode = new axe.SerialVirtualNode(vNode);
}

Expand Down
19 changes: 9 additions & 10 deletions lib/standards/html-elms.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ const htmlElms = {
'tab'
],
// 5.4 button Element
namingMethods: 'subtreeText'
namingMethods: ['subtreeText']
},
canvas: {
allowedRoles: true,
Expand Down Expand Up @@ -220,7 +220,7 @@ const htmlElms = {
contentTypes: ['flow'],
allowedRoles: ['none', 'presentation', 'radiogroup'],
// 5.5 fieldset and legend Elements
namingMethods: 'fieldsetLegendText'
namingMethods: ['fieldsetLegendText']
},
figcaption: {
allowedRoles: ['group', 'none', 'presentation']
Expand Down Expand Up @@ -359,7 +359,7 @@ const htmlElms = {
}
},
// 5.10 img Element
namingMethods: 'altText'
namingMethods: ['altText']
},
input: {
variant: {
Expand Down Expand Up @@ -504,15 +504,15 @@ const htmlElms = {
},
// 5.1 input type="text", input type="password", input type="search", input type="tel", input type="url" and textarea Element
// 5.7 Other Form Elements
namingMethods: 'labelText'
namingMethods: ['labelText']
}
}
},
ins: {
contentTypes: ['phrasing', 'flow'],
allowedRoles: true
},
kdb: {
kbd: {
contentTypes: ['phrasing', 'flow'],
allowedRoles: true
},
Expand Down Expand Up @@ -647,10 +647,10 @@ const htmlElms = {
contentTypes: ['phrasing', 'flow'],
allowedRoles: true,
// 5.6 output Element
namingMethods: 'subtreeText'
namingMethods: ['subtreeText']
},
p: {
contentTypes: ['phrasing', 'flow'],
contentTypes: ['flow'],
allowedRoles: true,
shadowRoot: true
},
Expand Down Expand Up @@ -811,10 +811,9 @@ const htmlElms = {
allowedRoles: true
},
summary: {
contentTypes: ['phrasing', 'flow'],
allowedRoles: false,
// 5.8 summary Element
namingMethods: 'subtreeText'
namingMethods: ['subtreeText']
},
sup: {
contentTypes: ['phrasing', 'flow'],
Expand All @@ -841,7 +840,7 @@ const htmlElms = {
'aria-valuenow': '',
'aria-multiline': 'true'
},
namingMethods: 'labelText'
namingMethods: ['labelText']
},
tfoot: {
allowedRoles: true
Expand Down
4 changes: 2 additions & 2 deletions test/checks/aria/no-implicit-explicit-label.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('no-implicit-explicit-label', function() {
});

describe('SerialVirtualNode', function() {
it('should return undefined', function() {
it('should return false', function() {
var serialNode = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
Expand All @@ -54,7 +54,7 @@ describe('no-implicit-explicit-label', function() {
});

var actual = check.evaluate.call(checkContext, null, {}, serialNode);
assert.isUndefined(actual);
assert.isFalse(actual);
});
});
});
Loading

0 comments on commit e8e17e4

Please sign in to comment.