react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / src / core / __tests__ / ReactCompositeComponent-test.js
81155 views/**1* Copyright 2013-2014, Facebook, Inc.2* All rights reserved.3*4* This source code is licensed under the BSD-style license found in the5* LICENSE file in the root directory of this source tree. An additional grant6* of patent rights can be found in the PATENTS file in the same directory.7*8* @emails react-core9*/1011"use strict";1213var ChildUpdates;14var MorphingComponent;15var React;16var ReactComponent;17var ReactCurrentOwner;18var ReactDoNotBindDeprecated;19var ReactMount;20var ReactPropTypes;21var ReactServerRendering;22var ReactTestUtils;23var TogglingComponent;2425var cx;26var reactComponentExpect;27var mocks;28var warn;293031describe('ReactCompositeComponent', function() {3233beforeEach(function() {34cx = require('cx');35mocks = require('mocks');3637reactComponentExpect = require('reactComponentExpect');38React = require('React');39ReactComponent = require('ReactComponent');40ReactCurrentOwner = require('ReactCurrentOwner');41ReactDoNotBindDeprecated = require('ReactDoNotBindDeprecated');42ReactPropTypes = require('ReactPropTypes');43ReactTestUtils = require('ReactTestUtils');44ReactMount = require('ReactMount');45ReactServerRendering = require('ReactServerRendering');4647MorphingComponent = React.createClass({48getInitialState: function() {49return {activated: false};50},5152_toggleActivatedState: function() {53this.setState({activated: !this.state.activated});54},5556render: function() {57var toggleActivatedState = this._toggleActivatedState;58return !this.state.activated ?59<a ref="x" onClick={toggleActivatedState} /> :60<b ref="x" onClick={toggleActivatedState} />;61}62});6364/**65* We'll use this to ensure that an old version is not cached when it is66* reallocated again.67*/68ChildUpdates = React.createClass({69getAnchorID: function() {70return this.refs.anch._rootNodeID;71},72render: function() {73var className = cx({'anchorClass': this.props.anchorClassOn});74return this.props.renderAnchor ?75<a ref="anch" className={className}></a> :76<b></b>;77}78});7980TogglingComponent = React.createClass({81getInitialState: function() {82return {component: this.props.firstComponent};83},84componentDidMount: function() {85console.log(this.getDOMNode());86this.setState({component: this.props.secondComponent});87},88componentDidUpdate: function() {89console.log(this.getDOMNode());90},91render: function() {92var Component = this.state.component;93return Component ? <Component /> : null;94}95});9697warn = console.warn;98console.warn = mocks.getMockFunction();99});100101afterEach(function() {102console.warn = warn;103});104105it('should give context for PropType errors in nested components.', () => {106// In this test, we're making sure that if a proptype error is found in a107// component, we give a small hint as to which parent instantiated that108// component as per warnings about key usage in ReactElementValidator.109spyOn(console, 'warn');110var MyComp = React.createClass({111propTypes: {112color: ReactPropTypes.string113},114render: function() {115return <div>My color is {this.color}</div>;116}117});118var ParentComp = React.createClass({119render: function() {120return <MyComp color={123} />;121}122});123ReactTestUtils.renderIntoDocument(<ParentComp />);124expect(console.warn.calls[0].args[0]).toBe(125'Warning: Invalid prop `color` of type `number` supplied to `MyComp`, ' +126'expected `string`. Check the render method of `ParentComp`.'127);128});129130it('should support rendering to different child types over time', function() {131var instance = <MorphingComponent />;132instance = ReactTestUtils.renderIntoDocument(instance);133134reactComponentExpect(instance)135.expectRenderedChild()136.toBeDOMComponentWithTag('a');137138instance._toggleActivatedState();139reactComponentExpect(instance)140.expectRenderedChild()141.toBeDOMComponentWithTag('b');142143instance._toggleActivatedState();144reactComponentExpect(instance)145.expectRenderedChild()146.toBeDOMComponentWithTag('a');147});148149it('should render null and false as a noscript tag under the hood', () => {150var Component1 = React.createClass({151render: function() {152return null;153}154});155var Component2 = React.createClass({156render: function() {157return false;158}159});160161var instance1 = ReactTestUtils.renderIntoDocument(<Component1 />);162var instance2 = ReactTestUtils.renderIntoDocument(<Component2 />);163reactComponentExpect(instance1)164.expectRenderedChild()165.toBeDOMComponentWithTag('noscript');166reactComponentExpect(instance2)167.expectRenderedChild()168.toBeDOMComponentWithTag('noscript');169});170171it('should still throw when rendering to undefined', () => {172var Component = React.createClass({173render: function() {}174});175expect(function() {176ReactTestUtils.renderIntoDocument(<Component />);177}).toThrow(178'Invariant Violation: Component.render(): A valid ReactComponent must ' +179'be returned. You may have returned undefined, an array or some other ' +180'invalid object.'181);182});183184it('should be able to switch between rendering null and a normal tag', () => {185spyOn(console, 'log');186187var instance1 =188<TogglingComponent189firstComponent={null}190secondComponent={'div'}191/>;192var instance2 =193<TogglingComponent194firstComponent={'div'}195secondComponent={null}196/>;197198expect(function() {199ReactTestUtils.renderIntoDocument(instance1);200ReactTestUtils.renderIntoDocument(instance2);201}).not.toThrow();202203expect(console.log.argsForCall.length).toBe(4);204expect(console.log.argsForCall[0][0]).toBe(null);205expect(console.log.argsForCall[1][0].tagName).toBe('DIV');206expect(console.log.argsForCall[2][0].tagName).toBe('DIV');207expect(console.log.argsForCall[3][0]).toBe(null);208});209210it('should distinguish between a script placeholder and an actual script tag',211() => {212spyOn(console, 'log');213214var instance1 =215<TogglingComponent216firstComponent={null}217secondComponent={'script'}218/>;219var instance2 =220<TogglingComponent221firstComponent={'script'}222secondComponent={null}223/>;224225expect(function() {226ReactTestUtils.renderIntoDocument(instance1);227}).not.toThrow();228expect(function() {229ReactTestUtils.renderIntoDocument(instance2);230}).not.toThrow();231232expect(console.log.argsForCall.length).toBe(4);233expect(console.log.argsForCall[0][0]).toBe(null);234expect(console.log.argsForCall[1][0].tagName).toBe('SCRIPT');235expect(console.log.argsForCall[2][0].tagName).toBe('SCRIPT');236expect(console.log.argsForCall[3][0]).toBe(null);237}238);239240it('should have getDOMNode return null when multiple layers of composite ' +241'components render to the same null placeholder', () => {242spyOn(console, 'log');243244var GrandChild = React.createClass({245render: function() {246return null;247}248});249250var Child = React.createClass({251render: function() {252return <GrandChild />;253}254});255256var instance1 =257<TogglingComponent258firstComponent={'div'}259secondComponent={Child}260/>;261var instance2 =262<TogglingComponent263firstComponent={Child}264secondComponent={'div'}265/>;266267expect(function() {268ReactTestUtils.renderIntoDocument(instance1);269}).not.toThrow();270expect(function() {271ReactTestUtils.renderIntoDocument(instance2);272}).not.toThrow();273274expect(console.log.argsForCall.length).toBe(4);275expect(console.log.argsForCall[0][0].tagName).toBe('DIV');276expect(console.log.argsForCall[1][0]).toBe(null);277expect(console.log.argsForCall[2][0]).toBe(null);278expect(console.log.argsForCall[3][0].tagName).toBe('DIV');279}280);281282it('should not thrash a server rendered layout with client side one', () => {283var Child = React.createClass({284render: function() {285return null;286}287});288var Parent = React.createClass({289render: function() {290return <div><Child /></div>;291}292});293294var markup = ReactServerRendering.renderToString(<Parent />);295var container = document.createElement('div');296container.innerHTML = markup;297298spyOn(console, 'warn');299React.render(<Parent />, container);300expect(console.warn).not.toHaveBeenCalled();301});302303it('should react to state changes from callbacks', function() {304var instance = <MorphingComponent />;305instance = ReactTestUtils.renderIntoDocument(instance);306307var renderedChild = reactComponentExpect(instance)308.expectRenderedChild()309.instance();310311ReactTestUtils.Simulate.click(renderedChild);312reactComponentExpect(instance)313.expectRenderedChild()314.toBeDOMComponentWithTag('b');315});316317it('should rewire refs when rendering to different child types', function() {318var instance = <MorphingComponent />;319instance = ReactTestUtils.renderIntoDocument(instance);320321reactComponentExpect(instance.refs.x).toBeDOMComponentWithTag('a');322instance._toggleActivatedState();323reactComponentExpect(instance.refs.x).toBeDOMComponentWithTag('b');324instance._toggleActivatedState();325reactComponentExpect(instance.refs.x).toBeDOMComponentWithTag('a');326});327328it('should not cache old DOM nodes when switching constructors', function() {329var instance = <ChildUpdates renderAnchor={true} anchorClassOn={false}/>;330instance = ReactTestUtils.renderIntoDocument(instance);331instance.setProps({anchorClassOn: true}); // Warm any cache332instance.setProps({renderAnchor: false}); // Clear out the anchor333// rerender334instance.setProps({renderAnchor: true, anchorClassOn: false});335var anchorID = instance.getAnchorID();336var actualDOMAnchorNode = ReactMount.getNode(anchorID);337expect(actualDOMAnchorNode.className).toBe('');338});339340it('should auto bind methods and values correctly', function() {341spyOn(console, 'warn');342343var ComponentClass = React.createClass({344getInitialState: function() {345return {valueToReturn: 'hi'};346},347methodToBeExplicitlyBound: function() {348return this;349},350methodAutoBound: function() {351return this;352},353methodExplicitlyNotBound: ReactDoNotBindDeprecated.doNotBind(function() {354return this;355}),356render: function() {357return <div></div>;358}359});360var instance = <ComponentClass />;361362// Next, prove that once mounted, the scope is bound correctly to the actual363// component.364var mountedInstance = ReactTestUtils.renderIntoDocument(instance);365366expect(function() {367mountedInstance.methodToBeExplicitlyBound.bind(instance)();368}).not.toThrow();369expect(function() {370mountedInstance.methodAutoBound();371}).not.toThrow();372expect(function() {373mountedInstance.methodExplicitlyNotBound();374}).not.toThrow();375376expect(console.warn.argsForCall.length).toBe(1);377var explicitlyBound = mountedInstance.methodToBeExplicitlyBound.bind(378mountedInstance379);380expect(console.warn.argsForCall.length).toBe(2);381var autoBound = mountedInstance.methodAutoBound;382var explicitlyNotBound = mountedInstance.methodExplicitlyNotBound;383384var context = {};385expect(explicitlyBound.call(context)).toBe(mountedInstance);386expect(autoBound.call(context)).toBe(mountedInstance);387expect(explicitlyNotBound.call(context)).toBe(context);388389expect(explicitlyBound.call(mountedInstance)).toBe(mountedInstance);390expect(autoBound.call(mountedInstance)).toBe(mountedInstance);391// This one is the weird one392expect(explicitlyNotBound.call(mountedInstance)).toBe(mountedInstance);393394});395396it('should not pass this to getDefaultProps', function() {397var Component = React.createClass({398getDefaultProps: function() {399expect(this.render).not.toBeDefined();400return {};401},402render: function() {403return <div />;404}405});406ReactTestUtils.renderIntoDocument(<Component />);407});408409it('should use default values for undefined props', function() {410var Component = React.createClass({411getDefaultProps: function() {412return {prop: 'testKey'};413},414render: function() {415return <span />;416}417});418419var instance1 = <Component />;420instance1 = ReactTestUtils.renderIntoDocument(instance1);421reactComponentExpect(instance1).scalarPropsEqual({prop: 'testKey'});422423var instance2 = <Component prop={undefined} />;424instance2 = ReactTestUtils.renderIntoDocument(instance2);425reactComponentExpect(instance2).scalarPropsEqual({prop: 'testKey'});426427var instance3 = <Component prop={null} />;428instance3 = ReactTestUtils.renderIntoDocument(instance3);429reactComponentExpect(instance3).scalarPropsEqual({prop: null});430});431432it('should not mutate passed-in props object', function() {433var Component = React.createClass({434getDefaultProps: function() {435return {prop: 'testKey'};436},437render: function() {438return <span />;439}440});441442var inputProps = {};443var instance1 = <Component {...inputProps} />;444instance1 = ReactTestUtils.renderIntoDocument(instance1);445expect(instance1.props.prop).toBe('testKey');446447// We don't mutate the input, just in case the caller wants to do something448// with it after using it to instantiate a component449expect(inputProps.prop).not.toBeDefined();450});451452it('should use default prop value when removing a prop', function() {453var Component = React.createClass({454getDefaultProps: function() {455return {fruit: 'persimmon'};456},457render: function() {458return <span />;459}460});461462var container = document.createElement('div');463var instance = React.render(464<Component fruit="mango" />,465container466);467expect(instance.props.fruit).toBe('mango');468469React.render(<Component />, container);470expect(instance.props.fruit).toBe('persimmon');471});472473it('should normalize props with default values', function() {474var Component = React.createClass({475propTypes: {prop: ReactPropTypes.string.isRequired},476getDefaultProps: function() {477return {prop: 'testKey'};478},479getInitialState: function() {480return {prop: this.props.prop + 'State'};481},482render: function() {483return <span>{this.props.prop}</span>;484}485});486487var instance = ReactTestUtils.renderIntoDocument(<Component />);488reactComponentExpect(instance).scalarPropsEqual({prop: 'testKey'});489reactComponentExpect(instance).scalarStateEqual({prop: 'testKeyState'});490491ReactTestUtils.renderIntoDocument(<Component prop={null} />);492493expect(console.warn.mock.calls.length).toBe(1);494expect(console.warn.mock.calls[0][0]).toBe(495'Warning: Required prop `prop` was not specified in `Component`.'496);497});498499it('should check default prop values', function() {500var Component = React.createClass({501propTypes: {prop: ReactPropTypes.string.isRequired},502getDefaultProps: function() {503return {prop: null};504},505render: function() {506return <span>{this.props.prop}</span>;507}508});509510ReactTestUtils.renderIntoDocument(<Component />);511512expect(console.warn.mock.calls.length).toBe(1);513expect(console.warn.mock.calls[0][0]).toBe(514'Warning: Required prop `prop` was not specified in `Component`.'515);516});517518it('should check declared prop types', function() {519var Component = React.createClass({520propTypes: {521prop: ReactPropTypes.string.isRequired522},523render: function() {524return <span>{this.props.prop}</span>;525}526});527528ReactTestUtils.renderIntoDocument(<Component />);529ReactTestUtils.renderIntoDocument(<Component prop={42} />);530531expect(console.warn.mock.calls.length).toBe(2);532expect(console.warn.mock.calls[0][0]).toBe(533'Warning: Required prop `prop` was not specified in `Component`.'534);535536expect(console.warn.mock.calls[1][0]).toBe(537'Warning: Invalid prop `prop` of type `number` supplied to ' +538'`Component`, expected `string`.'539);540541ReactTestUtils.renderIntoDocument(<Component prop="string" />);542543// Should not error for strings544expect(console.warn.mock.calls.length).toBe(2);545});546547it('should throw on invalid prop types', function() {548expect(function() {549React.createClass({550displayName: 'Component',551propTypes: {552prop: null553},554render: function() {555return <span>{this.props.prop}</span>;556}557});558}).toThrow(559'Invariant Violation: Component: prop type `prop` is invalid; ' +560'it must be a function, usually from React.PropTypes.'561);562});563564it('should throw on invalid context types', function() {565expect(function() {566React.createClass({567displayName: 'Component',568contextTypes: {569prop: null570},571render: function() {572return <span>{this.props.prop}</span>;573}574});575}).toThrow(576'Invariant Violation: Component: context type `prop` is invalid; ' +577'it must be a function, usually from React.PropTypes.'578);579});580581it('should throw on invalid child context types', function() {582expect(function() {583React.createClass({584displayName: 'Component',585childContextTypes: {586prop: null587},588render: function() {589return <span>{this.props.prop}</span>;590}591});592}).toThrow(593'Invariant Violation: Component: child context type `prop` is invalid; ' +594'it must be a function, usually from React.PropTypes.'595);596});597598it('should not allow `forceUpdate` on unmounted components', function() {599var container = document.createElement('div');600document.documentElement.appendChild(container);601602var Component = React.createClass({603render: function() {604return <div />;605}606});607608var instance = <Component />;609expect(instance.forceUpdate).not.toBeDefined();610611instance = React.render(instance, container);612expect(function() {613instance.forceUpdate();614}).not.toThrow();615616React.unmountComponentAtNode(container);617expect(function() {618instance.forceUpdate();619}).toThrow(620'Invariant Violation: forceUpdate(...): Can only force an update on ' +621'mounted or mounting components.'622);623});624625it('should cleanup even if render() fatals', function() {626var BadComponent = React.createClass({627render: function() {628throw new Error();629}630});631var instance = <BadComponent />;632633expect(ReactCurrentOwner.current).toBe(null);634635expect(function() {636instance = ReactTestUtils.renderIntoDocument(instance);637}).toThrow();638639expect(ReactCurrentOwner.current).toBe(null);640});641642it('should support mixins with getInitialState()', function() {643var Mixin = {644getInitialState: function() {645return {mixin: true};646}647};648var Component = React.createClass({649mixins: [Mixin],650getInitialState: function() {651return {component: true};652},653render: function() {654return <span />;655}656});657var instance = <Component />;658instance = ReactTestUtils.renderIntoDocument(instance);659expect(instance.state.component).toBe(true);660expect(instance.state.mixin).toBe(true);661});662663it('should throw with conflicting getInitialState() methods', function() {664var Mixin = {665getInitialState: function() {666return {x: true};667}668};669var Component = React.createClass({670mixins: [Mixin],671getInitialState: function() {672return {x: true};673},674render: function() {675return <span />;676}677});678var instance = <Component />;679expect(function() {680instance = ReactTestUtils.renderIntoDocument(instance);681}).toThrow(682'Invariant Violation: mergeObjectsWithNoDuplicateKeys(): ' +683'Tried to merge two objects with the same key: `x`. This conflict ' +684'may be due to a mixin; in particular, this may be caused by two ' +685'getInitialState() or getDefaultProps() methods returning objects ' +686'with clashing keys.'687);688});689690it('should work with object getInitialState() return values', function() {691var Component = React.createClass({692getInitialState: function() {693return {694occupation: 'clown'695};696},697render: function() {698return <span />;699}700});701var instance = <Component />;702instance = ReactTestUtils.renderIntoDocument(instance);703expect(instance.state.occupation).toEqual('clown');704});705706it('should throw with non-object getInitialState() return values', function() {707[['an array'], 'a string', 1234].forEach(function(state) {708var Component = React.createClass({709getInitialState: function() {710return state;711},712render: function() {713return <span />;714}715});716var instance = <Component />;717expect(function() {718instance = ReactTestUtils.renderIntoDocument(instance);719}).toThrow(720'Invariant Violation: Component.getInitialState(): ' +721'must return an object or null'722);723});724});725726it('should work with a null getInitialState() return value', function() {727var Component = React.createClass({728getInitialState: function() {729return null;730},731render: function() {732return <span />;733}734});735expect(736() => ReactTestUtils.renderIntoDocument(<Component />)737).not.toThrow();738});739740it('should work with a null getInitialState return value and a mixin', () => {741var Component;742var instance;743744var Mixin = {745getInitialState: function() {746return {foo: 'bar'};747}748};749Component = React.createClass({750mixins: [Mixin],751getInitialState: function() {752return null;753},754render: function() {755return <span />;756}757});758expect(759() => ReactTestUtils.renderIntoDocument(<Component />)760).not.toThrow();761762instance = <Component />;763instance = ReactTestUtils.renderIntoDocument(instance);764expect(instance.state).toEqual({foo: 'bar'});765766// Also the other way round should work767var Mixin2 = {768getInitialState: function() {769return null;770}771};772Component = React.createClass({773mixins: [Mixin2],774getInitialState: function() {775return {foo: 'bar'};776},777render: function() {778return <span />;779}780});781expect(782() => ReactTestUtils.renderIntoDocument(<Component />)783).not.toThrow();784785instance = <Component />;786instance = ReactTestUtils.renderIntoDocument(instance);787expect(instance.state).toEqual({foo: 'bar'});788789// Multiple mixins should be fine too790Component = React.createClass({791mixins: [Mixin, Mixin2],792getInitialState: function() {793return {x: true};794},795render: function() {796return <span />;797}798});799expect(800() => ReactTestUtils.renderIntoDocument(<Component />)801).not.toThrow();802803instance = <Component />;804instance = ReactTestUtils.renderIntoDocument(instance);805expect(instance.state).toEqual({foo: 'bar', x: true});806});807808it('should work with object getInitialState() return values', function() {809var Component = React.createClass({810getInitialState: function() {811return {812occupation: 'clown'813};814},815render: function() {816return <span />;817}818});819var instance = <Component />;820instance = ReactTestUtils.renderIntoDocument(instance);821expect(instance.state.occupation).toEqual('clown');822});823824it('should throw with non-object getInitialState() return values', function() {825[['an array'], 'a string', 1234].forEach(function(state) {826var Component = React.createClass({827getInitialState: function() {828return state;829},830render: function() {831return <span />;832}833});834var instance = <Component />;835expect(function() {836instance = ReactTestUtils.renderIntoDocument(instance);837}).toThrow(838'Invariant Violation: Component.getInitialState(): ' +839'must return an object or null'840);841});842});843844it('should call componentWillUnmount before unmounting', function() {845var container = document.createElement('div');846var innerUnmounted = false;847848spyOn(ReactMount, 'purgeID').andCallThrough();849850var Component = React.createClass({851render: function() {852return <div>853<Inner />854</div>;855}856});857var Inner = React.createClass({858componentWillUnmount: function() {859// It's important that ReactMount.purgeID be called after any component860// lifecycle methods, because a componentWillMount implementation is861// likely call this.getDOMNode(), which will repopulate the node cache862// after it's been cleared, causing a memory leak.863expect(ReactMount.purgeID.callCount).toBe(0);864innerUnmounted = true;865},866render: function() {867return <div />;868}869});870871React.render(<Component />, container);872React.unmountComponentAtNode(container);873expect(innerUnmounted).toBe(true);874875// <Component />, <Inner />, and both <div /> elements each call876// unmountIDFromEnvironment which calls purgeID, for a total of 4.877expect(ReactMount.purgeID.callCount).toBe(4);878});879880it('should warn but detect valid CompositeComponent classes', function() {881var warn = console.warn;882console.warn = mocks.getMockFunction();883884var Component = React.createClass({885render: function() {886return <div/>;887}888});889890expect(React.isValidClass(Component)).toBe(true);891892expect(console.warn.mock.calls.length).toBe(1);893expect(console.warn.mock.calls[0][0]).toContain(894'isValidClass is deprecated and will be removed in a future release'895);896});897898it('should warn but detect invalid CompositeComponent classes', function() {899var warn = console.warn;900console.warn = mocks.getMockFunction();901902var FnComponent = function() {903return false;904};905906var NullComponent = null;907908var TrickFnComponent = function() {909return true;910};911TrickFnComponent.componentConstructor = true;912913expect(React.isValidClass(FnComponent)).toBe(false);914expect(React.isValidClass(NullComponent)).toBe(false);915expect(React.isValidClass(TrickFnComponent)).toBe(false);916917expect(console.warn.mock.calls.length).toBe(3);918console.warn.mock.calls.forEach(function(call) {919expect(call[0]).toContain(920'isValidClass is deprecated and will be removed in a future release'921);922});923});924925it('should warn when shouldComponentUpdate() returns undefined', function() {926var warn = console.warn;927console.warn = mocks.getMockFunction();928929try {930var Component = React.createClass({931getInitialState: function () {932return {bogus: false};933},934935shouldComponentUpdate: function() {936return undefined;937},938939render: function() {940return <div />;941}942});943944var instance = ReactTestUtils.renderIntoDocument(<Component />);945instance.setState({bogus: true});946947expect(console.warn.mock.calls.length).toBe(1);948expect(console.warn.mock.calls[0][0]).toBe(949'Component.shouldComponentUpdate(): Returned undefined instead of a ' +950'boolean value. Make sure to return true or false.'951);952} finally {953console.warn = warn;954}955});956957it('should warn when mispelling shouldComponentUpdate', function() {958var warn = console.warn;959console.warn = mocks.getMockFunction();960961try {962React.createClass({963componentShouldUpdate: function() {964return false;965},966render: function() {967return <div />;968}969});970expect(console.warn.mock.calls.length).toBe(1);971expect(console.warn.mock.calls[0][0]).toBe(972'A component has a method called componentShouldUpdate(). Did you ' +973'mean shouldComponentUpdate()? The name is phrased as a question ' +974'because the function is expected to return a value.'975);976977var NamedComponent = React.createClass({978componentShouldUpdate: function() {979return false;980},981render: function() {982return <div />;983}984});985expect(console.warn.mock.calls.length).toBe(2);986expect(console.warn.mock.calls[1][0]).toBe(987'NamedComponent has a method called componentShouldUpdate(). Did you ' +988'mean shouldComponentUpdate()? The name is phrased as a question ' +989'because the function is expected to return a value.'990);991992<NamedComponent />; // Shut up lint993} finally {994console.warn = warn;995}996});997998xit('should warn when using deprecated non-static spec keys', function() {999var warn = console.warn;1000console.warn = mocks.getMockFunction();1001try {1002React.createClass({1003mixins: [{}],1004propTypes: {1005foo: ReactPropTypes.string1006},1007contextTypes: {1008foo: ReactPropTypes.string1009},1010childContextTypes: {1011foo: ReactPropTypes.string1012},1013render: function() {1014return <div />;1015}1016});1017expect(console.warn.mock.calls.length).toBe(4);1018expect(console.warn.mock.calls[0][0]).toBe(1019'createClass(...): `mixins` is now a static property and should ' +1020'be defined inside "statics".'1021);1022expect(console.warn.mock.calls[1][0]).toBe(1023'createClass(...): `propTypes` is now a static property and should ' +1024'be defined inside "statics".'1025);1026expect(console.warn.mock.calls[2][0]).toBe(1027'createClass(...): `contextTypes` is now a static property and ' +1028'should be defined inside "statics".'1029);1030expect(console.warn.mock.calls[3][0]).toBe(1031'createClass(...): `childContextTypes` is now a static property and ' +1032'should be defined inside "statics".'1033);1034} finally {1035console.warn = warn;1036}1037});10381039it('should pass context', function() {1040var childInstance = null;1041var grandchildInstance = null;10421043var Parent = React.createClass({1044childContextTypes: {1045foo: ReactPropTypes.string,1046depth: ReactPropTypes.number1047},10481049getChildContext: function() {1050return {1051foo: 'bar',1052depth: 01053};1054},10551056render: function() {1057return <Child />;1058}1059});10601061var Child = React.createClass({1062contextTypes: {1063foo: ReactPropTypes.string,1064depth: ReactPropTypes.number1065},10661067childContextTypes: {1068depth: ReactPropTypes.number1069},10701071getChildContext: function() {1072return {1073depth: this.context.depth + 11074};1075},10761077render: function() {1078childInstance = this;1079return <Grandchild />;1080}1081});10821083var Grandchild = React.createClass({1084contextTypes: {1085foo: ReactPropTypes.string,1086depth: ReactPropTypes.number1087},10881089render: function() {1090grandchildInstance = this;1091return <div />;1092}1093});10941095ReactTestUtils.renderIntoDocument(<Parent />);1096reactComponentExpect(childInstance).scalarContextEqual({foo: 'bar', depth: 0});1097reactComponentExpect(grandchildInstance).scalarContextEqual({foo: 'bar', depth: 1});1098});10991100it('should check context types', function() {1101var Component = React.createClass({1102contextTypes: {1103foo: ReactPropTypes.string.isRequired1104},11051106render: function() {1107return <div />;1108}1109});11101111ReactTestUtils.renderIntoDocument(<Component />);11121113expect(console.warn.mock.calls.length).toBe(1);1114expect(console.warn.mock.calls[0][0]).toBe(1115'Warning: Required context `foo` was not specified in `Component`.'1116);11171118React.withContext({foo: 'bar'}, function() {1119ReactTestUtils.renderIntoDocument(<Component />);1120});11211122// Previous call should not error1123expect(console.warn.mock.calls.length).toBe(1);11241125React.withContext({foo: 123}, function() {1126ReactTestUtils.renderIntoDocument(<Component />);1127});11281129expect(console.warn.mock.calls.length).toBe(2);1130expect(console.warn.mock.calls[1][0]).toBe(1131'Warning: Invalid context `foo` of type `number` supplied ' +1132'to `Component`, expected `string`.'1133);1134});11351136it('should check child context types', function() {1137var Component = React.createClass({1138childContextTypes: {1139foo: ReactPropTypes.string.isRequired,1140bar: ReactPropTypes.number1141},11421143getChildContext: function() {1144return this.props.testContext;1145},11461147render: function() {1148return <div />;1149}1150});11511152ReactTestUtils.renderIntoDocument(<Component testContext={{bar: 123}} />);11531154expect(console.warn.mock.calls.length).toBe(1);1155expect(console.warn.mock.calls[0][0]).toBe(1156'Warning: Required child context `foo` was not specified in `Component`.'1157);11581159ReactTestUtils.renderIntoDocument(<Component testContext={{foo: 123}} />);11601161expect(console.warn.mock.calls.length).toBe(2);1162expect(console.warn.mock.calls[1][0]).toBe(1163'Warning: Invalid child context `foo` of type `number` ' +1164'supplied to `Component`, expected `string`.'1165);11661167ReactTestUtils.renderIntoDocument(1168<Component testContext={{foo: 'foo', bar: 123}} />1169);11701171ReactTestUtils.renderIntoDocument(1172<Component testContext={{foo: 'foo'}} />1173);11741175// Previous calls should not log errors1176expect(console.warn.mock.calls.length).toBe(2);1177});11781179it('should filter out context not in contextTypes', function() {1180var Component = React.createClass({1181contextTypes: {1182foo: ReactPropTypes.string1183},11841185render: function() {1186return <div />;1187}1188});11891190var instance = React.withContext({foo: 'abc', bar: 123}, function() {1191return <Component />;1192});1193instance = ReactTestUtils.renderIntoDocument(instance);1194reactComponentExpect(instance).scalarContextEqual({foo: 'abc'});1195});11961197it('should filter context properly in callbacks', function() {1198var actualComponentWillReceiveProps;1199var actualShouldComponentUpdate;1200var actualComponentWillUpdate;1201var actualComponentDidUpdate;12021203var Parent = React.createClass({1204childContextTypes: {1205foo: ReactPropTypes.string.isRequired,1206bar: ReactPropTypes.string.isRequired1207},12081209getChildContext: function() {1210return {1211foo: this.props.foo,1212bar: "bar"1213};1214},12151216render: function() {1217return <Component />;1218}1219});12201221var Component = React.createClass({1222contextTypes: {1223foo: ReactPropTypes.string1224},12251226componentWillReceiveProps: function(nextProps, nextContext) {1227actualComponentWillReceiveProps = nextContext;1228return true;1229},12301231shouldComponentUpdate: function(nextProps, nextState, nextContext) {1232actualShouldComponentUpdate = nextContext;1233return true;1234},12351236componentWillUpdate: function(nextProps, nextState, nextContext) {1237actualComponentWillUpdate = nextContext;1238},12391240componentDidUpdate: function(prevProps, prevState, prevContext) {1241actualComponentDidUpdate = prevContext;1242},12431244render: function() {1245return <div />;1246}1247});12481249var instance = <Parent foo="abc" />;1250instance = ReactTestUtils.renderIntoDocument(instance);1251instance.replaceProps({foo: "def"});1252expect(actualComponentWillReceiveProps).toEqual({foo: 'def'});1253expect(actualShouldComponentUpdate).toEqual({foo: 'def'});1254expect(actualComponentWillUpdate).toEqual({foo: 'def'});1255expect(actualComponentDidUpdate).toEqual({foo: 'abc'});1256});12571258it('should support statics', function() {1259var Component = React.createClass({1260statics: {1261abc: 'def',1262def: 0,1263ghi: null,1264jkl: 'mno',1265pqr: function() {1266return this;1267}1268},12691270render: function() {1271return <span />;1272}1273});1274var instance = <Component />;1275instance = ReactTestUtils.renderIntoDocument(instance);1276expect(instance.constructor.abc).toBe('def');1277expect(Component.abc).toBe('def');1278expect(instance.constructor.def).toBe(0);1279expect(Component.def).toBe(0);1280expect(instance.constructor.ghi).toBe(null);1281expect(Component.ghi).toBe(null);1282expect(instance.constructor.jkl).toBe('mno');1283expect(Component.jkl).toBe('mno');1284expect(instance.constructor.pqr()).toBe(Component.type);1285expect(Component.pqr()).toBe(Component.type);1286});12871288it('should throw if a reserved property is in statics', function() {1289expect(function() {1290React.createClass({1291statics: {1292getDefaultProps: function() {1293return {1294foo: 01295};1296}1297},12981299render: function() {1300return <span />;1301}1302});1303}).toThrow(1304'Invariant Violation: ReactCompositeComponent: You are attempting to ' +1305'define a reserved property, `getDefaultProps`, that shouldn\'t be on ' +1306'the "statics" key. Define it as an instance property instead; it ' +1307'will still be accessible on the constructor.'1308);1309});13101311it('should support statics in mixins', function() {1312var Mixin = {1313statics: {1314foo: 'bar'1315}1316};1317var Component = React.createClass({1318mixins: [Mixin],13191320statics: {1321abc: 'def'1322},13231324render: function() {1325return <span />;1326}1327});1328var instance = <Component />;1329instance = ReactTestUtils.renderIntoDocument(instance);1330expect(instance.constructor.foo).toBe('bar');1331expect(Component.foo).toBe('bar');1332expect(instance.constructor.abc).toBe('def');1333expect(Component.abc).toBe('def');1334});13351336it("should throw if mixins override each others' statics", function() {1337expect(function() {1338var Mixin = {1339statics: {1340abc: 'foo'1341}1342};1343React.createClass({1344mixins: [Mixin],13451346statics: {1347abc: 'bar'1348},13491350render: function() {1351return <span />;1352}1353});1354}).toThrow(1355'Invariant Violation: ReactCompositeComponent: You are attempting to ' +1356'define `abc` on your component more than once. This conflict may be ' +1357'due to a mixin.'1358);1359});13601361it("should throw if mixins override functions in statics", function() {1362expect(function() {1363var Mixin = {1364statics: {1365abc: function() { console.log('foo'); }1366}1367};1368React.createClass({1369mixins: [Mixin],13701371statics: {1372abc: function() { console.log('bar'); }1373},13741375render: function() {1376return <span />;1377}1378});1379}).toThrow(1380'Invariant Violation: ReactCompositeComponent: You are attempting to ' +1381'define `abc` on your component more than once. This conflict may be ' +1382'due to a mixin.'1383);1384});13851386it("should throw if the mixin is a React component", function() {1387expect(function() {1388React.createClass({1389mixins: [<div />],13901391render: function() {1392return <span />;1393}1394});1395}).toThrow(1396'Invariant Violation: ReactCompositeComponent: You\'re attempting to ' +1397'use a component as a mixin. Instead, just use a regular object.'1398);1399});14001401it("should throw if the mixin is a React component class", function() {1402expect(function() {1403var Component = React.createClass({1404render: function() {1405return <span />;1406}1407});14081409React.createClass({1410mixins: [Component],14111412render: function() {1413return <span />;1414}1415});1416}).toThrow(1417'Invariant Violation: ReactCompositeComponent: You\'re attempting to ' +1418'use a component class as a mixin. Instead, just use a regular object.'1419);1420});14211422it('should have bound the mixin methods to the component', function() {1423var mixin = {1424mixinFunc: function() {return this;}1425};14261427var Component = React.createClass({1428mixins: [mixin],1429componentDidMount: function() {1430expect(this.mixinFunc()).toBe(this);1431},1432render: function() {1433return <span />;1434}1435});1436var instance = <Component />;1437instance = ReactTestUtils.renderIntoDocument(instance);1438});14391440it('should include the mixin keys in even if their values are falsy',1441function() {1442var mixin = {1443keyWithNullValue: null,1444randomCounter: 01445};14461447var Component = React.createClass({1448mixins: [mixin],1449componentDidMount: function() {1450expect(this.randomCounter).toBe(0);1451expect(this.keyWithNullValue).toBeNull();1452},1453render: function() {1454return <span />;1455}1456});1457var instance = <Component />;1458instance = ReactTestUtils.renderIntoDocument(instance);1459});14601461it('should disallow nested render calls', function() {1462spyOn(console, 'warn');1463var Inner = React.createClass({1464render: function() {1465return <div />;1466}1467});1468var Outer = React.createClass({1469render: function() {1470ReactTestUtils.renderIntoDocument(<Inner />);1471return <div />;1472}1473});14741475ReactTestUtils.renderIntoDocument(<Outer />);1476expect(console.warn.argsForCall.length).toBe(1);1477expect(console.warn.argsForCall[0][0]).toBe(1478'Warning: _renderNewRootComponent(): Render methods should ' +1479'be a pure function of props and state; triggering nested component ' +1480'updates from render is not allowed. If necessary, trigger nested ' +1481'updates in componentDidUpdate.'1482);1483});14841485it('gives a helpful error when passing null or undefined', function() {1486spyOn(console, 'warn');1487React.createElement(undefined);1488React.createElement(null);1489expect(console.warn.calls.length).toBe(2);1490expect(console.warn.calls[0].args[0]).toBe(1491'Warning: React.createElement: type should not be null or undefined. ' +1492'It should be a string (for DOM elements) or a ReactClass (for ' +1493'composite components).'1494);1495expect(console.warn.calls[1].args[0]).toBe(1496'Warning: React.createElement: type should not be null or undefined. ' +1497'It should be a string (for DOM elements) or a ReactClass (for ' +1498'composite components).'1499);1500React.createElement('div');1501expect(console.warn.calls.length).toBe(2);15021503expect(() => React.createElement(undefined)).not.toThrow()1504});15051506});150715081509