123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- /**
- * 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 = <div></div>;';
- var result = 'var x = React.createElement("div", null);';
- expect(transform(code).code).toEqual(result);
- });
- it('should convert simple text', function() {
- var code = 'var x = <div>text</div>;';
- 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 = <div>',
- ' <div><br /></div>',
- ' <Component>{foo}<br />{bar}</Component>',
- ' <br />',
- '</div>;'
- ].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 = <div>',
- ' <Component />',
- '</div>;'
- ].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 = <div>',
- ' {this.props.children}',
- '</div>;'
- ].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 = <Composite>',
- ' {this.props.children}',
- '</Composite>;'
- ].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 = <Composite>',
- ' <Composite2 />',
- '</Composite>;'
- ].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 =',
- ' <div',
- ' attr1={',
- ' "foo" + "bar"',
- ' }',
- ' attr2={',
- ' "foo" + "bar" +',
- ' ',
- ' "baz" + "bug"',
- ' }',
- ' attr3={',
- ' "foo" + "bar" +',
- ' "baz" + "bug"',
- ' // Extra line here.',
- ' }',
- ' attr4="baz">',
- ' </div>;'
- ].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 = (',
- ' <div>',
- ' {/* A comment at the beginning */}',
- ' {/* A second comment at the beginning */}',
- ' <span>',
- ' {/* A nested comment */}',
- ' </span>',
- ' {/* A sandwiched comment */}',
- ' <br />',
- ' {/* A comment at the end */}',
- ' {/* A second comment at the end */}',
- ' </div>',
- ');'
- ].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 = (',
- ' <div',
- ' /* a multi-line',
- ' comment */',
- ' attr1="foo">',
- ' <span // a double-slash comment',
- ' attr2="bar"',
- ' />',
- ' </div>',
- ');'
- ].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 = [
- '<div> </div>;'
- ].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 = [
- '<div> </div>;'
- ].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 = '<hasOwnProperty>testing</hasOwnProperty>;';
- var result = 'React.createElement("hasOwnProperty", null, "testing");';
- expect(transform(code).code).toBe(result);
- });
- it('should allow constructor as prop', function() {
- var code = '<Component constructor="foo" />;';
- var result = 'React.createElement(Component, {constructor: "foo"});';
- expect(transform(code).code).toBe(result);
- });
- it('should allow JS namespacing', function() {
- var code = '<Namespace.Component />;';
- var result = 'React.createElement(Namespace.Component, null);';
- expect(transform(code).code).toBe(result);
- });
- it('should allow deeper JS namespacing', function() {
- var code = '<Namespace.DeepNamespace.Component />;';
- var result =
- 'React.createElement(Namespace.DeepNamespace.Component, null);';
- expect(transform(code).code).toBe(result);
- });
- it('should disallow XML namespacing', function() {
- var code = '<Namespace:Component />;';
- expect(() => transform(code)).toThrow();
- });
- it('wraps props in React.__spread for spread attributes', function() {
- var code =
- '<Component { ... x } y\n' +
- '={2 } z />';
- 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 =
- '<Component\n' +
- ' {...this.props}\n' +
- ' sound="moo" />';
- 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 a={(b)} c={(d)}>Foo {(e+f //A line comment\n' +
- '/* A multiline comment */)\n' +
- '} bar\n' +
- '</foo>';
- 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 = '<font-face />;';
- 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(
- '<Component x={y} />'
- ).not.toBeCalled();
- });
- it('should not throw for unknown hyphenated tags', function() {
- var code = '<x-component />;';
- expect(function() {
- transform(code);
- }).not.toThrow();
- });
- it('calls assign with a new target object for spreads', function() {
- expectObjectAssign(
- '<Component {...x} />'
- ).toBeCalledWith({}, x);
- });
- it('calls assign with an empty object when the spread is first', function() {
- expectObjectAssign(
- '<Component { ...x } y={2} />'
- ).toBeCalledWith({}, x, {y: 2});
- });
- it('coalesces consecutive properties into a single object', function() {
- expectObjectAssign(
- '<Component { ... x } y={2} z />'
- ).toBeCalledWith({}, x, {y: 2, z: true});
- });
- it('avoids an unnecessary empty object when spread is not first', function() {
- expectObjectAssign(
- '<Component x={1} {...y} />'
- ).toBeCalledWith({x: 1}, y);
- });
- it('passes the same value multiple times to React.__spread', function() {
- expectObjectAssign(
- '<Component x={1} y="2" {...z} {...z}><Child /></Component>'
- ).toBeCalledWith({x: 1, y: '2'}, z, z);
- });
- it('evaluates sequences before passing them to React.__spread', function() {
- expectObjectAssign(
- '<Component x="1" {...(z = { y: 2 }, z)} z={3}>Text</Component>'
- ).toBeCalledWith({x: '1'}, {y: 2}, {z: 3});
- });
- });
|