diff --git a/src/classic/__tests__/ReactContextValidator-test.js b/src/classic/__tests__/ReactContextValidator-test.js new file mode 100644 index 0000000000..3f9f8c017b --- /dev/null +++ b/src/classic/__tests__/ReactContextValidator-test.js @@ -0,0 +1,247 @@ +/** + * Copyright 2013-2014, 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 + */ + +// This test doesn't really have a good home yet. I'm leaving it here since this +// behavior belongs to the old propTypes system yet is currently implemented +// in the core ReactCompositeComponent. It should technically live in core's +// test suite but I'll leave it here to indicate that this is an issue that +// needs to be fixed. + +"use strict"; + +var React; +var ReactTestUtils; + +var reactComponentExpect; +var mocks; + +describe('ReactContextValidator', function() { + beforeEach(function() { + require('mock-modules').dumpCache(); + + React = require('React'); + ReactTestUtils = require('ReactTestUtils'); + reactComponentExpect = require('reactComponentExpect'); + mocks = require('mocks'); + + console.warn = mocks.getMockFunction(); + }); + + // TODO: This behavior creates a runtime dependency on propTypes. We should + // ensure that this is not required for ES6 classes with Flow. + + it('should filter out context not in contextTypes', function() { + var Component = React.createClass({ + contextTypes: { + foo: React.PropTypes.string + }, + + render: function() { + return
; + } + }); + + var ComponentInFooBarContext = React.createClass({ + childContextTypes: { + foo: React.PropTypes.string, + bar: React.PropTypes.number + }, + + getChildContext: function() { + return { + foo: 'abc', + bar: 123 + }; + }, + + render: function() { + return ; + } + }); + + var instance = ReactTestUtils.renderIntoDocument(); + reactComponentExpect(instance).expectRenderedChild().scalarContextEqual({foo: 'abc'}); + }); + + it('should filter context properly in callbacks', function() { + var actualComponentWillReceiveProps; + var actualShouldComponentUpdate; + var actualComponentWillUpdate; + var actualComponentDidUpdate; + + var Parent = React.createClass({ + childContextTypes: { + foo: React.PropTypes.string.isRequired, + bar: React.PropTypes.string.isRequired + }, + + getChildContext: function() { + return { + foo: this.props.foo, + bar: "bar" + }; + }, + + render: function() { + return ; + } + }); + + var Component = React.createClass({ + contextTypes: { + foo: React.PropTypes.string + }, + + componentWillReceiveProps: function(nextProps, nextContext) { + actualComponentWillReceiveProps = nextContext; + return true; + }, + + shouldComponentUpdate: function(nextProps, nextState, nextContext) { + actualShouldComponentUpdate = nextContext; + return true; + }, + + componentWillUpdate: function(nextProps, nextState, nextContext) { + actualComponentWillUpdate = nextContext; + }, + + componentDidUpdate: function(prevProps, prevState, prevContext) { + actualComponentDidUpdate = prevContext; + }, + + render: function() { + return
; + } + }); + + var instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + instance.replaceProps({foo: "def"}); + expect(actualComponentWillReceiveProps).toEqual({foo: 'def'}); + expect(actualShouldComponentUpdate).toEqual({foo: 'def'}); + expect(actualComponentWillUpdate).toEqual({foo: 'def'}); + expect(actualComponentDidUpdate).toEqual({foo: 'abc'}); + }); + + it('should check context types', function() { + var Component = React.createClass({ + contextTypes: { + foo: React.PropTypes.string.isRequired + }, + + render: function() { + return
; + } + }); + + ReactTestUtils.renderIntoDocument(); + + expect(console.warn.mock.calls.length).toBe(1); + expect(console.warn.mock.calls[0][0]).toBe( + 'Warning: Required context `foo` was not specified in `Component`.' + ); + + var ComponentInFooStringContext = React.createClass({ + childContextTypes: { + foo: React.PropTypes.string + }, + + getChildContext: function() { + return { + foo: this.props.fooValue + }; + }, + + render: function() { + return ; + } + }); + + ReactTestUtils.renderIntoDocument( + + ); + + // Previous call should not error + expect(console.warn.mock.calls.length).toBe(1); + + var ComponentInFooNumberContext = React.createClass({ + childContextTypes: { + foo: React.PropTypes.number + }, + + getChildContext: function() { + return { + foo: this.props.fooValue + }; + }, + + render: function() { + return ; + } + }); + + ReactTestUtils.renderIntoDocument(); + + expect(console.warn.mock.calls.length).toBe(2); + expect(console.warn.mock.calls[1][0]).toBe( + 'Warning: Invalid context `foo` of type `number` supplied ' + + 'to `Component`, expected `string`.' + + ' Check the render method of `ComponentInFooNumberContext`.' + ); + }); + + it('should check child context types', function() { + var Component = React.createClass({ + childContextTypes: { + foo: React.PropTypes.string.isRequired, + bar: React.PropTypes.number + }, + + getChildContext: function() { + return this.props.testContext; + }, + + render: function() { + return
; + } + }); + + ReactTestUtils.renderIntoDocument(); + expect(console.warn.mock.calls.length).toBe(2); + expect(console.warn.mock.calls[0][0]).toBe( + 'Warning: Required child context `foo` was not specified in `Component`.' + ); + expect(console.warn.mock.calls[1][0]).toBe( + 'Warning: Required child context `foo` was not specified in `Component`.' + ); + + ReactTestUtils.renderIntoDocument(); + + expect(console.warn.mock.calls.length).toBe(4); + expect(console.warn.mock.calls[3][0]).toBe( + 'Warning: Invalid child context `foo` of type `number` ' + + 'supplied to `Component`, expected `string`.' + ); + + ReactTestUtils.renderIntoDocument( + + ); + + ReactTestUtils.renderIntoDocument( + + ); + + // Previous calls should not log errors + expect(console.warn.mock.calls.length).toBe(4); + }); + +}); diff --git a/src/class/ReactClass.js b/src/classic/class/ReactClass.js similarity index 100% rename from src/class/ReactClass.js rename to src/classic/class/ReactClass.js diff --git a/src/core/ReactDoNotBindDeprecated.js b/src/classic/class/ReactDoNotBindDeprecated.js similarity index 100% rename from src/core/ReactDoNotBindDeprecated.js rename to src/classic/class/ReactDoNotBindDeprecated.js diff --git a/src/core/__tests__/ReactBind-test.js b/src/classic/class/__tests__/ReactBind-test.js similarity index 100% rename from src/core/__tests__/ReactBind-test.js rename to src/classic/class/__tests__/ReactBind-test.js diff --git a/src/classic/class/__tests__/ReactClass-test.js b/src/classic/class/__tests__/ReactClass-test.js new file mode 100644 index 0000000000..edabe5a057 --- /dev/null +++ b/src/classic/class/__tests__/ReactClass-test.js @@ -0,0 +1,338 @@ +/** + * Copyright 2013-2014, 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 mocks = require('mocks'); + +var React; +var ReactTestUtils; +var reactComponentExpect; + +describe('ReactClass-spec', function() { + + beforeEach(function() { + React = require('React'); + ReactTestUtils = require('ReactTestUtils'); + reactComponentExpect = require('reactComponentExpect'); + }); + + it('should throw when `render` is not specified', function() { + expect(function() { + React.createClass({}); + }).toThrow( + 'Invariant Violation: createClass(...): Class specification must ' + + 'implement a `render` method.' + ); + }); + + it('should copy `displayName` onto the Constructor', function() { + var TestComponent = React.createClass({ + render: function() { + return
; + } + }); + + expect(TestComponent.displayName) + .toBe('TestComponent'); + }); + + it('should copy prop types onto the Constructor', function() { + var propValidator = mocks.getMockFunction(); + var TestComponent = React.createClass({ + propTypes: { + value: propValidator + }, + render: function() { + return
; + } + }); + + expect(TestComponent.propTypes).toBeDefined(); + expect(TestComponent.propTypes.value) + .toBe(propValidator); + }); + + it('should throw on invalid prop types', function() { + expect(function() { + React.createClass({ + displayName: 'Component', + propTypes: { + prop: null + }, + render: function() { + return {this.props.prop}; + } + }); + }).toThrow( + 'Invariant Violation: Component: prop type `prop` is invalid; ' + + 'it must be a function, usually from React.PropTypes.' + ); + }); + + it('should throw on invalid context types', function() { + expect(function() { + React.createClass({ + displayName: 'Component', + contextTypes: { + prop: null + }, + render: function() { + return {this.props.prop}; + } + }); + }).toThrow( + 'Invariant Violation: Component: context type `prop` is invalid; ' + + 'it must be a function, usually from React.PropTypes.' + ); + }); + + it('should throw on invalid child context types', function() { + expect(function() { + React.createClass({ + displayName: 'Component', + childContextTypes: { + prop: null + }, + render: function() { + return {this.props.prop}; + } + }); + }).toThrow( + 'Invariant Violation: Component: child context type `prop` is invalid; ' + + 'it must be a function, usually from React.PropTypes.' + ); + }); + + it('should warn when mispelling shouldComponentUpdate', function() { + var warn = console.warn; + console.warn = mocks.getMockFunction(); + + try { + React.createClass({ + componentShouldUpdate: function() { + return false; + }, + render: function() { + return
; + } + }); + expect(console.warn.mock.calls.length).toBe(1); + expect(console.warn.mock.calls[0][0]).toBe( + '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.' + ); + + var NamedComponent = React.createClass({ + componentShouldUpdate: function() { + return false; + }, + render: function() { + return
; + } + }); + expect(console.warn.mock.calls.length).toBe(2); + expect(console.warn.mock.calls[1][0]).toBe( + '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.' + ); + + ; // Shut up lint + } finally { + console.warn = warn; + } + }); + + it('should throw if a reserved property is in statics', function() { + expect(function() { + React.createClass({ + statics: { + getDefaultProps: function() { + return { + foo: 0 + }; + } + }, + + render: function() { + return ; + } + }); + }).toThrow( + 'Invariant Violation: 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', function() { + var warn = console.warn; + console.warn = mocks.getMockFunction(); + try { + React.createClass({ + mixins: [{}], + propTypes: { + foo: ReactPropTypes.string + }, + contextTypes: { + foo: ReactPropTypes.string + }, + childContextTypes: { + foo: ReactPropTypes.string + }, + render: function() { + return
; + } + }); + expect(console.warn.mock.calls.length).toBe(4); + expect(console.warn.mock.calls[0][0]).toBe( + 'createClass(...): `mixins` is now a static property and should ' + + 'be defined inside "statics".' + ); + expect(console.warn.mock.calls[1][0]).toBe( + 'createClass(...): `propTypes` is now a static property and should ' + + 'be defined inside "statics".' + ); + expect(console.warn.mock.calls[2][0]).toBe( + 'createClass(...): `contextTypes` is now a static property and ' + + 'should be defined inside "statics".' + ); + expect(console.warn.mock.calls[3][0]).toBe( + 'createClass(...): `childContextTypes` is now a static property and ' + + 'should be defined inside "statics".' + ); + } finally { + console.warn = warn; + } + }); + + it('should support statics', function() { + var Component = React.createClass({ + statics: { + abc: 'def', + def: 0, + ghi: null, + jkl: 'mno', + pqr: function() { + return this; + } + }, + + render: function() { + return ; + } + }); + var instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + expect(instance.constructor.abc).toBe('def'); + expect(Component.abc).toBe('def'); + expect(instance.constructor.def).toBe(0); + expect(Component.def).toBe(0); + expect(instance.constructor.ghi).toBe(null); + expect(Component.ghi).toBe(null); + expect(instance.constructor.jkl).toBe('mno'); + expect(Component.jkl).toBe('mno'); + expect(instance.constructor.pqr()).toBe(Component); + expect(Component.pqr()).toBe(Component); + }); + + it('should work with object getInitialState() return values', function() { + var Component = React.createClass({ + getInitialState: function() { + return { + occupation: 'clown' + }; + }, + render: function() { + return ; + } + }); + var instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + expect(instance.state.occupation).toEqual('clown'); + }); + + it('should throw with non-object getInitialState() return values', function() { + [['an array'], 'a string', 1234].forEach(function(state) { + var Component = React.createClass({ + getInitialState: function() { + return state; + }, + render: function() { + return ; + } + }); + var instance = ; + expect(function() { + instance = ReactTestUtils.renderIntoDocument(instance); + }).toThrow( + 'Invariant Violation: Component.getInitialState(): ' + + 'must return an object or null' + ); + }); + }); + + it('should work with a null getInitialState() return value', function() { + var Component = React.createClass({ + getInitialState: function() { + return null; + }, + render: function() { + return ; + } + }); + expect( + () => ReactTestUtils.renderIntoDocument() + ).not.toThrow(); + }); + + it('should work with object getInitialState() return values', function() { + var Component = React.createClass({ + getInitialState: function() { + return { + occupation: 'clown' + }; + }, + render: function() { + return ; + } + }); + var instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + expect(instance.state.occupation).toEqual('clown'); + }); + + it('should throw with non-object getInitialState() return values', function() { + [['an array'], 'a string', 1234].forEach(function(state) { + var Component = React.createClass({ + getInitialState: function() { + return state; + }, + render: function() { + return ; + } + }); + var instance = ; + expect(function() { + instance = ReactTestUtils.renderIntoDocument(instance); + }).toThrow( + 'Invariant Violation: Component.getInitialState(): ' + + 'must return an object or null' + ); + }); + }); + +}); diff --git a/src/classic/class/__tests__/ReactClassMixin-test.js b/src/classic/class/__tests__/ReactClassMixin-test.js new file mode 100644 index 0000000000..1db776f590 --- /dev/null +++ b/src/classic/class/__tests__/ReactClassMixin-test.js @@ -0,0 +1,459 @@ +/** + * Copyright 2013-2014, 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 mocks = require('mocks'); + +var React; +var ReactTestUtils; +var reactComponentExpect; + +var TestComponent; +var TestComponentWithPropTypes; +var TestComponentWithReverseSpec; +var mixinPropValidator; +var componentPropValidator; + +describe('ReactClass-mixin', function() { + + beforeEach(function() { + React = require('React'); + ReactTestUtils = require('ReactTestUtils'); + reactComponentExpect = require('reactComponentExpect'); + mixinPropValidator = mocks.getMockFunction(); + componentPropValidator = mocks.getMockFunction(); + + var MixinA = { + propTypes: { + propA: function() {} + }, + componentDidMount: function() { + this.props.listener('MixinA didMount'); + } + }; + + var MixinB = { + mixins: [MixinA], + propTypes: { + propB: function() {} + }, + componentDidMount: function() { + this.props.listener('MixinB didMount'); + } + }; + + var MixinBWithReverseSpec = { + componentDidMount: function() { + this.props.listener('MixinBWithReverseSpec didMount'); + }, + mixins: [MixinA] + }; + + var MixinC = { + statics: { + staticC: function() {} + }, + componentDidMount: function() { + this.props.listener('MixinC didMount'); + } + }; + + var MixinD = { + propTypes: { + value: mixinPropValidator + } + }; + + TestComponent = React.createClass({ + mixins: [MixinB, MixinC, MixinD], + statics: { + staticComponent: function() {} + }, + propTypes: { + propComponent: function() {} + }, + componentDidMount: function() { + this.props.listener('Component didMount'); + }, + render: function() { + return
; + } + }); + + TestComponentWithReverseSpec = React.createClass({ + render: function() { + return
; + }, + componentDidMount: function() { + this.props.listener('Component didMount'); + }, + mixins: [MixinBWithReverseSpec, MixinC, MixinD] + }); + + TestComponentWithPropTypes = React.createClass({ + mixins: [MixinD], + propTypes: { + value: componentPropValidator + }, + render: function() { + return
; + } + }); + }); + + it('should support merging propTypes and statics', function() { + var listener = mocks.getMockFunction(); + var instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + + var instancePropTypes = instance.constructor.propTypes; + + expect('propA' in instancePropTypes).toBe(true); + expect('propB' in instancePropTypes).toBe(true); + expect('propComponent' in instancePropTypes).toBe(true); + + expect('staticC' in TestComponent).toBe(true); + expect('staticComponent' in TestComponent).toBe(true); + }); + + it('should support chaining delegate functions', function() { + var listener = mocks.getMockFunction(); + var instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + + expect(listener.mock.calls).toEqual([ + ['MixinA didMount'], + ['MixinB didMount'], + ['MixinC didMount'], + ['Component didMount'] + ]); + }); + + it('should chain functions regardless of spec property order', function() { + var listener = mocks.getMockFunction(); + var instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + + expect(listener.mock.calls).toEqual([ + ['MixinA didMount'], + ['MixinBWithReverseSpec didMount'], + ['MixinC didMount'], + ['Component didMount'] + ]); + }); + + it('should validate prop types via mixins', function() { + expect(TestComponent.propTypes).toBeDefined(); + expect(TestComponent.propTypes.value) + .toBe(mixinPropValidator); + }); + + it('should override mixin prop types with class prop types', function() { + // Sanity check... + expect(componentPropValidator).toNotBe(mixinPropValidator); + // Actually check... + expect(TestComponentWithPropTypes.propTypes) + .toBeDefined(); + expect(TestComponentWithPropTypes.propTypes.value) + .toNotBe(mixinPropValidator); + expect(TestComponentWithPropTypes.propTypes.value) + .toBe(componentPropValidator); + }); + + + it('should support mixins with getInitialState()', function() { + var Mixin = { + getInitialState: function() { + return {mixin: true}; + } + }; + var Component = React.createClass({ + mixins: [Mixin], + getInitialState: function() { + return {component: true}; + }, + render: function() { + return ; + } + }); + var instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + expect(instance.state.component).toBe(true); + expect(instance.state.mixin).toBe(true); + }); + + it('should throw with conflicting getInitialState() methods', function() { + var Mixin = { + getInitialState: function() { + return {x: true}; + } + }; + var Component = React.createClass({ + mixins: [Mixin], + getInitialState: function() { + return {x: true}; + }, + render: function() { + return ; + } + }); + var instance = ; + expect(function() { + instance = ReactTestUtils.renderIntoDocument(instance); + }).toThrow( + 'Invariant Violation: mergeIntoWithNoDuplicateKeys(): ' + + 'Tried to merge two objects with the same key: `x`. This conflict ' + + 'may be due to a mixin; in particular, this may be caused by two ' + + 'getInitialState() or getDefaultProps() methods returning objects ' + + 'with clashing keys.' + ); + }); + + it('should not mutate objects returned by getInitialState()', function() { + var Mixin = { + getInitialState: function() { + return Object.freeze({mixin: true}); + } + }; + var Component = React.createClass({ + mixins: [Mixin], + getInitialState: function() { + return Object.freeze({component: true}); + }, + render: function() { + return ; + } + }); + expect(() => { + ReactTestUtils.renderIntoDocument(); + }).not.toThrow(); + }); + + it('should support statics in mixins', function() { + var Mixin = { + statics: { + foo: 'bar' + } + }; + var Component = React.createClass({ + mixins: [Mixin], + + statics: { + abc: 'def' + }, + + render: function() { + return ; + } + }); + var instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + expect(instance.constructor.foo).toBe('bar'); + expect(Component.foo).toBe('bar'); + expect(instance.constructor.abc).toBe('def'); + expect(Component.abc).toBe('def'); + }); + + it("should throw if mixins override each others' statics", function() { + expect(function() { + var Mixin = { + statics: { + abc: 'foo' + } + }; + React.createClass({ + mixins: [Mixin], + + statics: { + abc: 'bar' + }, + + render: function() { + return ; + } + }); + }).toThrow( + 'Invariant Violation: ReactClass: You are attempting to ' + + 'define `abc` on your component more than once. This conflict may be ' + + 'due to a mixin.' + ); + }); + + it("should throw if mixins override functions in statics", function() { + expect(function() { + var Mixin = { + statics: { + abc: function() { console.log('foo'); } + } + }; + React.createClass({ + mixins: [Mixin], + + statics: { + abc: function() { console.log('bar'); } + }, + + render: function() { + return ; + } + }); + }).toThrow( + 'Invariant Violation: ReactClass: You are attempting to ' + + 'define `abc` on your component more than once. This conflict may be ' + + 'due to a mixin.' + ); + }); + + it("should throw if the mixin is a React component", function() { + expect(function() { + React.createClass({ + mixins: [
], + + render: function() { + return ; + } + }); + }).toThrow( + 'Invariant Violation: ReactClass: You\'re attempting to ' + + 'use a component as a mixin. Instead, just use a regular object.' + ); + }); + + it("should throw if the mixin is a React component class", function() { + expect(function() { + var Component = React.createClass({ + render: function() { + return ; + } + }); + + React.createClass({ + mixins: [Component], + + render: function() { + return ; + } + }); + }).toThrow( + 'Invariant Violation: ReactClass: You\'re attempting to ' + + 'use a component class as a mixin. Instead, just use a regular object.' + ); + }); + + it('should have bound the mixin methods to the component', function() { + var mixin = { + mixinFunc: function() {return this;} + }; + + var Component = React.createClass({ + mixins: [mixin], + componentDidMount: function() { + expect(this.mixinFunc()).toBe(this); + }, + render: function() { + return ; + } + }); + var instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + }); + + it('should include the mixin keys in even if their values are falsy', + function() { + var mixin = { + keyWithNullValue: null, + randomCounter: 0 + }; + + var Component = React.createClass({ + mixins: [mixin], + componentDidMount: function() { + expect(this.randomCounter).toBe(0); + expect(this.keyWithNullValue).toBeNull(); + }, + render: function() { + return ; + } + }); + var instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + }); + + it('should work with a null getInitialState return value and a mixin', () => { + var Component; + var instance; + + var Mixin = { + getInitialState: function() { + return {foo: 'bar'}; + } + }; + Component = React.createClass({ + mixins: [Mixin], + getInitialState: function() { + return null; + }, + render: function() { + return ; + } + }); + expect( + () => ReactTestUtils.renderIntoDocument() + ).not.toThrow(); + + instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + expect(instance.state).toEqual({foo: 'bar'}); + + // Also the other way round should work + var Mixin2 = { + getInitialState: function() { + return null; + } + }; + Component = React.createClass({ + mixins: [Mixin2], + getInitialState: function() { + return {foo: 'bar'}; + }, + render: function() { + return ; + } + }); + expect( + () => ReactTestUtils.renderIntoDocument() + ).not.toThrow(); + + instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + expect(instance.state).toEqual({foo: 'bar'}); + + // Multiple mixins should be fine too + Component = React.createClass({ + mixins: [Mixin, Mixin2], + getInitialState: function() { + return {x: true}; + }, + render: function() { + return ; + } + }); + expect( + () => ReactTestUtils.renderIntoDocument() + ).not.toThrow(); + + instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + expect(instance.state).toEqual({foo: 'bar', x: true}); + }); + +}); diff --git a/src/core/ReactElement.js b/src/classic/element/ReactElement.js similarity index 100% rename from src/core/ReactElement.js rename to src/classic/element/ReactElement.js diff --git a/src/core/ReactElementValidator.js b/src/classic/element/ReactElementValidator.js similarity index 100% rename from src/core/ReactElementValidator.js rename to src/classic/element/ReactElementValidator.js diff --git a/src/core/__tests__/ReactElement-test.js b/src/classic/element/__tests__/ReactElement-test.js similarity index 87% rename from src/core/__tests__/ReactElement-test.js rename to src/classic/element/__tests__/ReactElement-test.js index c4c348fff1..0f64f24d8b 100644 --- a/src/core/__tests__/ReactElement-test.js +++ b/src/classic/element/__tests__/ReactElement-test.js @@ -11,6 +11,8 @@ "use strict"; +var mocks; + var React; var ReactElement; var ReactTestUtils; @@ -21,6 +23,8 @@ describe('ReactElement', function() { beforeEach(function() { require('mock-modules').dumpCache(); + mocks = require('mocks'); + React = require('React'); ReactElement = require('ReactElement'); ReactTestUtils = require('ReactTestUtils'); @@ -370,4 +374,56 @@ describe('ReactElement', function() { expect(element.constructor).toBe(object.constructor); }); + it('should use default prop value when removing a prop', function() { + var Component = React.createClass({ + getDefaultProps: function() { + return {fruit: 'persimmon'}; + }, + render: function() { + return ; + } + }); + + var container = document.createElement('div'); + var instance = React.render( + , + container + ); + expect(instance.props.fruit).toBe('mango'); + + React.render(, container); + expect(instance.props.fruit).toBe('persimmon'); + }); + + it('should normalize props with default values', function() { + var warn = console.warn; + console.warn = mocks.getMockFunction(); + + var Component = React.createClass({ + propTypes: {prop: React.PropTypes.string.isRequired}, + getDefaultProps: function() { + return {prop: 'testKey'}; + }, + getInitialState: function() { + return {prop: this.props.prop + 'State'}; + }, + render: function() { + return {this.props.prop}; + } + }); + + var instance = ReactTestUtils.renderIntoDocument(); + expect(instance.props.prop).toBe('testKey'); + expect(instance.state.prop).toBe('testKeyState'); + + ReactTestUtils.renderIntoDocument(); + + expect(console.warn.mock.calls.length).toBe(1); + expect(console.warn.mock.calls[0][0]).toBe( + 'Warning: Required prop `prop` was not specified in `Component`.' + ); + + console.warn = warn; + }); + }); diff --git a/src/classic/element/__tests__/ReactElementValidator-test.js b/src/classic/element/__tests__/ReactElementValidator-test.js new file mode 100644 index 0000000000..490dc9182f --- /dev/null +++ b/src/classic/element/__tests__/ReactElementValidator-test.js @@ -0,0 +1,110 @@ +/** + * Copyright 2013-2014, 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 ReactTestUtils; + +describe('ReactElementValidator', function() { + var ComponentClass; + + beforeEach(function() { + require('mock-modules').dumpCache(); + + React = require('React'); + ReactTestUtils = require('ReactTestUtils'); + ComponentClass = React.createClass({ + render: function() { return
; } + }); + }); + + // TODO: These warnings currently come from the composite component, but + // they should be moved into the ReactElementValidator. + + it('should give context for PropType errors in nested components.', () => { + // In this test, we're making sure that if a proptype error is found in a + // component, we give a small hint as to which parent instantiated that + // component as per warnings about key usage in ReactElementValidator. + spyOn(console, 'warn'); + var MyComp = React.createClass({ + propTypes: { + color: React.PropTypes.string + }, + render: function() { + return
My color is {this.color}
; + } + }); + var ParentComp = React.createClass({ + render: function() { + return ; + } + }); + ReactTestUtils.renderIntoDocument(); + expect(console.warn.calls[0].args[0]).toBe( + 'Warning: Invalid prop `color` of type `number` supplied to `MyComp`, ' + + 'expected `string`. Check the render method of `ParentComp`.' + ); + }); + + it('should check default prop values', function() { + spyOn(console, 'warn'); + + var Component = React.createClass({ + propTypes: {prop: React.PropTypes.string.isRequired}, + getDefaultProps: function() { + return {prop: null}; + }, + render: function() { + return {this.props.prop}; + } + }); + + ReactTestUtils.renderIntoDocument(); + + expect(console.warn.calls.length).toBe(1); + expect(console.warn.calls[0].args[0]).toBe( + 'Warning: Required prop `prop` was not specified in `Component`.' + ); + }); + + it('should check declared prop types', function() { + spyOn(console, 'warn'); + + var Component = React.createClass({ + propTypes: { + prop: React.PropTypes.string.isRequired + }, + render: function() { + return {this.props.prop}; + } + }); + + ReactTestUtils.renderIntoDocument(); + ReactTestUtils.renderIntoDocument(); + + expect(console.warn.calls.length).toBe(2); + expect(console.warn.calls[0].args[0]).toBe( + 'Warning: Required prop `prop` was not specified in `Component`.' + ); + + expect(console.warn.calls[1].args[0]).toBe( + 'Warning: Invalid prop `prop` of type `number` supplied to ' + + '`Component`, expected `string`.' + ); + + ReactTestUtils.renderIntoDocument(); + + // Should not error for strings + expect(console.warn.calls.length).toBe(2); + }); + +}); diff --git a/src/core/ReactPropTypeLocationNames.js b/src/classic/propTypes/ReactPropTypeLocationNames.js similarity index 100% rename from src/core/ReactPropTypeLocationNames.js rename to src/classic/propTypes/ReactPropTypeLocationNames.js diff --git a/src/core/ReactPropTypeLocations.js b/src/classic/propTypes/ReactPropTypeLocations.js similarity index 100% rename from src/core/ReactPropTypeLocations.js rename to src/classic/propTypes/ReactPropTypeLocations.js diff --git a/src/core/ReactPropTypes.js b/src/classic/propTypes/ReactPropTypes.js similarity index 100% rename from src/core/ReactPropTypes.js rename to src/classic/propTypes/ReactPropTypes.js diff --git a/src/core/__tests__/ReactPropTypes-test.js b/src/classic/propTypes/__tests__/ReactPropTypes-test.js similarity index 100% rename from src/core/__tests__/ReactPropTypes-test.js rename to src/classic/propTypes/__tests__/ReactPropTypes-test.js diff --git a/src/core/__tests__/ReactCompositeComponent-test.js b/src/core/__tests__/ReactCompositeComponent-test.js index 299a5ccf5c..101f2218e6 100644 --- a/src/core/__tests__/ReactCompositeComponent-test.js +++ b/src/core/__tests__/ReactCompositeComponent-test.js @@ -85,31 +85,6 @@ describe('ReactCompositeComponent', function() { console.warn = warn; }); - it('should give context for PropType errors in nested components.', () => { - // In this test, we're making sure that if a proptype error is found in a - // component, we give a small hint as to which parent instantiated that - // component as per warnings about key usage in ReactElementValidator. - spyOn(console, 'warn'); - var MyComp = React.createClass({ - propTypes: { - color: ReactPropTypes.string - }, - render: function() { - return
My color is {this.color}
; - } - }); - var ParentComp = React.createClass({ - render: function() { - return ; - } - }); - ReactTestUtils.renderIntoDocument(); - expect(console.warn.calls[0].args[0]).toBe( - 'Warning: Invalid prop `color` of type `number` supplied to `MyComp`, ' + - 'expected `string`. Check the render method of `ParentComp`.' - ); - }); - it('should support rendering to different child types over time', function() { var instance = ; instance = ReactTestUtils.renderIntoDocument(instance); @@ -299,152 +274,6 @@ describe('ReactCompositeComponent', function() { expect(inputProps.prop).not.toBeDefined(); }); - it('should use default prop value when removing a prop', function() { - var Component = React.createClass({ - getDefaultProps: function() { - return {fruit: 'persimmon'}; - }, - render: function() { - return ; - } - }); - - var container = document.createElement('div'); - var instance = React.render( - , - container - ); - expect(instance.props.fruit).toBe('mango'); - - React.render(, container); - expect(instance.props.fruit).toBe('persimmon'); - }); - - it('should normalize props with default values', function() { - var Component = React.createClass({ - propTypes: {prop: ReactPropTypes.string.isRequired}, - getDefaultProps: function() { - return {prop: 'testKey'}; - }, - getInitialState: function() { - return {prop: this.props.prop + 'State'}; - }, - render: function() { - return {this.props.prop}; - } - }); - - var instance = ReactTestUtils.renderIntoDocument(); - reactComponentExpect(instance).scalarPropsEqual({prop: 'testKey'}); - reactComponentExpect(instance).scalarStateEqual({prop: 'testKeyState'}); - - ReactTestUtils.renderIntoDocument(); - - expect(console.warn.mock.calls.length).toBe(1); - expect(console.warn.mock.calls[0][0]).toBe( - 'Warning: Required prop `prop` was not specified in `Component`.' - ); - }); - - it('should check default prop values', function() { - var Component = React.createClass({ - propTypes: {prop: ReactPropTypes.string.isRequired}, - getDefaultProps: function() { - return {prop: null}; - }, - render: function() { - return {this.props.prop}; - } - }); - - ReactTestUtils.renderIntoDocument(); - - expect(console.warn.mock.calls.length).toBe(1); - expect(console.warn.mock.calls[0][0]).toBe( - 'Warning: Required prop `prop` was not specified in `Component`.' - ); - }); - - it('should check declared prop types', function() { - var Component = React.createClass({ - propTypes: { - prop: ReactPropTypes.string.isRequired - }, - render: function() { - return {this.props.prop}; - } - }); - - ReactTestUtils.renderIntoDocument(); - ReactTestUtils.renderIntoDocument(); - - expect(console.warn.mock.calls.length).toBe(2); - expect(console.warn.mock.calls[0][0]).toBe( - 'Warning: Required prop `prop` was not specified in `Component`.' - ); - - expect(console.warn.mock.calls[1][0]).toBe( - 'Warning: Invalid prop `prop` of type `number` supplied to ' + - '`Component`, expected `string`.' - ); - - ReactTestUtils.renderIntoDocument(); - - // Should not error for strings - expect(console.warn.mock.calls.length).toBe(2); - }); - - it('should throw on invalid prop types', function() { - expect(function() { - React.createClass({ - displayName: 'Component', - propTypes: { - prop: null - }, - render: function() { - return {this.props.prop}; - } - }); - }).toThrow( - 'Invariant Violation: Component: prop type `prop` is invalid; ' + - 'it must be a function, usually from React.PropTypes.' - ); - }); - - it('should throw on invalid context types', function() { - expect(function() { - React.createClass({ - displayName: 'Component', - contextTypes: { - prop: null - }, - render: function() { - return {this.props.prop}; - } - }); - }).toThrow( - 'Invariant Violation: Component: context type `prop` is invalid; ' + - 'it must be a function, usually from React.PropTypes.' - ); - }); - - it('should throw on invalid child context types', function() { - expect(function() { - React.createClass({ - displayName: 'Component', - childContextTypes: { - prop: null - }, - render: function() { - return {this.props.prop}; - } - }); - }).toThrow( - 'Invariant Violation: Component: child context type `prop` is invalid; ' + - 'it must be a function, usually from React.PropTypes.' - ); - }); - it('should not allow `forceUpdate` on unmounted components', function() { var container = document.createElement('div'); document.documentElement.appendChild(container); @@ -574,228 +403,6 @@ describe('ReactCompositeComponent', function() { expect(ReactCurrentOwner.current).toBe(null); }); - it('should support mixins with getInitialState()', function() { - var Mixin = { - getInitialState: function() { - return {mixin: true}; - } - }; - var Component = React.createClass({ - mixins: [Mixin], - getInitialState: function() { - return {component: true}; - }, - render: function() { - return ; - } - }); - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.state.component).toBe(true); - expect(instance.state.mixin).toBe(true); - }); - - it('should throw with conflicting getInitialState() methods', function() { - var Mixin = { - getInitialState: function() { - return {x: true}; - } - }; - var Component = React.createClass({ - mixins: [Mixin], - getInitialState: function() { - return {x: true}; - }, - render: function() { - return ; - } - }); - var instance = ; - expect(function() { - instance = ReactTestUtils.renderIntoDocument(instance); - }).toThrow( - 'Invariant Violation: mergeIntoWithNoDuplicateKeys(): ' + - 'Tried to merge two objects with the same key: `x`. This conflict ' + - 'may be due to a mixin; in particular, this may be caused by two ' + - 'getInitialState() or getDefaultProps() methods returning objects ' + - 'with clashing keys.' - ); - }); - - it('should not mutate objects returned by getInitialState()', function() { - var Mixin = { - getInitialState: function() { - return Object.freeze({mixin: true}); - } - }; - var Component = React.createClass({ - mixins: [Mixin], - getInitialState: function() { - return Object.freeze({component: true}); - }, - render: function() { - return ; - } - }); - expect(() => { - ReactTestUtils.renderIntoDocument(); - }).not.toThrow(); - }); - - it('should work with object getInitialState() return values', function() { - var Component = React.createClass({ - getInitialState: function() { - return { - occupation: 'clown' - }; - }, - render: function() { - return ; - } - }); - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.state.occupation).toEqual('clown'); - }); - - it('should throw with non-object getInitialState() return values', function() { - [['an array'], 'a string', 1234].forEach(function(state) { - var Component = React.createClass({ - getInitialState: function() { - return state; - }, - render: function() { - return ; - } - }); - var instance = ; - expect(function() { - instance = ReactTestUtils.renderIntoDocument(instance); - }).toThrow( - 'Invariant Violation: Component.getInitialState(): ' + - 'must return an object or null' - ); - }); - }); - - it('should work with a null getInitialState() return value', function() { - var Component = React.createClass({ - getInitialState: function() { - return null; - }, - render: function() { - return ; - } - }); - expect( - () => ReactTestUtils.renderIntoDocument() - ).not.toThrow(); - }); - - it('should work with a null getInitialState return value and a mixin', () => { - var Component; - var instance; - - var Mixin = { - getInitialState: function() { - return {foo: 'bar'}; - } - }; - Component = React.createClass({ - mixins: [Mixin], - getInitialState: function() { - return null; - }, - render: function() { - return ; - } - }); - expect( - () => ReactTestUtils.renderIntoDocument() - ).not.toThrow(); - - instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.state).toEqual({foo: 'bar'}); - - // Also the other way round should work - var Mixin2 = { - getInitialState: function() { - return null; - } - }; - Component = React.createClass({ - mixins: [Mixin2], - getInitialState: function() { - return {foo: 'bar'}; - }, - render: function() { - return ; - } - }); - expect( - () => ReactTestUtils.renderIntoDocument() - ).not.toThrow(); - - instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.state).toEqual({foo: 'bar'}); - - // Multiple mixins should be fine too - Component = React.createClass({ - mixins: [Mixin, Mixin2], - getInitialState: function() { - return {x: true}; - }, - render: function() { - return ; - } - }); - expect( - () => ReactTestUtils.renderIntoDocument() - ).not.toThrow(); - - instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.state).toEqual({foo: 'bar', x: true}); - }); - - it('should work with object getInitialState() return values', function() { - var Component = React.createClass({ - getInitialState: function() { - return { - occupation: 'clown' - }; - }, - render: function() { - return ; - } - }); - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.state.occupation).toEqual('clown'); - }); - - it('should throw with non-object getInitialState() return values', function() { - [['an array'], 'a string', 1234].forEach(function(state) { - var Component = React.createClass({ - getInitialState: function() { - return state; - }, - render: function() { - return ; - } - }); - var instance = ; - expect(function() { - instance = ReactTestUtils.renderIntoDocument(instance); - }).toThrow( - 'Invariant Violation: Component.getInitialState(): ' + - 'must return an object or null' - ); - }); - }); - it('should call componentWillUnmount before unmounting', function() { var container = document.createElement('div'); var innerUnmounted = false; @@ -866,88 +473,6 @@ describe('ReactCompositeComponent', function() { } }); - it('should warn when mispelling shouldComponentUpdate', function() { - var warn = console.warn; - console.warn = mocks.getMockFunction(); - - try { - React.createClass({ - componentShouldUpdate: function() { - return false; - }, - render: function() { - return
; - } - }); - expect(console.warn.mock.calls.length).toBe(1); - expect(console.warn.mock.calls[0][0]).toBe( - '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.' - ); - - var NamedComponent = React.createClass({ - componentShouldUpdate: function() { - return false; - }, - render: function() { - return
; - } - }); - expect(console.warn.mock.calls.length).toBe(2); - expect(console.warn.mock.calls[1][0]).toBe( - '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.' - ); - - ; // Shut up lint - } finally { - console.warn = warn; - } - }); - - xit('should warn when using deprecated non-static spec keys', function() { - var warn = console.warn; - console.warn = mocks.getMockFunction(); - try { - React.createClass({ - mixins: [{}], - propTypes: { - foo: ReactPropTypes.string - }, - contextTypes: { - foo: ReactPropTypes.string - }, - childContextTypes: { - foo: ReactPropTypes.string - }, - render: function() { - return
; - } - }); - expect(console.warn.mock.calls.length).toBe(4); - expect(console.warn.mock.calls[0][0]).toBe( - 'createClass(...): `mixins` is now a static property and should ' + - 'be defined inside "statics".' - ); - expect(console.warn.mock.calls[1][0]).toBe( - 'createClass(...): `propTypes` is now a static property and should ' + - 'be defined inside "statics".' - ); - expect(console.warn.mock.calls[2][0]).toBe( - 'createClass(...): `contextTypes` is now a static property and ' + - 'should be defined inside "statics".' - ); - expect(console.warn.mock.calls[3][0]).toBe( - 'createClass(...): `childContextTypes` is now a static property and ' + - 'should be defined inside "statics".' - ); - } finally { - console.warn = warn; - } - }); - it('should pass context', function() { var childInstance = null; var grandchildInstance = null; @@ -1074,413 +599,6 @@ describe('ReactCompositeComponent', function() { }); - it('should check context types', function() { - var Component = React.createClass({ - contextTypes: { - foo: ReactPropTypes.string.isRequired - }, - - render: function() { - return
; - } - }); - - ReactTestUtils.renderIntoDocument(); - - expect(console.warn.mock.calls.length).toBe(1); - expect(console.warn.mock.calls[0][0]).toBe( - 'Warning: Required context `foo` was not specified in `Component`.' - ); - - var ComponentInFooStringContext = React.createClass({ - childContextTypes: { - foo: ReactPropTypes.string - }, - - getChildContext: function() { - return { - foo: this.props.fooValue - }; - }, - - render: function() { - return ; - } - }); - - ReactTestUtils.renderIntoDocument(); - - // Previous call should not error - expect(console.warn.mock.calls.length).toBe(1); - - var ComponentInFooNumberContext = React.createClass({ - childContextTypes: { - foo: ReactPropTypes.number - }, - - getChildContext: function() { - return { - foo: this.props.fooValue - }; - }, - - render: function() { - return ; - } - }); - - ReactTestUtils.renderIntoDocument(); - - expect(console.warn.mock.calls.length).toBe(2); - expect(console.warn.mock.calls[1][0]).toBe( - 'Warning: Invalid context `foo` of type `number` supplied ' + - 'to `Component`, expected `string`.' + - ' Check the render method of `ComponentInFooNumberContext`.' - ); - }); - - it('should check child context types', function() { - var Component = React.createClass({ - childContextTypes: { - foo: ReactPropTypes.string.isRequired, - bar: ReactPropTypes.number - }, - - getChildContext: function() { - return this.props.testContext; - }, - - render: function() { - return
; - } - }); - - ReactTestUtils.renderIntoDocument(); - expect(console.warn.mock.calls.length).toBe(2); - expect(console.warn.mock.calls[0][0]).toBe( - 'Warning: Required child context `foo` was not specified in `Component`.' - ); - expect(console.warn.mock.calls[1][0]).toBe( - 'Warning: Required child context `foo` was not specified in `Component`.' - ); - - ReactTestUtils.renderIntoDocument(); - - expect(console.warn.mock.calls.length).toBe(4); - expect(console.warn.mock.calls[3][0]).toBe( - 'Warning: Invalid child context `foo` of type `number` ' + - 'supplied to `Component`, expected `string`.' - ); - - ReactTestUtils.renderIntoDocument( - - ); - - ReactTestUtils.renderIntoDocument( - - ); - - // Previous calls should not log errors - expect(console.warn.mock.calls.length).toBe(4); - }); - - it('should filter out context not in contextTypes', function() { - var Component = React.createClass({ - contextTypes: { - foo: ReactPropTypes.string - }, - - render: function() { - return
; - } - }); - - var ComponentInFooBarContext = React.createClass({ - childContextTypes: { - foo: ReactPropTypes.string, - bar: ReactPropTypes.number - }, - - getChildContext: function() { - return { - foo: 'abc', - bar: 123 - }; - }, - - render: function() { - return ; - } - }); - - var instance = ReactTestUtils.renderIntoDocument(); - reactComponentExpect(instance).expectRenderedChild().scalarContextEqual({foo: 'abc'}); - }); - - it('should filter context properly in callbacks', function() { - var actualComponentWillReceiveProps; - var actualShouldComponentUpdate; - var actualComponentWillUpdate; - var actualComponentDidUpdate; - - var Parent = React.createClass({ - childContextTypes: { - foo: ReactPropTypes.string.isRequired, - bar: ReactPropTypes.string.isRequired - }, - - getChildContext: function() { - return { - foo: this.props.foo, - bar: "bar" - }; - }, - - render: function() { - return ; - } - }); - - var Component = React.createClass({ - contextTypes: { - foo: ReactPropTypes.string - }, - - componentWillReceiveProps: function(nextProps, nextContext) { - actualComponentWillReceiveProps = nextContext; - return true; - }, - - shouldComponentUpdate: function(nextProps, nextState, nextContext) { - actualShouldComponentUpdate = nextContext; - return true; - }, - - componentWillUpdate: function(nextProps, nextState, nextContext) { - actualComponentWillUpdate = nextContext; - }, - - componentDidUpdate: function(prevProps, prevState, prevContext) { - actualComponentDidUpdate = prevContext; - }, - - render: function() { - return
; - } - }); - - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - instance.replaceProps({foo: "def"}); - expect(actualComponentWillReceiveProps).toEqual({foo: 'def'}); - expect(actualShouldComponentUpdate).toEqual({foo: 'def'}); - expect(actualComponentWillUpdate).toEqual({foo: 'def'}); - expect(actualComponentDidUpdate).toEqual({foo: 'abc'}); - }); - - it('should support statics', function() { - var Component = React.createClass({ - statics: { - abc: 'def', - def: 0, - ghi: null, - jkl: 'mno', - pqr: function() { - return this; - } - }, - - render: function() { - return ; - } - }); - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.constructor.abc).toBe('def'); - expect(Component.abc).toBe('def'); - expect(instance.constructor.def).toBe(0); - expect(Component.def).toBe(0); - expect(instance.constructor.ghi).toBe(null); - expect(Component.ghi).toBe(null); - expect(instance.constructor.jkl).toBe('mno'); - expect(Component.jkl).toBe('mno'); - expect(instance.constructor.pqr()).toBe(Component); - expect(Component.pqr()).toBe(Component); - }); - - it('should throw if a reserved property is in statics', function() { - expect(function() { - React.createClass({ - statics: { - getDefaultProps: function() { - return { - foo: 0 - }; - } - }, - - render: function() { - return ; - } - }); - }).toThrow( - 'Invariant Violation: 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.' - ); - }); - - it('should support statics in mixins', function() { - var Mixin = { - statics: { - foo: 'bar' - } - }; - var Component = React.createClass({ - mixins: [Mixin], - - statics: { - abc: 'def' - }, - - render: function() { - return ; - } - }); - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.constructor.foo).toBe('bar'); - expect(Component.foo).toBe('bar'); - expect(instance.constructor.abc).toBe('def'); - expect(Component.abc).toBe('def'); - }); - - it("should throw if mixins override each others' statics", function() { - expect(function() { - var Mixin = { - statics: { - abc: 'foo' - } - }; - React.createClass({ - mixins: [Mixin], - - statics: { - abc: 'bar' - }, - - render: function() { - return ; - } - }); - }).toThrow( - 'Invariant Violation: ReactClass: You are attempting to ' + - 'define `abc` on your component more than once. This conflict may be ' + - 'due to a mixin.' - ); - }); - - it("should throw if mixins override functions in statics", function() { - expect(function() { - var Mixin = { - statics: { - abc: function() { console.log('foo'); } - } - }; - React.createClass({ - mixins: [Mixin], - - statics: { - abc: function() { console.log('bar'); } - }, - - render: function() { - return ; - } - }); - }).toThrow( - 'Invariant Violation: ReactClass: You are attempting to ' + - 'define `abc` on your component more than once. This conflict may be ' + - 'due to a mixin.' - ); - }); - - it("should throw if the mixin is a React component", function() { - expect(function() { - React.createClass({ - mixins: [
], - - render: function() { - return ; - } - }); - }).toThrow( - 'Invariant Violation: ReactClass: You\'re attempting to ' + - 'use a component as a mixin. Instead, just use a regular object.' - ); - }); - - it("should throw if the mixin is a React component class", function() { - expect(function() { - var Component = React.createClass({ - render: function() { - return ; - } - }); - - React.createClass({ - mixins: [Component], - - render: function() { - return ; - } - }); - }).toThrow( - 'Invariant Violation: ReactClass: You\'re attempting to ' + - 'use a component class as a mixin. Instead, just use a regular object.' - ); - }); - - it('should have bound the mixin methods to the component', function() { - var mixin = { - mixinFunc: function() {return this;} - }; - - var Component = React.createClass({ - mixins: [mixin], - componentDidMount: function() { - expect(this.mixinFunc()).toBe(this); - }, - render: function() { - return ; - } - }); - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - }); - - it('should include the mixin keys in even if their values are falsy', - function() { - var mixin = { - keyWithNullValue: null, - randomCounter: 0 - }; - - var Component = React.createClass({ - mixins: [mixin], - componentDidMount: function() { - expect(this.randomCounter).toBe(0); - expect(this.keyWithNullValue).toBeNull(); - }, - render: function() { - return ; - } - }); - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - }); - it('should disallow nested render calls', function() { spyOn(console, 'warn'); var Inner = React.createClass({ diff --git a/src/core/__tests__/ReactCompositeComponentMixin-test.js b/src/core/__tests__/ReactCompositeComponentMixin-test.js deleted file mode 100644 index f76e657b76..0000000000 --- a/src/core/__tests__/ReactCompositeComponentMixin-test.js +++ /dev/null @@ -1,171 +0,0 @@ -/** - * Copyright 2013-2014, 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 mocks = require('mocks'); - -var React; -var ReactTestUtils; -var reactComponentExpect; - -var TestComponent; -var TestComponentWithPropTypes; -var TestComponentWithReverseSpec; -var mixinPropValidator; -var componentPropValidator; - -describe('ReactCompositeComponent-mixin', function() { - - beforeEach(function() { - React = require('React'); - ReactTestUtils = require('ReactTestUtils'); - reactComponentExpect = require('reactComponentExpect'); - mixinPropValidator = mocks.getMockFunction(); - componentPropValidator = mocks.getMockFunction(); - - var MixinA = { - propTypes: { - propA: function() {} - }, - componentDidMount: function() { - this.props.listener('MixinA didMount'); - } - }; - - var MixinB = { - mixins: [MixinA], - propTypes: { - propB: function() {} - }, - componentDidMount: function() { - this.props.listener('MixinB didMount'); - } - }; - - var MixinBWithReverseSpec = { - componentDidMount: function() { - this.props.listener('MixinBWithReverseSpec didMount'); - }, - mixins: [MixinA] - }; - - var MixinC = { - statics: { - staticC: function() {} - }, - componentDidMount: function() { - this.props.listener('MixinC didMount'); - } - }; - - var MixinD = { - propTypes: { - value: mixinPropValidator - } - }; - - TestComponent = React.createClass({ - mixins: [MixinB, MixinC, MixinD], - statics: { - staticComponent: function() {} - }, - propTypes: { - propComponent: function() {} - }, - componentDidMount: function() { - this.props.listener('Component didMount'); - }, - render: function() { - return
; - } - }); - - TestComponentWithReverseSpec = React.createClass({ - render: function() { - return
; - }, - componentDidMount: function() { - this.props.listener('Component didMount'); - }, - mixins: [MixinBWithReverseSpec, MixinC, MixinD] - }); - - TestComponentWithPropTypes = React.createClass({ - mixins: [MixinD], - propTypes: { - value: componentPropValidator - }, - render: function() { - return
; - } - }); - }); - - it('should support merging propTypes and statics', function() { - var listener = mocks.getMockFunction(); - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - - var instancePropTypes = instance.constructor.propTypes; - - expect('propA' in instancePropTypes).toBe(true); - expect('propB' in instancePropTypes).toBe(true); - expect('propComponent' in instancePropTypes).toBe(true); - - expect('staticC' in TestComponent).toBe(true); - expect('staticComponent' in TestComponent).toBe(true); - }); - - it('should support chaining delegate functions', function() { - var listener = mocks.getMockFunction(); - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - - expect(listener.mock.calls).toEqual([ - ['MixinA didMount'], - ['MixinB didMount'], - ['MixinC didMount'], - ['Component didMount'] - ]); - }); - - it('should chain functions regardless of spec property order', function() { - var listener = mocks.getMockFunction(); - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - - expect(listener.mock.calls).toEqual([ - ['MixinA didMount'], - ['MixinBWithReverseSpec didMount'], - ['MixinC didMount'], - ['Component didMount'] - ]); - }); - - it('should validate prop types via mixins', function() { - expect(TestComponent.propTypes).toBeDefined(); - expect(TestComponent.propTypes.value) - .toBe(mixinPropValidator); - }); - - it('should override mixin prop types with class prop types', function() { - // Sanity check... - expect(componentPropValidator).toNotBe(mixinPropValidator); - // Actually check... - expect(TestComponentWithPropTypes.propTypes) - .toBeDefined(); - expect(TestComponentWithPropTypes.propTypes.value) - .toNotBe(mixinPropValidator); - expect(TestComponentWithPropTypes.propTypes.value) - .toBe(componentPropValidator); - }); -}); diff --git a/src/core/__tests__/ReactCompositeComponentSpec-test.js b/src/core/__tests__/ReactCompositeComponentSpec-test.js deleted file mode 100644 index 63f5ff277e..0000000000 --- a/src/core/__tests__/ReactCompositeComponentSpec-test.js +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2013-2014, 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 mocks = require('mocks'); - -var React; -var ReactTestUtils; -var reactComponentExpect; - -describe('ReactCompositeComponent-spec', function() { - - beforeEach(function() { - React = require('React'); - ReactTestUtils = require('ReactTestUtils'); - reactComponentExpect = require('reactComponentExpect'); - }); - - it('should throw when `render` is not specified', function() { - expect(function() { - React.createClass({}); - }).toThrow( - 'Invariant Violation: createClass(...): Class specification must ' + - 'implement a `render` method.' - ); - }); - - it('should copy `displayName` onto the Constructor', function() { - var TestComponent = React.createClass({ - render: function() { - return
; - } - }); - - expect(TestComponent.displayName) - .toBe('TestComponent'); - }); - - it('should copy prop types onto the Constructor', function() { - var propValidator = mocks.getMockFunction(); - var TestComponent = React.createClass({ - propTypes: { - value: propValidator - }, - render: function() { - return
; - } - }); - - expect(TestComponent.propTypes).toBeDefined(); - expect(TestComponent.propTypes.value) - .toBe(propValidator); - }); -});