Skip to content

Commit

Permalink
feat(autocomplete-valid): allow autocomplete-valid to be run entirely…
Browse files Browse the repository at this point in the history
… off of a virtual node (#1591)

* feat(rule): new api to run rules using only virtual nodes

* only do autocomplete

* add tests for hasAttr
  • Loading branch information
straker authored May 30, 2019
1 parent 940de07 commit b3e0873
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 11 deletions.
8 changes: 4 additions & 4 deletions lib/checks/forms/autocomplete-appropriate.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Select and textarea is always allowed
if (node.nodeName.toUpperCase() !== 'INPUT') {
if (virtualNode.elementNodeName !== 'input') {
return true;
}

Expand Down Expand Up @@ -34,7 +34,7 @@ if (typeof options === 'object') {
});
}

const autocomplete = node.getAttribute('autocomplete');
const autocomplete = virtualNode.attr('autocomplete');
const autocompleteTerms = autocomplete
.split(/\s+/g)
.map(term => term.toLowerCase());
Expand All @@ -55,8 +55,8 @@ const allowedTypes = allowedTypesMap[purposeTerm];
* Reference HTML Spec - https://html.spec.whatwg.org/multipage/input.html#the-input-element to filter allowed values for `type`
* and sanitize (https://html.spec.whatwg.org/multipage/input.html#value-sanitization-algorithm)
*/
let type = node.hasAttribute('type')
? axe.commons.text.sanitize(node.getAttribute('type')).toLowerCase()
let type = virtualNode.hasAttr('type')
? axe.commons.text.sanitize(virtualNode.attr('type')).toLowerCase()
: 'text';
type = axe.utils.validInputTypes().includes(type) ? type : 'text';

Expand Down
2 changes: 1 addition & 1 deletion lib/checks/forms/autocomplete-valid.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
const autocomplete = node.getAttribute('autocomplete') || '';
const autocomplete = virtualNode.attr('autocomplete') || '';
return axe.commons.text.isValidAutocomplete(autocomplete, options);
25 changes: 19 additions & 6 deletions lib/core/base/virtual-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ class VirtualNode {
/**
* Wrap the real node and provide list of the flattened children
*
* @param node {Node} - the node in question
* @param shadowId {String} - the ID of the shadow DOM to which this node belongs
* @param node {Node} the node in question
* @param shadowId {String} the ID of the shadow DOM to which this node belongs
*/
constructor(node, shadowId) {
this.shadowId = shadowId;
Expand All @@ -32,7 +32,7 @@ class VirtualNode {
/**
* Determine if the actualNode has the given class name.
* @see https://j11y.io/jquery/#v=2.0.3&fn=jQuery.fn.hasClass
* @param {String} className - The class to check for.
* @param {String} className The class to check for.
* @return {Boolean} True if the actualNode has the given class, false otherwise.
*/
hasClass(className) {
Expand All @@ -50,8 +50,8 @@ class VirtualNode {

/**
* Get the value of the given attribute name.
* @param {String} attrName - The name of the attribute.
* @returns {String|null} The value of the attribute or null if the attribute does not exist
* @param {String} attrName The name of the attribute.
* @return {String|null} The value of the attribute or null if the attribute does not exist
*/
attr(attrName) {
if (typeof this.actualNode.getAttribute !== 'function') {
Expand All @@ -61,6 +61,19 @@ class VirtualNode {
return this.actualNode.getAttribute(attrName);
}

/**
* Determine if the element has the given attribute.
* @param {String} attrName The name of the attribute
* @return {Bool} True if the element has the attribute, false otherwise.
*/
hasAttr(attrName) {
if (typeof this.actualNode.hasAttribute !== 'function') {
return false;
}

return this.actualNode.hasAttribute(attrName);
}

/**
* Determine if the element is focusable and cache the result.
* @return {Boolean} True if the element is focusable, false otherwise.
Expand All @@ -74,7 +87,7 @@ class VirtualNode {

/**
* Return the list of tabbable elements for this element and cache the result.
* @returns {VirtualNode[]}
* @return {VirtualNode[]}
*/
get tabbableElements() {
if (!this._cache.hasOwnProperty('tabbableElements')) {
Expand Down
32 changes: 32 additions & 0 deletions test/core/base/virtual-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,38 @@ describe('VirtualNode', function() {
});
});

describe('hasAttr', function() {
it('should return true if the element has the attribute', function() {
node.setAttribute('foo', 'bar');
var vNode = new VirtualNode(node);

assert.isTrue(vNode.hasAttr('foo'));
});

it('should return false if the element does not have the attribute', function() {
var vNode = new VirtualNode(node);

assert.isFalse(vNode.hasAttr('foo'));
});

it('should return false for text nodes', function() {
node.textContent = 'hello';
var vNode = new VirtualNode(node.firstChild);

assert.isFalse(vNode.hasAttr('foo'));
});

it('should return false if hasAttribute is not a function', function() {
var node = {
nodeName: 'DIV',
hasAttribute: null
};
var vNode = new VirtualNode(node);

assert.isFalse(vNode.hasAttr('foo'));
});
});

describe('isFocusable', function() {
var commons;

Expand Down

0 comments on commit b3e0873

Please sign in to comment.