/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
/*jshint evil:true, unused:false*/
'use strict';
require('mock-modules').autoMockOff();
var transformFn = require('jstransform').transform;
var visitors = require('../react-jsx-visitors').visitorList;
function transform(code) {
return transformFn(visitors, code);
}
describe('react jsx', function() {
// These are placeholder variables in scope that we can use to assert that a
// specific variable reference was passed, rather than an object clone of it.
var x = 123456;
var y = 789012;
var z = 345678;
var expectObjectAssign = function(code) {
/*eslint-disable no-unused-vars, no-eval*/
var Component = jest.genMockFunction();
var Child = jest.genMockFunction();
var objectAssignMock = jest.genMockFunction();
React.__spread = objectAssignMock;
eval(transform(code).code);
return expect(objectAssignMock);
/*eslint-enable*/
};
var React = {
createElement: jest.genMockFunction()
};
it('should convert simple tags', function() {
var code = 'var x =
;';
var result = 'var x = React.createElement("div", null);';
expect(transform(code).code).toEqual(result);
});
it('should convert simple text', function() {
var code = 'var x = text
;';
var result = 'var x = React.createElement("div", null, "text");';
expect(transform(code).code).toEqual(result);
});
it('should have correct comma in nested children', function() {
var code = [
'var x = ',
'
',
'
{foo}
{bar}',
'
',
'
;'
].join('\n');
var result = [
'var x = React.createElement("div", null, ',
' React.createElement("div", null, ' +
'React.createElement("br", null)), ',
' React.createElement(Component, null, foo, ' +
'React.createElement("br", null), bar), ',
' React.createElement("br", null)',
');'
].join('\n');
expect(transform(code).code).toEqual(result);
});
it('should avoid wrapping in extra parens if not needed', function() {
// Try with a single composite child, wrapped in a div.
var code = [
'var x = ',
' ',
'
;'
].join('\n');
var result = [
'var x = React.createElement("div", null, ',
' React.createElement(Component, null)',
');'
].join('\n');
expect(transform(code).code).toEqual(result);
// Try with a single interpolated child, wrapped in a div.
code = [
'var x = ',
' {this.props.children}',
'
;'
].join('\n');
result = [
'var x = React.createElement("div", null, ',
' this.props.children',
');'
].join('\n');
expect(transform(code).code).toEqual(result);
// Try with a single interpolated child, wrapped in a composite.
code = [
'var x = ',
' {this.props.children}',
';'
].join('\n');
result = [
'var x = React.createElement(Composite, null, ',
' this.props.children',
');'
].join('\n');
expect(transform(code).code).toEqual(result);
// Try with a single composite child, wrapped in a composite.
code = [
'var x = ',
' ',
';'
].join('\n');
result = [
'var x = React.createElement(Composite, null, ',
' React.createElement(Composite2, null)',
');'
].join('\n');
expect(transform(code).code).toEqual(result);
});
it('should insert commas after expressions before whitespace', function() {
var code = [
'var x =',
' ',
'
;'
].join('\n');
var result = [
'var x =',
' React.createElement("div", {',
' attr1: ',
' "foo" + "bar", ',
' ',
' attr2: ',
' "foo" + "bar" +',
' ',
' "baz" + "bug", ',
' ',
' attr3: ',
' "foo" + "bar" +',
' "baz" + "bug", ',
' // Extra line here.',
' ',
' attr4: "baz"}',
' );'
].join('\n');
expect(transform(code).code).toEqual(result);
});
it('should properly handle comments adjacent to children', function() {
var code = [
'var x = (',
' ',
' {/* A comment at the beginning */}',
' {/* A second comment at the beginning */}',
' ',
' {/* A nested comment */}',
' ',
' {/* A sandwiched comment */}',
'
',
' {/* A comment at the end */}',
' {/* A second comment at the end */}',
'
',
');'
].join('\n');
var result = [
'var x = (',
' React.createElement("div", null, ',
' /* A comment at the beginning */',
' /* A second comment at the beginning */',
' React.createElement("span", null',
' /* A nested comment */',
' ), ',
' /* A sandwiched comment */',
' React.createElement("br", null)',
' /* A comment at the end */',
' /* A second comment at the end */',
' )',
');'
].join('\n');
expect(transform(code).code).toBe(result);
});
it('should properly handle comments between props', function() {
var code = [
'var x = (',
' ',
' ',
'
',
');'
].join('\n');
var result = [
'var x = (',
' React.createElement("div", {',
' /* a multi-line',
' comment */',
' attr1: "foo"}, ',
' React.createElement("span", {// a double-slash comment',
' attr2: "bar"}',
' )',
' )',
');'
].join('\n');
expect(transform(code).code).toBe(result);
});
it('should not strip tags with a single child of ', function() {
var code = [
'
;'
].join('\n');
var result = [
'React.createElement("div", null, "\u00A0");'
].join('\n');
expect(transform(code).code).toBe(result);
});
it('should not strip even coupled with other whitespace', function() {
var code = [
'
;'
].join('\n');
var result = [
'React.createElement("div", null, "\u00A0 ");'
].join('\n');
expect(transform(code).code).toBe(result);
});
it('should handle hasOwnProperty correctly', function() {
var code = 'testing;';
var result = 'React.createElement("hasOwnProperty", null, "testing");';
expect(transform(code).code).toBe(result);
});
it('should allow constructor as prop', function() {
var code = ';';
var result = 'React.createElement(Component, {constructor: "foo"});';
expect(transform(code).code).toBe(result);
});
it('should allow JS namespacing', function() {
var code = ';';
var result = 'React.createElement(Namespace.Component, null);';
expect(transform(code).code).toBe(result);
});
it('should allow deeper JS namespacing', function() {
var code = ';';
var result =
'React.createElement(Namespace.DeepNamespace.Component, null);';
expect(transform(code).code).toBe(result);
});
it('should disallow XML namespacing', function() {
var code = ';';
expect(() => transform(code)).toThrow();
});
it('wraps props in React.__spread for spread attributes', function() {
var code =
'';
var result =
'React.createElement(Component, React.__spread({}, x , {y: \n' +
'2, z: true}))';
expect(transform(code).code).toBe(result);
});
it('adds appropriate newlines when using spread attribute', function() {
var code =
'';
var result =
'React.createElement(Component, React.__spread({}, \n' +
' this.props, \n' +
' {sound: "moo"}))';
expect(transform(code).code).toBe(result);
});
it('handles overparenthesized JS', function() {
var code =
'Foo {(e+f //A line comment\n' +
'/* A multiline comment */)\n' +
'} bar\n' +
'';
var result = 'React.createElement("foo", {a: (b), c: (d)}, "Foo ", (e+f //A line comment\n' +
'/* A multiline comment */), \n' +
'" bar"\n' +
')';
expect(transform(code).code).toBe(result);
});
it('should transform known hyphenated tags', function() {
var code = ';';
var result = 'React.createElement("font-face", null);';
expect(transform(code).code).toBe(result);
});
it('does not call React.__spread when there are no spreads', function() {
expectObjectAssign(
''
).not.toBeCalled();
});
it('should not throw for unknown hyphenated tags', function() {
var code = ';';
expect(function() {
transform(code);
}).not.toThrow();
});
it('calls assign with a new target object for spreads', function() {
expectObjectAssign(
''
).toBeCalledWith({}, x);
});
it('calls assign with an empty object when the spread is first', function() {
expectObjectAssign(
''
).toBeCalledWith({}, x, {y: 2});
});
it('coalesces consecutive properties into a single object', function() {
expectObjectAssign(
''
).toBeCalledWith({}, x, {y: 2, z: true});
});
it('avoids an unnecessary empty object when spread is not first', function() {
expectObjectAssign(
''
).toBeCalledWith({x: 1}, y);
});
it('passes the same value multiple times to React.__spread', function() {
expectObjectAssign(
''
).toBeCalledWith({x: 1, y: '2'}, z, z);
});
it('evaluates sequences before passing them to React.__spread', function() {
expectObjectAssign(
'Text'
).toBeCalledWith({x: '1'}, {y: 2}, {z: 3});
});
});