-
Notifications
You must be signed in to change notification settings - Fork 779
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2725781
commit 82d01b0
Showing
2 changed files
with
253 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
// eslint-disable-next-line no-unused-vars | ||
class SerialVirtualNode extends axe.AbstractVirtualNode { | ||
/** | ||
* Convert a serialised node into a VirtualNode object | ||
* @param {Object} node Serialised node | ||
*/ | ||
constructor (serialNode) { | ||
super() | ||
this._props = normaliseProps(serialNode) | ||
this._attrs = normaliseAttrs(serialNode) | ||
} | ||
|
||
// Accessof for DOM node properties | ||
get props () { | ||
return this._props | ||
} | ||
|
||
/** | ||
* Get the value of the given attribute name. | ||
* @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) { | ||
return this._attrs[attrName] || null | ||
} | ||
|
||
/** | ||
* Determine if the element has the given attribute. | ||
* @param {String} attrName The name of the attribute | ||
* @return {Boolean} True if the element has the attribute, false otherwise. | ||
*/ | ||
hasAttr(attrName) { | ||
return this._attrs[attrName] !== undefined; | ||
} | ||
} | ||
|
||
/** | ||
* Convert between serialised props and DOM-like properties | ||
* @param {Object} serialNode | ||
* @return {Object} normalProperties | ||
*/ | ||
function normaliseProps(serialNode) { | ||
let { nodeName, nodeType = 1 } = serialNode | ||
axe.utils.assert(nodeType !== undefined || nodeType !== 1, | ||
`SerialVirtualNode expects nodeType of undefined or 1, got '${nodeType}'`) | ||
axe.utils.assert(typeof nodeName === 'string', | ||
`SerialVirtualNode expects nodeName to be a string, got '${nodeName}'`) | ||
|
||
const props = { | ||
...serialNode, | ||
nodeType, | ||
nodeName: nodeName.toLowerCase() | ||
} | ||
delete props.attributes | ||
return Object.freeze(props) | ||
} | ||
|
||
/** | ||
* Convert between serialised attributes and DOM-like attributes | ||
* @param {Object} serialNode | ||
* @return {Object} normalAttributes | ||
*/ | ||
function normaliseAttrs({ attributes = {} }) { | ||
const attrMap = { | ||
'htmlFor': 'for', | ||
'className': 'class' | ||
} | ||
|
||
return Object.keys(attributes).reduce((attrs, attrName) => { | ||
const value = attributes[attrName]; | ||
axe.utils.assert(typeof value !== 'object' || value === null, | ||
`SerialVirtualNode expects attributes not to be an object, '${attrName}' was`) | ||
|
||
if (value !== undefined) { | ||
const mappedName = attrMap[attrName] || attrName | ||
attrs[mappedName] = value !== null ? String(value) : null | ||
} | ||
return attrs | ||
}, {}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
/*global axe, SerialVirtualNode */ | ||
describe('SerialVirtualNode', function() { | ||
it('extends AbstractVirtualNode', function () { | ||
var vNode = new SerialVirtualNode({ | ||
nodeName: 'div' | ||
}); | ||
assert.instanceOf(vNode, axe.AbstractVirtualNode); | ||
}); | ||
|
||
describe('props', function () { | ||
it('assigns any properties to .props', function () { | ||
var props = { | ||
nodeType: 1, | ||
nodeName: 'div', | ||
someType: 'bar', | ||
somethingElse: 'baz' | ||
} | ||
var vNode = new SerialVirtualNode(props); | ||
assert.deepEqual(vNode.props, props); | ||
}) | ||
|
||
it('returns a frozen object', function () { | ||
var vNode = new SerialVirtualNode({ nodeName: 'div' }); | ||
assert.isTrue(Object.isFrozen(vNode.props), 'Expect object to be frozen'); | ||
}) | ||
|
||
it('has a default nodeType of 1', function () { | ||
var vNode = new SerialVirtualNode({ nodeName: 'div' }); | ||
assert.equal(vNode.props.nodeType, 1) | ||
}) | ||
|
||
it('converts nodeNames to lower case', function () { | ||
var htmlNodes = [ | ||
'DIV', | ||
'SPAN', | ||
'INPUT', | ||
'HeAdEr', | ||
'TABLE', | ||
'TITLE', | ||
'BUTTON', | ||
'Foo' | ||
] | ||
htmlNodes.forEach(function (nodeName) { | ||
var vNode = new SerialVirtualNode({ nodeName: nodeName }); | ||
assert.equal(vNode.props.nodeName, nodeName.toLowerCase()) | ||
}) | ||
}) | ||
|
||
it('ignores the `attributes` property', function () { | ||
var vNode = new SerialVirtualNode({ | ||
nodeName: 'div', | ||
attributes: { | ||
'foo': 'foo', | ||
'bar': 'bar', | ||
'baz': 'baz' | ||
} | ||
}); | ||
assert.isUndefined(vNode.props.attributes) | ||
}) | ||
}) | ||
|
||
describe('attr', function () { | ||
it('returns a string value for the attribute', function () { | ||
var vNode = new SerialVirtualNode({ | ||
nodeName: 'div', | ||
attributes: { | ||
'foo': 'foo', | ||
'bar': 123, | ||
'baz': true | ||
} | ||
}); | ||
assert.equal(vNode.attr('foo'), 'foo'); | ||
assert.equal(vNode.attr('bar'), '123'); | ||
assert.equal(vNode.attr('baz'), 'true'); | ||
}) | ||
|
||
it('returns null if the attribute is null', function () { | ||
var vNode = new SerialVirtualNode({ | ||
nodeName: 'div', | ||
attributes: { 'foo': null } | ||
}); | ||
assert.isNull(vNode.attr('foo')); | ||
}) | ||
|
||
it('returns null if the attribute is not set', function () { | ||
var vNode = new SerialVirtualNode({ | ||
nodeName: 'div' | ||
}); | ||
assert.isNull(vNode.attr('foo')); | ||
}) | ||
|
||
it('converts `className` to `class`', function () { | ||
var vNode = new SerialVirtualNode({ | ||
nodeName: 'div', | ||
attributes: { | ||
className: 'foo bar baz' | ||
} | ||
}); | ||
assert.equal(vNode.attr('class'), 'foo bar baz'); | ||
}) | ||
|
||
it('converts `htmlFor` to `for`', function () { | ||
var vNode = new SerialVirtualNode({ | ||
nodeName: 'div', | ||
attributes: { | ||
htmlFor: 'foo' | ||
} | ||
}); | ||
assert.equal(vNode.attr('for'), 'foo'); | ||
}) | ||
}) | ||
|
||
describe('hasAttr', function () { | ||
it('returns true if the attribute has a value', function () { | ||
var vNode = new SerialVirtualNode({ | ||
nodeName: 'div', | ||
attributes: { | ||
'foo': '', | ||
'bar': 0, | ||
'baz': false | ||
} | ||
}); | ||
assert.isTrue(vNode.hasAttr('foo')); | ||
assert.isTrue(vNode.hasAttr('bar')); | ||
assert.isTrue(vNode.hasAttr('baz')); | ||
}) | ||
|
||
it('returns true if the attribute is null', function () { | ||
var vNode = new SerialVirtualNode({ | ||
nodeName: 'div', | ||
attributes: { 'foo': null } | ||
}); | ||
assert.isTrue(vNode.hasAttr('foo')); | ||
}) | ||
|
||
it('returns false if the attribute is undefined', function () { | ||
var vNode = new SerialVirtualNode({ | ||
nodeName: 'div', | ||
attributes: { 'foo': undefined } | ||
}); | ||
assert.isFalse(vNode.hasAttr('foo')); | ||
assert.isFalse(vNode.hasAttr('bar')); | ||
}) | ||
|
||
it('converts `htmlFor` to `for`', function () { | ||
var nodeWithoutFor = new SerialVirtualNode({ | ||
nodeName: 'div', | ||
attributes: {} | ||
}); | ||
var nodeWithFor = new SerialVirtualNode({ | ||
nodeName: 'div', | ||
attributes: { 'htmlFor': 'foo' } | ||
}); | ||
|
||
assert.isFalse(nodeWithoutFor.hasAttr('for')); | ||
assert.isTrue(nodeWithFor.hasAttr('for')); | ||
}) | ||
|
||
it('converts `className` to `class`', function () { | ||
var nodeWithoutClass = new SerialVirtualNode({ | ||
nodeName: 'div', | ||
attributes: {} | ||
}); | ||
var nodeWithClass = new SerialVirtualNode({ | ||
nodeName: 'div', | ||
attributes: { 'className': 'foo bar baz' } | ||
}); | ||
|
||
assert.isFalse(nodeWithoutClass.hasAttr('class')); | ||
assert.isTrue(nodeWithClass.hasAttr('class')); | ||
}) | ||
}) | ||
}); |