rails-assets-enzyme 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/lib/rails/assets/enzyme.rb +9 -0
- data/lib/rails/assets/enzyme/version.rb +7 -0
- data/vendor/assets/javascripts/Debug.js +133 -0
- data/vendor/assets/javascripts/MountedTraversal.js +305 -0
- data/vendor/assets/javascripts/ReactWrapper.js +777 -0
- data/vendor/assets/javascripts/ReactWrapperComponent.jsx +91 -0
- data/vendor/assets/javascripts/ShallowTraversal.js +173 -0
- data/vendor/assets/javascripts/ShallowWrapper.js +744 -0
- data/vendor/assets/javascripts/Utils.js +266 -0
- data/vendor/assets/javascripts/index.js +14 -0
- data/vendor/assets/javascripts/mount.js +11 -0
- data/vendor/assets/javascripts/react-compat.js +166 -0
- data/vendor/assets/javascripts/render.js +19 -0
- data/vendor/assets/javascripts/shallow.js +11 -0
- data/vendor/assets/javascripts/version.js +5 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a2c7dd53db462ee9f020ae0e6804788737dc4d33
|
4
|
+
data.tar.gz: 152794407dc2405b920646ebc5c675c0183711ee
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bc5b396ced19648aeb68cbfd563768fa816e7643bba93a428c413b7465e9517777598bcd3d4205c6d8b48d56cc6092c5053972a50d39cbb30cec53262b655678
|
7
|
+
data.tar.gz: 70ee5a89eb4d5be6e86596793410f3a05504480ec0084c7a488c91ae69bf4a29919b48ff218681ace6e43f8597e717bf59491c83d40b9b7acb2aaca553d8b761
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2016 Franze Jr.
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Rails::Assets::Enzyme
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'rails-assets-enzyme'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install rails-assets-enzyme
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it ( http://github.com/<my-github-username>/rails-assets-enzyme/fork )
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
@@ -0,0 +1,133 @@
|
|
1
|
+
import {
|
2
|
+
childrenOfNode,
|
3
|
+
} from './ShallowTraversal';
|
4
|
+
import {
|
5
|
+
renderedChildrenOfInst,
|
6
|
+
} from './MountedTraversal';
|
7
|
+
import {
|
8
|
+
isDOMComponent,
|
9
|
+
isCompositeComponent,
|
10
|
+
isElement,
|
11
|
+
} from './react-compat';
|
12
|
+
import {
|
13
|
+
internalInstance,
|
14
|
+
propsOfNode,
|
15
|
+
} from './Utils';
|
16
|
+
import without from 'lodash/without';
|
17
|
+
import escape from 'lodash/escape';
|
18
|
+
import compact from 'lodash/compact';
|
19
|
+
import { REACT013 } from './version';
|
20
|
+
import objectValues from 'object.values';
|
21
|
+
|
22
|
+
export function typeName(node) {
|
23
|
+
return typeof node.type === 'function'
|
24
|
+
? (node.type.displayName || node.type.name || 'Component')
|
25
|
+
: node.type;
|
26
|
+
}
|
27
|
+
|
28
|
+
export function spaces(n) {
|
29
|
+
return Array(n + 1).join(' ');
|
30
|
+
}
|
31
|
+
|
32
|
+
export function indent(depth, string) {
|
33
|
+
return string.split('\n').map(x => `${spaces(depth)}${x}`).join('\n');
|
34
|
+
}
|
35
|
+
|
36
|
+
function propString(prop) {
|
37
|
+
switch (typeof prop) {
|
38
|
+
case 'function':
|
39
|
+
return '{[Function]}';
|
40
|
+
case 'string':
|
41
|
+
return `"${prop}"`;
|
42
|
+
case 'number':
|
43
|
+
case 'boolean':
|
44
|
+
return `{${prop}}`;
|
45
|
+
case 'object':
|
46
|
+
return '{{...}}';
|
47
|
+
default:
|
48
|
+
return `{[${typeof prop}]}`;
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
function propsString(node) {
|
53
|
+
const props = propsOfNode(node);
|
54
|
+
const keys = without(Object.keys(props), 'children');
|
55
|
+
return keys.map(key => `${key}=${propString(props[key])}`).join(' ');
|
56
|
+
}
|
57
|
+
|
58
|
+
export function debugNode(node, indentLength = 2) {
|
59
|
+
if (typeof node === 'string' || typeof node === 'number') return escape(node);
|
60
|
+
if (!node) return '';
|
61
|
+
|
62
|
+
const children = compact(childrenOfNode(node).map(n => debugNode(n, indentLength)));
|
63
|
+
const type = typeName(node);
|
64
|
+
const props = propsString(node);
|
65
|
+
const beforeProps = props ? ' ' : '';
|
66
|
+
const nodeClose = children.length ? `</${type}>` : '/>';
|
67
|
+
const afterProps = children.length
|
68
|
+
? '>'
|
69
|
+
: ' ';
|
70
|
+
const childrenIndented = children.length
|
71
|
+
? `\n${children.map(x => indent(indentLength, x)).join('\n')}\n`
|
72
|
+
: '';
|
73
|
+
return `<${type}${beforeProps}${props}${afterProps}${childrenIndented}${nodeClose}`;
|
74
|
+
}
|
75
|
+
|
76
|
+
export function debugNodes(nodes) {
|
77
|
+
return nodes.map(debugNode).join('\n\n\n');
|
78
|
+
}
|
79
|
+
|
80
|
+
export function debugInst(inst, indentLength = 2) {
|
81
|
+
if (typeof inst === 'string' || typeof inst === 'number') return escape(inst);
|
82
|
+
if (!inst) return '';
|
83
|
+
|
84
|
+
if (!inst.getPublicInstance) {
|
85
|
+
const internal = internalInstance(inst);
|
86
|
+
return debugInst(internal, indentLength);
|
87
|
+
}
|
88
|
+
const publicInst = inst.getPublicInstance();
|
89
|
+
|
90
|
+
if (typeof publicInst === 'string' || typeof publicInst === 'number') return escape(publicInst);
|
91
|
+
if (!publicInst && !inst._renderedComponent) return '';
|
92
|
+
|
93
|
+
// do stuff with publicInst
|
94
|
+
const currentElement = inst._currentElement;
|
95
|
+
const type = typeName(currentElement);
|
96
|
+
const props = propsString(currentElement);
|
97
|
+
const children = [];
|
98
|
+
if (isDOMComponent(publicInst)) {
|
99
|
+
const renderedChildren = renderedChildrenOfInst(inst);
|
100
|
+
if (!renderedChildren) {
|
101
|
+
children.push(...childrenOfNode(currentElement));
|
102
|
+
} else {
|
103
|
+
children.push(...objectValues(renderedChildren));
|
104
|
+
}
|
105
|
+
} else if (
|
106
|
+
!REACT013 &&
|
107
|
+
isElement(currentElement) &&
|
108
|
+
typeof currentElement.type === 'function'
|
109
|
+
) {
|
110
|
+
children.push(inst._renderedComponent);
|
111
|
+
} else if (
|
112
|
+
REACT013 &&
|
113
|
+
isCompositeComponent(publicInst)
|
114
|
+
) {
|
115
|
+
children.push(inst._renderedComponent);
|
116
|
+
}
|
117
|
+
|
118
|
+
const childrenStrs = compact(children.map(n => debugInst(n, indentLength)));
|
119
|
+
|
120
|
+
const beforeProps = props ? ' ' : '';
|
121
|
+
const nodeClose = childrenStrs.length ? `</${type}>` : '/>';
|
122
|
+
const afterProps = childrenStrs.length
|
123
|
+
? '>'
|
124
|
+
: ' ';
|
125
|
+
const childrenIndented = childrenStrs.length
|
126
|
+
? `\n${childrenStrs.map(x => indent(indentLength + 2, x)).join('\n')}\n`
|
127
|
+
: '';
|
128
|
+
return `<${type}${beforeProps}${props}${afterProps}${childrenIndented}${nodeClose}`;
|
129
|
+
}
|
130
|
+
|
131
|
+
export function debugInsts(insts) {
|
132
|
+
return insts.map(debugInst).join('\n\n\n');
|
133
|
+
}
|
@@ -0,0 +1,305 @@
|
|
1
|
+
import isEmpty from 'lodash/isEmpty';
|
2
|
+
import isSubset from 'is-subset';
|
3
|
+
import {
|
4
|
+
internalInstance,
|
5
|
+
coercePropValue,
|
6
|
+
nodeEqual,
|
7
|
+
propsOfNode,
|
8
|
+
isFunctionalComponent,
|
9
|
+
isSimpleSelector,
|
10
|
+
splitSelector,
|
11
|
+
selectorError,
|
12
|
+
selectorType,
|
13
|
+
isCompoundSelector,
|
14
|
+
AND,
|
15
|
+
SELECTOR,
|
16
|
+
nodeHasType,
|
17
|
+
} from './Utils';
|
18
|
+
import {
|
19
|
+
isDOMComponent,
|
20
|
+
isCompositeComponent,
|
21
|
+
isCompositeComponentWithType,
|
22
|
+
isElement,
|
23
|
+
findDOMNode,
|
24
|
+
} from './react-compat';
|
25
|
+
import { REACT013 } from './version';
|
26
|
+
|
27
|
+
export function getNode(inst) {
|
28
|
+
if (!inst || inst._store || typeof inst === 'string') {
|
29
|
+
return inst;
|
30
|
+
}
|
31
|
+
if (inst._currentElement) {
|
32
|
+
return inst._currentElement;
|
33
|
+
}
|
34
|
+
if (internalInstance(inst)) {
|
35
|
+
return internalInstance(inst)._currentElement;
|
36
|
+
}
|
37
|
+
if (inst._reactInternalInstance) {
|
38
|
+
return inst._reactInternalInstance._currentElement;
|
39
|
+
}
|
40
|
+
if (inst._reactInternalComponent) {
|
41
|
+
return inst._reactInternalComponent._currentElement;
|
42
|
+
}
|
43
|
+
return inst;
|
44
|
+
}
|
45
|
+
|
46
|
+
export function instEqual(a, b) {
|
47
|
+
return nodeEqual(getNode(a), getNode(b));
|
48
|
+
}
|
49
|
+
|
50
|
+
export function instHasClassName(inst, className) {
|
51
|
+
if (!isDOMComponent(inst)) {
|
52
|
+
return false;
|
53
|
+
}
|
54
|
+
let classes = findDOMNode(inst).className || '';
|
55
|
+
classes = classes.replace(/\s/g, ' ');
|
56
|
+
return ` ${classes} `.indexOf(` ${className} `) > -1;
|
57
|
+
}
|
58
|
+
|
59
|
+
export function instHasId(inst, id) {
|
60
|
+
if (!isDOMComponent(inst)) return false;
|
61
|
+
const instId = findDOMNode(inst).id || '';
|
62
|
+
return instId === id;
|
63
|
+
}
|
64
|
+
|
65
|
+
function isFunctionalComponentWithType(inst, func) {
|
66
|
+
return isFunctionalComponent(inst) && getNode(inst).type === func;
|
67
|
+
}
|
68
|
+
|
69
|
+
export function instHasType(inst, type) {
|
70
|
+
switch (typeof type) {
|
71
|
+
case 'string':
|
72
|
+
return nodeHasType(getNode(inst), type);
|
73
|
+
case 'function':
|
74
|
+
return isCompositeComponentWithType(inst, type) ||
|
75
|
+
isFunctionalComponentWithType(inst, type);
|
76
|
+
default:
|
77
|
+
return false;
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
export function instHasProperty(inst, propKey, stringifiedPropValue) {
|
82
|
+
if (!isDOMComponent(inst)) return false;
|
83
|
+
const node = getNode(inst);
|
84
|
+
const nodeProps = propsOfNode(node);
|
85
|
+
const descriptor = Object.getOwnPropertyDescriptor(nodeProps, propKey);
|
86
|
+
if (descriptor && descriptor.get) {
|
87
|
+
return false;
|
88
|
+
}
|
89
|
+
const nodePropValue = nodeProps[propKey];
|
90
|
+
|
91
|
+
const propValue = coercePropValue(propKey, stringifiedPropValue);
|
92
|
+
|
93
|
+
// intentionally not matching node props that are undefined
|
94
|
+
if (nodePropValue === undefined) {
|
95
|
+
return false;
|
96
|
+
}
|
97
|
+
|
98
|
+
if (propValue) {
|
99
|
+
return nodePropValue === propValue;
|
100
|
+
}
|
101
|
+
|
102
|
+
return nodeProps.hasOwnProperty(propKey);
|
103
|
+
}
|
104
|
+
|
105
|
+
// called with private inst
|
106
|
+
export function renderedChildrenOfInst(inst) {
|
107
|
+
return REACT013
|
108
|
+
? inst._renderedComponent._renderedChildren
|
109
|
+
: inst._renderedChildren;
|
110
|
+
}
|
111
|
+
|
112
|
+
// called with a private instance
|
113
|
+
export function childrenOfInstInternal(inst) {
|
114
|
+
if (!inst) {
|
115
|
+
return [];
|
116
|
+
}
|
117
|
+
if (!inst.getPublicInstance) {
|
118
|
+
const internal = internalInstance(inst);
|
119
|
+
return childrenOfInstInternal(internal);
|
120
|
+
}
|
121
|
+
|
122
|
+
const publicInst = inst.getPublicInstance();
|
123
|
+
const currentElement = inst._currentElement;
|
124
|
+
if (isDOMComponent(publicInst)) {
|
125
|
+
const children = [];
|
126
|
+
const renderedChildren = renderedChildrenOfInst(inst);
|
127
|
+
let key;
|
128
|
+
for (key in renderedChildren) {
|
129
|
+
if (!renderedChildren.hasOwnProperty(key)) {
|
130
|
+
continue;
|
131
|
+
}
|
132
|
+
if (REACT013 && !renderedChildren[key].getPublicInstance) {
|
133
|
+
continue;
|
134
|
+
}
|
135
|
+
if (!REACT013 && typeof renderedChildren[key]._currentElement.type === 'function') {
|
136
|
+
children.push(renderedChildren[key]._instance);
|
137
|
+
continue;
|
138
|
+
}
|
139
|
+
children.push(renderedChildren[key].getPublicInstance());
|
140
|
+
}
|
141
|
+
return children;
|
142
|
+
} else if (
|
143
|
+
!REACT013 &&
|
144
|
+
isElement(currentElement) &&
|
145
|
+
typeof currentElement.type === 'function'
|
146
|
+
) {
|
147
|
+
return childrenOfInstInternal(inst._renderedComponent);
|
148
|
+
} else if (
|
149
|
+
REACT013 &&
|
150
|
+
isCompositeComponent(publicInst)
|
151
|
+
) {
|
152
|
+
return childrenOfInstInternal(inst._renderedComponent);
|
153
|
+
}
|
154
|
+
return [];
|
155
|
+
}
|
156
|
+
|
157
|
+
export function internalInstanceOrComponent(node) {
|
158
|
+
if (REACT013) {
|
159
|
+
return node;
|
160
|
+
} else if (node._reactInternalComponent) {
|
161
|
+
return node._reactInternalComponent;
|
162
|
+
} else if (node._reactInternalInstance) {
|
163
|
+
return node._reactInternalInstance;
|
164
|
+
}
|
165
|
+
return node;
|
166
|
+
}
|
167
|
+
|
168
|
+
export function childrenOfInst(node) {
|
169
|
+
return childrenOfInstInternal(internalInstanceOrComponent(node));
|
170
|
+
}
|
171
|
+
|
172
|
+
export function pathToNode(node, root) {
|
173
|
+
const queue = [root];
|
174
|
+
const path = [];
|
175
|
+
|
176
|
+
while (queue.length) {
|
177
|
+
const current = queue.pop();
|
178
|
+
const children = childrenOfInst(current);
|
179
|
+
|
180
|
+
if (current === node) return path;
|
181
|
+
|
182
|
+
path.push(current);
|
183
|
+
|
184
|
+
if (children.length === 0) {
|
185
|
+
// leaf node. if it isn't the node we are looking for, we pop.
|
186
|
+
path.pop();
|
187
|
+
}
|
188
|
+
queue.push.apply(queue, children);
|
189
|
+
}
|
190
|
+
|
191
|
+
return null;
|
192
|
+
}
|
193
|
+
|
194
|
+
export function parentsOfInst(inst, root) {
|
195
|
+
return pathToNode(inst, root).reverse();
|
196
|
+
}
|
197
|
+
|
198
|
+
export function instMatchesObjectProps(inst, props) {
|
199
|
+
if (!isDOMComponent(inst)) return false;
|
200
|
+
const node = getNode(inst);
|
201
|
+
return isSubset(propsOfNode(node), props);
|
202
|
+
}
|
203
|
+
|
204
|
+
export function buildInstPredicate(selector) {
|
205
|
+
switch (typeof selector) {
|
206
|
+
case 'function':
|
207
|
+
// selector is a component constructor
|
208
|
+
return inst => instHasType(inst, selector);
|
209
|
+
|
210
|
+
case 'string':
|
211
|
+
if (!isSimpleSelector(selector)) {
|
212
|
+
throw selectorError(selector);
|
213
|
+
}
|
214
|
+
if (isCompoundSelector.test(selector)) {
|
215
|
+
return AND(splitSelector(selector).map(buildInstPredicate));
|
216
|
+
}
|
217
|
+
|
218
|
+
switch (selectorType(selector)) {
|
219
|
+
case SELECTOR.CLASS_TYPE:
|
220
|
+
return inst => instHasClassName(inst, selector.substr(1));
|
221
|
+
case SELECTOR.ID_TYPE:
|
222
|
+
return inst => instHasId(inst, selector.substr(1));
|
223
|
+
case SELECTOR.PROP_TYPE: {
|
224
|
+
const propKey = selector.split(/\[([a-zA-Z\-\:]*?)(=|\])/)[1];
|
225
|
+
const propValue = selector.split(/=(.*?)]/)[1];
|
226
|
+
|
227
|
+
return node => instHasProperty(node, propKey, propValue);
|
228
|
+
}
|
229
|
+
default:
|
230
|
+
// selector is a string. match to DOM tag or constructor displayName
|
231
|
+
return inst => instHasType(inst, selector);
|
232
|
+
}
|
233
|
+
|
234
|
+
case 'object':
|
235
|
+
if (!Array.isArray(selector) && selector !== null && !isEmpty(selector)) {
|
236
|
+
return node => instMatchesObjectProps(node, selector);
|
237
|
+
}
|
238
|
+
throw new TypeError(
|
239
|
+
'Enzyme::Selector does not support an array, null, or empty object as a selector'
|
240
|
+
);
|
241
|
+
|
242
|
+
default:
|
243
|
+
throw new TypeError('Enzyme::Selector expects a string, object, or Component Constructor');
|
244
|
+
}
|
245
|
+
}
|
246
|
+
|
247
|
+
// This function should be called with an "internal instance". Nevertheless, if it is
|
248
|
+
// called with a "public instance" instead, the function will call itself with the
|
249
|
+
// internal instance and return the proper result.
|
250
|
+
function findAllInRenderedTreeInternal(inst, test) {
|
251
|
+
if (!inst) {
|
252
|
+
return [];
|
253
|
+
}
|
254
|
+
|
255
|
+
if (!inst.getPublicInstance) {
|
256
|
+
const internal = internalInstance(inst);
|
257
|
+
return findAllInRenderedTreeInternal(internal, test);
|
258
|
+
}
|
259
|
+
const publicInst = inst.getPublicInstance() || inst._instance;
|
260
|
+
let ret = test(publicInst) ? [publicInst] : [];
|
261
|
+
const currentElement = inst._currentElement;
|
262
|
+
if (isDOMComponent(publicInst)) {
|
263
|
+
const renderedChildren = renderedChildrenOfInst(inst);
|
264
|
+
let key;
|
265
|
+
for (key in renderedChildren) {
|
266
|
+
if (!renderedChildren.hasOwnProperty(key)) {
|
267
|
+
continue;
|
268
|
+
}
|
269
|
+
if (REACT013 && !renderedChildren[key].getPublicInstance) {
|
270
|
+
continue;
|
271
|
+
}
|
272
|
+
ret = ret.concat(
|
273
|
+
findAllInRenderedTreeInternal(renderedChildren[key], test)
|
274
|
+
);
|
275
|
+
}
|
276
|
+
} else if (
|
277
|
+
!REACT013 &&
|
278
|
+
isElement(currentElement) &&
|
279
|
+
typeof currentElement.type === 'function'
|
280
|
+
) {
|
281
|
+
ret = ret.concat(
|
282
|
+
findAllInRenderedTreeInternal(
|
283
|
+
inst._renderedComponent,
|
284
|
+
test
|
285
|
+
)
|
286
|
+
);
|
287
|
+
} else if (
|
288
|
+
REACT013 &&
|
289
|
+
isCompositeComponent(publicInst)
|
290
|
+
) {
|
291
|
+
ret = ret.concat(
|
292
|
+
findAllInRenderedTreeInternal(
|
293
|
+
inst._renderedComponent,
|
294
|
+
test
|
295
|
+
)
|
296
|
+
);
|
297
|
+
}
|
298
|
+
return ret;
|
299
|
+
}
|
300
|
+
|
301
|
+
// This function could be called with a number of different things technically, so we need to
|
302
|
+
// pass the *right* thing to our internal helper.
|
303
|
+
export function treeFilter(node, test) {
|
304
|
+
return findAllInRenderedTreeInternal(internalInstanceOrComponent(node), test);
|
305
|
+
}
|