-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathindex.js
187 lines (168 loc) · 6.01 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
var assert = require('assert');
/**
* Check if the given `node` looks like a virtual node. If `type` is specified,
* it will ensure that type is strictly equal. (whether that is a `Component`
* or a `String` element name)
*
* @param {Object} node The virtual node to check.
* @param {*} [type] If given, the type of node this must be.
*/
exports.isNode = function (node, type) {
assert(node, 'expected a virtual node');
assert(node.attributes, 'expected a virtual node');
assert(node.children, 'expected a virtual node');
assert(node.type, 'expected a virtual node');
if (type) assert.strictEqual(node.type, type);
};
/**
* Check if the given `node` has the given `attr` attribute. If the `value` is
* passed, it must match strictly.
*
* @param {Object} node The virtual node to check.
* @param {String} attr The attribute name to look for.
* @param {*} [value] If given, the type of node this must be.
*/
exports.hasAttribute = function (node, attr, value) {
exports.isNode(node);
assert(attr, 'expected an attribute name');
assert(attr in node.attributes, 'expected to find the attribute ' + attr + ' in the given node');
if (typeof value !== 'undefined') {
if (typeof value === 'function') {
value(node.attributes[attr]);
} else {
assert.strictEqual(node.attributes[attr], value);
}
}
};
/**
* Ensure that the given `node` does **not** have the given `attr` attribute.
*
* @param {Object} node The virtual node to check.
* @param {String} attr The attribute name to look for.
*/
exports.notHasAttribute = function (node, attr) {
exports.isNode(node);
assert(attr, 'expected an attribute name');
assert(!(attr in node.attributes), 'expected to not find the attribute ' + attr + ' in the given node');
};
/**
* Check if the given `node` has the corresponding `children`, using the
* following criteria:
*
* - when not given, it will ensure that there is at least 1 child node.
* - when an `Array`, it will ensure that the child nodes are loosely equal.
* - when a `Number`, it will ensure that the number of child nodes matches
* that number.
* - when a `Function`, it will run the fn against each node, where they are
* expected to throw if they are invalid.
*
* @param {Object} node The virtual node to check.
* @param {*} [children] The criteria for the child nodes. (see above)
*/
exports.hasChildren = function (node, children) {
exports.isNode(node);
if (Array.isArray(children)) {
assert.deepEqual(node.children, children);
} else if (typeof children === 'number') {
assert.strictEqual(node.children.length, children);
} else if (typeof children === 'function') {
node.children.forEach(children);
} else if (typeof children === 'string') {
assert.deepEqual(node.children, [ children ]);
} else {
assert(node.children.length > 0, 'expected to find child nodes');
}
};
/**
* Check if the given `node` at a given zero-indexed `index` has the
* corresponding `child`, using the following `criteria`:
*
* - When a `Function`, it will run `criteria`, passing the child node as an
* argument. `criteria` is expected to throw an error if the node is invalid.
* - Otherwise, it will do a deep comparison between the child node and
* the criteria.
*
* @param {Object} node The virtual node to check.
* @param {number} index The index of the child to inspect. Zero indexed.
* @param {*} [criteria] The criteria for the child nodes. (see above)
*/
exports.hasChild = function (node, index, criteria) {
exports.isNode(node);
assert(node.children.length > 0, 'provided node has no children');
var child = Array.isArray(index)
? deepChild(node, index)
: node.children[index];
assert(typeof child !== 'undefined', 'child does not exist at the given index ' + index);
if (typeof criteria !== 'undefined') {
if (typeof criteria === 'function') {
criteria(child);
} else {
assert.deepEqual(child, criteria);
}
}
};
/**
* Ensures that the given `node` does not have any child nodes.
*
* @param {Object} node The virtual node to check.
*/
exports.notHasChildren = function (node) {
exports.isNode(node);
assert(node.children.length === 0, 'expected to not find any child nodes');
};
/**
* Assert that the given `node` has the given CSS class `name` applied.
* (generally, this is only useful for HTML nodes)
*
* @param {Object} node The virtual node to check.
* @param {String} name The class name to look for.
*/
exports.hasClass = function (node, name) {
exports.isNode(node);
assert(name, 'expected a class name');
var list = classes(node.attributes.class);
assert(list.indexOf(name) > -1, 'expected to find class name ' + name);
};
/**
* Assert that the given `node` does **not** have the given CSS class `name`.
*
* @param {Object} node The virtual node to check.
* @param {String} name The class name to look for.
*/
exports.notHasClass = function (node, name) {
exports.isNode(node);
assert(name, 'expected a class name');
var list = classes(node.attributes.class);
assert(list.indexOf(name) === -1, 'expected to not find class name ' + name);
};
// private helpers
/**
* Parse the given `input` into an `Array` of class names. Will always return
* an `Array`, even if it's empty.
*
* @param {String} [input] The class attribute string.
* @return {Array}
*/
function classes(input) {
if (!input) return [];
assert.strictEqual(typeof input, 'string', 'expected a string for the class name');
if (!input.trim()) return [];
return input.trim().split(/\s+/g);
}
/**
* Retrieve a deep child via an input array `index` of indices to traverse.
*
* @param {Object} node The virtual node to traverse.
* @param {Array:Number} path The path to traverse.
* @return {Object}
*/
function deepChild(root, path) {
return path.reduce(function (node, index, x) {
assert(index in node.children, 'child does not exist at the given deep index ' + path.join('.'));
return node.children[index];
}, root);
}
/**
* No operation.
*/
function noop() {}