Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81164 views
1
/**
2
* Copyright 2013-2014, Facebook, Inc.
3
* All rights reserved.
4
*
5
* This source code is licensed under the BSD-style license found in the
6
* LICENSE file in the root directory of this source tree. An additional grant
7
* of patent rights can be found in the PATENTS file in the same directory.
8
*
9
* @emails react-core
10
*/
11
12
/*jshint evil:true, unused:false*/
13
14
"use strict";
15
16
require('mock-modules').autoMockOff();
17
18
describe('react jsx', function() {
19
var transformAll = require('../../syntax.js').transformAll;
20
var xjs = require('../xjs.js');
21
22
// Add <font-face> to list of known tags to ensure that when we test support
23
// for hyphentated known tags, it's there.
24
// TODO: remove this when/if <font-face> is supported out of the box.
25
xjs.knownTags['font-face'] = true;
26
27
var transform = function(code, options, excludes) {
28
return transformAll(
29
code,
30
options,
31
(excludes || []).concat(['sourcemeta', 'allocate'])
32
);
33
};
34
35
// These are placeholder variables in scope that we can use to assert that a
36
// specific variable reference was passed, rather than an object clone of it.
37
var x = 123456;
38
var y = 789012;
39
var z = 345678;
40
41
var expectObjectAssign = function(code) {
42
var Component = jest.genMockFunction();
43
var Child = jest.genMockFunction();
44
var objectAssignMock = jest.genMockFunction();
45
React.__spread = objectAssignMock;
46
eval(transform(code).code);
47
return expect(objectAssignMock);
48
}
49
50
var React = {
51
createElement: jest.genMockFunction()
52
};
53
54
it('should convert simple tags', function() {
55
var code = 'var x = <div></div>;';
56
var result = 'var x = React.createElement("div", null);';
57
58
expect(transform(code).code).toEqual(result);
59
});
60
61
it('should convert simple text', function() {
62
var code = 'var x = <div>text</div>;';
63
var result = 'var x = React.createElement("div", null, "text");';
64
65
expect(transform(code).code).toEqual(result);
66
});
67
68
it('should have correct comma in nested children', function() {
69
var code = [
70
'var x = <div>',
71
' <div><br /></div>',
72
' <Component>{foo}<br />{bar}</Component>',
73
' <br />',
74
'</div>;'
75
].join('\n');
76
var result = [
77
'var x = React.createElement("div", null, ',
78
' React.createElement("div", null, ' +
79
'React.createElement("br", null)), ',
80
' React.createElement(Component, null, foo, ' +
81
'React.createElement("br", null), bar), ',
82
' React.createElement("br", null)',
83
');'
84
].join('\n');
85
86
expect(transform(code).code).toEqual(result);
87
});
88
89
it('should avoid wrapping in extra parens if not needed', function() {
90
// Try with a single composite child, wrapped in a div.
91
var code = [
92
'var x = <div>',
93
' <Component />',
94
'</div>;'
95
].join('\n');
96
var result = [
97
'var x = React.createElement("div", null, ',
98
' React.createElement(Component, null)',
99
');'
100
].join('\n');
101
102
expect(transform(code).code).toEqual(result);
103
104
// Try with a single interpolated child, wrapped in a div.
105
code = [
106
'var x = <div>',
107
' {this.props.children}',
108
'</div>;'
109
].join('\n');
110
result = [
111
'var x = React.createElement("div", null, ',
112
' this.props.children',
113
');'
114
].join('\n');
115
expect(transform(code).code).toEqual(result);
116
117
// Try with a single interpolated child, wrapped in a composite.
118
code = [
119
'var x = <Composite>',
120
' {this.props.children}',
121
'</Composite>;'
122
].join('\n');
123
result = [
124
'var x = React.createElement(Composite, null, ',
125
' this.props.children',
126
');'
127
].join('\n');
128
expect(transform(code).code).toEqual(result);
129
130
// Try with a single composite child, wrapped in a composite.
131
code = [
132
'var x = <Composite>',
133
' <Composite2 />',
134
'</Composite>;'
135
].join('\n');
136
result = [
137
'var x = React.createElement(Composite, null, ',
138
' React.createElement(Composite2, null)',
139
');'
140
].join('\n');
141
expect(transform(code).code).toEqual(result);
142
});
143
144
it('should insert commas after expressions before whitespace', function() {
145
var code = [
146
'var x =',
147
' <div',
148
' attr1={',
149
' "foo" + "bar"',
150
' }',
151
' attr2={',
152
' "foo" + "bar" +',
153
' ',
154
' "baz" + "bug"',
155
' }',
156
' attr3={',
157
' "foo" + "bar" +',
158
' "baz" + "bug"',
159
' // Extra line here.',
160
' }',
161
' attr4="baz">',
162
' </div>;'
163
].join('\n');
164
var result = [
165
'var x =',
166
' React.createElement("div", {',
167
' attr1: ',
168
' "foo" + "bar", ',
169
' ',
170
' attr2: ',
171
' "foo" + "bar" +',
172
' ',
173
' "baz" + "bug", ',
174
' ',
175
' attr3: ',
176
' "foo" + "bar" +',
177
' "baz" + "bug", ',
178
' // Extra line here.',
179
' ',
180
' attr4: "baz"}',
181
' );'
182
].join('\n');
183
184
expect(transform(code).code).toEqual(result);
185
});
186
187
it('should properly handle comments adjacent to children', function() {
188
var code = [
189
'var x = (',
190
' <div>',
191
' {/* A comment at the beginning */}',
192
' {/* A second comment at the beginning */}',
193
' <span>',
194
' {/* A nested comment */}',
195
' </span>',
196
' {/* A sandwiched comment */}',
197
' <br />',
198
' {/* A comment at the end */}',
199
' {/* A second comment at the end */}',
200
' </div>',
201
');'
202
].join('\n');
203
var result = [
204
'var x = (',
205
' React.createElement("div", null, ',
206
' /* A comment at the beginning */',
207
' /* A second comment at the beginning */',
208
' React.createElement("span", null',
209
' /* A nested comment */',
210
' ), ',
211
' /* A sandwiched comment */',
212
' React.createElement("br", null)',
213
' /* A comment at the end */',
214
' /* A second comment at the end */',
215
' )',
216
');'
217
].join('\n');
218
219
expect(transform(code).code).toBe(result);
220
});
221
222
it('should properly handle comments between props', function() {
223
var code = [
224
'var x = (',
225
' <div',
226
' /* a multi-line',
227
' comment */',
228
' attr1="foo">',
229
' <span // a double-slash comment',
230
' attr2="bar"',
231
' />',
232
' </div>',
233
');'
234
].join('\n');
235
var result = [
236
'var x = (',
237
' React.createElement("div", {',
238
' /* a multi-line',
239
' comment */',
240
' attr1: "foo"}, ',
241
' React.createElement("span", {// a double-slash comment',
242
' attr2: "bar"}',
243
' )',
244
' )',
245
');'
246
].join('\n');
247
248
expect(transform(code).code).toBe(result);
249
});
250
251
it('should not strip tags with a single child of &nbsp;', function() {
252
var code = [
253
'<div>&nbsp;</div>;'
254
].join('\n');
255
var result = [
256
'React.createElement("div", null, "\u00A0");'
257
].join('\n');
258
259
expect(transform(code).code).toBe(result);
260
});
261
262
it('should not strip &nbsp; even coupled with other whitespace', function() {
263
var code = [
264
'<div>&nbsp; </div>;'
265
].join('\n');
266
var result = [
267
'React.createElement("div", null, "\u00A0 ");'
268
].join('\n');
269
270
expect(transform(code).code).toBe(result);
271
});
272
273
it('should handle hasOwnProperty correctly', function() {
274
var code = '<hasOwnProperty>testing</hasOwnProperty>;';
275
// var result = 'React.createElement("hasOwnProperty", null, "testing");';
276
277
// expect(transform(code).code).toBe(result);
278
279
// This is currently not supported, and will generate a string tag in
280
// a follow up.
281
expect(() => transform(code)).toThrow();
282
});
283
284
it('should allow constructor as prop', function() {
285
var code = '<Component constructor="foo" />;';
286
var result = 'React.createElement(Component, {constructor: "foo"});';
287
288
expect(transform(code).code).toBe(result);
289
});
290
291
it('should allow JS namespacing', function() {
292
var code = '<Namespace.Component />;';
293
var result = 'React.createElement(Namespace.Component, null);';
294
295
expect(transform(code).code).toBe(result);
296
});
297
298
it('should allow deeper JS namespacing', function() {
299
var code = '<Namespace.DeepNamespace.Component />;';
300
var result =
301
'React.createElement(Namespace.DeepNamespace.Component, null);';
302
303
expect(transform(code).code).toBe(result);
304
});
305
306
it('should disallow XML namespacing', function() {
307
var code = '<Namespace:Component />;';
308
309
expect(() => transform(code)).toThrow();
310
});
311
312
it('wraps props in React.__spread for spread attributes', function() {
313
var code =
314
'<Component { ... x } y\n' +
315
'={2 } z />';
316
var result =
317
'React.createElement(Component, React.__spread({}, x , {y: \n' +
318
'2, z: true}))';
319
320
expect(transform(code).code).toBe(result);
321
});
322
323
it('adds appropriate newlines when using spread attribute', function() {
324
var code =
325
'<Component\n' +
326
' {...this.props}\n' +
327
' sound="moo" />';
328
var result =
329
'React.createElement(Component, React.__spread({}, \n' +
330
' this.props, \n' +
331
' {sound: "moo"}))';
332
333
expect(transform(code).code).toBe(result);
334
});
335
336
it('should transform known hyphenated tags', function() {
337
var code = '<font-face />;';
338
var result = 'React.createElement("font-face", null);';
339
340
expect(transform(code).code).toBe(result);
341
});
342
343
it('does not call React.__spread when there are no spreads', function() {
344
expectObjectAssign(
345
'<Component x={y} />'
346
).not.toBeCalled();
347
});
348
349
it('should throw for unknown hyphenated tags', function() {
350
var code = '<x-component />;';
351
352
expect(() => transform(code)).toThrow();
353
});
354
355
it('calls assign with a new target object for spreads', function() {
356
expectObjectAssign(
357
'<Component {...x} />'
358
).toBeCalledWith({}, x);
359
});
360
361
it('calls assign with an empty object when the spread is first', function() {
362
expectObjectAssign(
363
'<Component { ...x } y={2} />'
364
).toBeCalledWith({}, x, { y: 2 });
365
});
366
367
it('coalesces consecutive properties into a single object', function() {
368
expectObjectAssign(
369
'<Component { ... x } y={2} z />'
370
).toBeCalledWith({}, x, { y: 2, z: true });
371
});
372
373
it('avoids an unnecessary empty object when spread is not first', function() {
374
expectObjectAssign(
375
'<Component x={1} {...y} />'
376
).toBeCalledWith({x: 1}, y);
377
});
378
379
it('passes the same value multiple times to React.__spread', function() {
380
expectObjectAssign(
381
'<Component x={1} y="2" {...z} {...z}><Child /></Component>'
382
).toBeCalledWith({x: 1, y: "2"}, z, z);
383
});
384
385
it('evaluates sequences before passing them to React.__spread', function() {
386
expectObjectAssign(
387
'<Component x="1" {...(z = { y: 2 }, z)} z={3}>Text</Component>'
388
).toBeCalledWith({x: "1"}, { y: 2 }, {z: 3});
389
});
390
391
});
392
393