/** * 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 */ 'use strict'; var React; var ReactDOM; var ReactTestUtils; var createReactClass; // For testing DOM Fiber. global.requestAnimationFrame = function(callback) { setTimeout(callback); }; global.requestIdleCallback = function(callback) { setTimeout(() => { callback({ timeRemaining() { return Infinity; } }); }); }; describe('ReactClass-spec', () => { beforeEach(() => { React = require('react'); ReactDOM = require('react-dom'); ReactTestUtils = require('react-addons-test-utils'); createReactClass = require('./index'); }); it('should throw when `render` is not specified', () => { expect(function() { createReactClass({}); }).toThrowError( 'createClass(...): Class specification must implement a `render` method.' ); }); // TODO: Update babel-plugin-transform-react-display-name xit('should copy `displayName` onto the Constructor', () => { var TestComponent = createReactClass({ render: function() { return
; }, }); expect(TestComponent.displayName) .toBe('TestComponent'); }); it('should copy prop types onto the Constructor', () => { var propValidator = jest.fn(); var TestComponent = createReactClass({ propTypes: { value: propValidator, }, render: function() { return ; }, }); expect(TestComponent.propTypes).toBeDefined(); expect(TestComponent.propTypes.value) .toBe(propValidator); }); it('should warn on invalid prop types', () => { spyOn(console, 'error'); createReactClass({ displayName: 'Component', propTypes: { prop: null, }, render: function() { return {this.props.prop}; }, }); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: Component: prop type `prop` is invalid; ' + 'it must be a function, usually from React.PropTypes.' ); }); it('should warn on invalid context types', () => { spyOn(console, 'error'); createReactClass({ displayName: 'Component', contextTypes: { prop: null, }, render: function() { return {this.props.prop}; }, }); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: Component: context type `prop` is invalid; ' + 'it must be a function, usually from React.PropTypes.' ); }); it('should throw on invalid child context types', () => { spyOn(console, 'error'); createReactClass({ displayName: 'Component', childContextTypes: { prop: null, }, render: function() { return {this.props.prop}; }, }); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: Component: child context type `prop` is invalid; ' + 'it must be a function, usually from React.PropTypes.' ); }); it('should warn when mispelling shouldComponentUpdate', () => { spyOn(console, 'error'); createReactClass({ componentShouldUpdate: function() { return false; }, render: function() { return ; }, }); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: A component has a method called componentShouldUpdate(). Did you ' + 'mean shouldComponentUpdate()? The name is phrased as a question ' + 'because the function is expected to return a value.' ); createReactClass({ displayName: 'NamedComponent', componentShouldUpdate: function() { return false; }, render: function() { return ; }, }); expect(console.error.calls.count()).toBe(2); expect(console.error.calls.argsFor(1)[0]).toBe( 'Warning: NamedComponent has a method called componentShouldUpdate(). Did you ' + 'mean shouldComponentUpdate()? The name is phrased as a question ' + 'because the function is expected to return a value.' ); }); it('should warn when mispelling componentWillReceiveProps', () => { spyOn(console, 'error'); createReactClass({ componentWillRecieveProps: function() { return false; }, render: function() { return ; }, }); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: A component has a method called componentWillRecieveProps(). Did you ' + 'mean componentWillReceiveProps()?' ); }); it('should throw if a reserved property is in statics', () => { expect(function() { createReactClass({ statics: { getDefaultProps: function() { return { foo: 0, }; }, }, render: function() { return ; }, }); }).toThrowError( 'ReactClass: You are attempting to define a reserved property, ' + '`getDefaultProps`, that shouldn\'t be on the "statics" key. Define ' + 'it as an instance property instead; it will still be accessible on ' + 'the constructor.' ); }); // TODO: Consider actually moving these to statics or drop this unit test. xit('should warn when using deprecated non-static spec keys', () => { spyOn(console, 'error'); createReactClass({ mixins: [{}], propTypes: { foo: React.PropTypes.string, }, contextTypes: { foo: React.PropTypes.string, }, childContextTypes: { foo: React.PropTypes.string, }, render: function() { return ; }, }); expect(console.error.calls.count()).toBe(4); expect(console.error.calls.argsFor(0)[0]).toBe( 'createClass(...): `mixins` is now a static property and should ' + 'be defined inside "statics".' ); expect(console.error.calls.argsFor(1)[0]).toBe( 'createClass(...): `propTypes` is now a static property and should ' + 'be defined inside "statics".' ); expect(console.error.calls.argsFor(2)[0]).toBe( 'createClass(...): `contextTypes` is now a static property and ' + 'should be defined inside "statics".' ); expect(console.error.calls.argsFor(3)[0]).toBe( 'createClass(...): `childContextTypes` is now a static property and ' + 'should be defined inside "statics".' ); }); it('should support statics', () => { var Component = createReactClass({ statics: { abc: 'def', def: 0, ghi: null, jkl: 'mno', pqr: function() { return this; }, }, render: function() { return ; }, }); var instance =