react/src/renderers/__tests__/ReactStatelessComponent-tes...

390 lines
11 KiB
JavaScript
Raw Normal View History

/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/
'use strict';
var PropTypes;
var React;
2015-08-25 07:46:11 +08:00
var ReactDOM;
var ReactTestUtils;
function StatelessComponent(props) {
return <div>{props.name}</div>;
}
2016-09-07 06:18:42 +08:00
describe('ReactStatelessComponent', () => {
Warn for callback refs on functional components (Stack + Fiber) (#8635) * Fiber: warn for refs on SFCs * Stateless refs: update warning to use component stack * Warn for function refs (stack and fiber) * Add owner reference to ref warnings * Centralize stack ref warnings in ReactRef.attachRef * Fiber stateless comp ref warning should only do work when necessary * ReactDebugCurrentFiber maybe FunctionalComponents should act this way instead * (chore) scripts/fiber/record-tests * Add component._compositeType to ReactInstance Flow definition * Don't handle 'stack inside fiber' case in the warning We don't have a test for it. It's easy to mess it up and print the wrong thing so instead of verifying it I'll just remove this bit. * Revert the change to getCurrentFiberOwnerName This happened to work, but it is just a coincidence. This change didn’t really match what the function was supposed to be doing. I’m not sure what the correct fix would be yet so this commit breaks tests. * Add component indirection to the tests using owner name This passes in Stack. It helps ensure we supply the correct owner name. * Invalid type invariant should read owner from element This brings the Fiber behavior in line with how Stack does it. The Fiber test was passing accidentally but broke down in more complicated cases (such as when we have an <Indirection> between the owner and the failing element). Now we can also remove the weird cases from getCurrentFiberOwnerName() that didn't really make sense but helped get the (incomplete) tests pass in the past. * Fiber should throw on a string ref inside functional component
2017-01-10 07:09:18 +08:00
function normalizeCodeLocInfo(str) {
return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
}
2016-09-07 06:18:42 +08:00
beforeEach(() => {
jest.resetModuleRegistry();
PropTypes = require('prop-types');
React = require('react');
ReactDOM = require('react-dom');
ReactTestUtils = require('react-dom/test-utils');
});
2016-09-07 06:18:42 +08:00
it('should render stateless component', () => {
var el = document.createElement('div');
2015-08-25 07:46:11 +08:00
ReactDOM.render(<StatelessComponent name="A" />, el);
expect(el.textContent).toBe('A');
});
2016-09-07 06:18:42 +08:00
it('should update stateless component', () => {
class Parent extends React.Component {
render() {
return <StatelessComponent {...this.props} />;
}
}
var el = document.createElement('div');
2015-08-25 07:46:11 +08:00
ReactDOM.render(<Parent name="A" />, el);
expect(el.textContent).toBe('A');
2015-08-25 07:46:11 +08:00
ReactDOM.render(<Parent name="B" />, el);
expect(el.textContent).toBe('B');
});
2016-09-07 06:18:42 +08:00
it('should unmount stateless component', () => {
var container = document.createElement('div');
2015-08-25 07:46:11 +08:00
ReactDOM.render(<StatelessComponent name="A" />, container);
expect(container.textContent).toBe('A');
2015-08-25 07:46:11 +08:00
ReactDOM.unmountComponentAtNode(container);
expect(container.textContent).toBe('');
});
2016-09-07 06:18:42 +08:00
it('should pass context thru stateless component', () => {
class Child extends React.Component {
static contextTypes = {
test: PropTypes.string.isRequired,
};
render() {
return <div>{this.context.test}</div>;
}
}
function Parent() {
return <Child />;
}
class GrandParent extends React.Component {
static childContextTypes = {
test: PropTypes.string.isRequired,
};
getChildContext() {
return {test: this.props.test};
}
render() {
return <Parent />;
}
}
var el = document.createElement('div');
2015-08-25 07:46:11 +08:00
ReactDOM.render(<GrandParent test="test" />, el);
expect(el.textContent).toBe('test');
2015-08-25 07:46:11 +08:00
ReactDOM.render(<GrandParent test="mest" />, el);
expect(el.textContent).toBe('mest');
});
it('should warn for childContextTypes on a functional component', () => {
spyOn(console, 'error');
function StatelessComponentWithChildContext(props) {
return <div>{props.name}</div>;
}
StatelessComponentWithChildContext.childContextTypes = {
foo: PropTypes.string,
};
var container = document.createElement('div');
ReactDOM.render(<StatelessComponentWithChildContext name="A" />, container);
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'StatelessComponentWithChildContext(...): childContextTypes cannot ' +
'be defined on a functional component.',
);
});
it('should throw when stateless component returns undefined', () => {
function NotAComponent() {}
expect(function() {
ReactTestUtils.renderIntoDocument(<div><NotAComponent /></div>);
}).toThrowError(
'NotAComponent(...): Nothing was returned from render. ' +
'This usually means a return statement is missing. Or, to render nothing, return null.',
);
});
2016-09-07 06:18:42 +08:00
it('should throw on string refs in pure functions', () => {
function Child() {
return <div ref="me" />;
}
expect(function() {
ReactTestUtils.renderIntoDocument(<Child test="test" />);
2017-03-14 08:05:18 +08:00
}).toThrowError('Stateless function components cannot have refs.');
});
Warn for callback refs on functional components (Stack + Fiber) (#8635) * Fiber: warn for refs on SFCs * Stateless refs: update warning to use component stack * Warn for function refs (stack and fiber) * Add owner reference to ref warnings * Centralize stack ref warnings in ReactRef.attachRef * Fiber stateless comp ref warning should only do work when necessary * ReactDebugCurrentFiber maybe FunctionalComponents should act this way instead * (chore) scripts/fiber/record-tests * Add component._compositeType to ReactInstance Flow definition * Don't handle 'stack inside fiber' case in the warning We don't have a test for it. It's easy to mess it up and print the wrong thing so instead of verifying it I'll just remove this bit. * Revert the change to getCurrentFiberOwnerName This happened to work, but it is just a coincidence. This change didn’t really match what the function was supposed to be doing. I’m not sure what the correct fix would be yet so this commit breaks tests. * Add component indirection to the tests using owner name This passes in Stack. It helps ensure we supply the correct owner name. * Invalid type invariant should read owner from element This brings the Fiber behavior in line with how Stack does it. The Fiber test was passing accidentally but broke down in more complicated cases (such as when we have an <Indirection> between the owner and the failing element). Now we can also remove the weird cases from getCurrentFiberOwnerName() that didn't really make sense but helped get the (incomplete) tests pass in the past. * Fiber should throw on a string ref inside functional component
2017-01-10 07:09:18 +08:00
it('should warn when given a string ref', () => {
spyOn(console, 'error');
Warn for callback refs on functional components (Stack + Fiber) (#8635) * Fiber: warn for refs on SFCs * Stateless refs: update warning to use component stack * Warn for function refs (stack and fiber) * Add owner reference to ref warnings * Centralize stack ref warnings in ReactRef.attachRef * Fiber stateless comp ref warning should only do work when necessary * ReactDebugCurrentFiber maybe FunctionalComponents should act this way instead * (chore) scripts/fiber/record-tests * Add component._compositeType to ReactInstance Flow definition * Don't handle 'stack inside fiber' case in the warning We don't have a test for it. It's easy to mess it up and print the wrong thing so instead of verifying it I'll just remove this bit. * Revert the change to getCurrentFiberOwnerName This happened to work, but it is just a coincidence. This change didn’t really match what the function was supposed to be doing. I’m not sure what the correct fix would be yet so this commit breaks tests. * Add component indirection to the tests using owner name This passes in Stack. It helps ensure we supply the correct owner name. * Invalid type invariant should read owner from element This brings the Fiber behavior in line with how Stack does it. The Fiber test was passing accidentally but broke down in more complicated cases (such as when we have an <Indirection> between the owner and the failing element). Now we can also remove the weird cases from getCurrentFiberOwnerName() that didn't really make sense but helped get the (incomplete) tests pass in the past. * Fiber should throw on a string ref inside functional component
2017-01-10 07:09:18 +08:00
function Indirection(props) {
return <div>{props.children}</div>;
}
class ParentUsingStringRef extends React.Component {
Warn for callback refs on functional components (Stack + Fiber) (#8635) * Fiber: warn for refs on SFCs * Stateless refs: update warning to use component stack * Warn for function refs (stack and fiber) * Add owner reference to ref warnings * Centralize stack ref warnings in ReactRef.attachRef * Fiber stateless comp ref warning should only do work when necessary * ReactDebugCurrentFiber maybe FunctionalComponents should act this way instead * (chore) scripts/fiber/record-tests * Add component._compositeType to ReactInstance Flow definition * Don't handle 'stack inside fiber' case in the warning We don't have a test for it. It's easy to mess it up and print the wrong thing so instead of verifying it I'll just remove this bit. * Revert the change to getCurrentFiberOwnerName This happened to work, but it is just a coincidence. This change didn’t really match what the function was supposed to be doing. I’m not sure what the correct fix would be yet so this commit breaks tests. * Add component indirection to the tests using owner name This passes in Stack. It helps ensure we supply the correct owner name. * Invalid type invariant should read owner from element This brings the Fiber behavior in line with how Stack does it. The Fiber test was passing accidentally but broke down in more complicated cases (such as when we have an <Indirection> between the owner and the failing element). Now we can also remove the weird cases from getCurrentFiberOwnerName() that didn't really make sense but helped get the (incomplete) tests pass in the past. * Fiber should throw on a string ref inside functional component
2017-01-10 07:09:18 +08:00
render() {
return (
<Indirection>
<StatelessComponent name="A" ref="stateless" />
</Indirection>
);
Warn for callback refs on functional components (Stack + Fiber) (#8635) * Fiber: warn for refs on SFCs * Stateless refs: update warning to use component stack * Warn for function refs (stack and fiber) * Add owner reference to ref warnings * Centralize stack ref warnings in ReactRef.attachRef * Fiber stateless comp ref warning should only do work when necessary * ReactDebugCurrentFiber maybe FunctionalComponents should act this way instead * (chore) scripts/fiber/record-tests * Add component._compositeType to ReactInstance Flow definition * Don't handle 'stack inside fiber' case in the warning We don't have a test for it. It's easy to mess it up and print the wrong thing so instead of verifying it I'll just remove this bit. * Revert the change to getCurrentFiberOwnerName This happened to work, but it is just a coincidence. This change didn’t really match what the function was supposed to be doing. I’m not sure what the correct fix would be yet so this commit breaks tests. * Add component indirection to the tests using owner name This passes in Stack. It helps ensure we supply the correct owner name. * Invalid type invariant should read owner from element This brings the Fiber behavior in line with how Stack does it. The Fiber test was passing accidentally but broke down in more complicated cases (such as when we have an <Indirection> between the owner and the failing element). Now we can also remove the weird cases from getCurrentFiberOwnerName() that didn't really make sense but helped get the (incomplete) tests pass in the past. * Fiber should throw on a string ref inside functional component
2017-01-10 07:09:18 +08:00
}
}
ReactTestUtils.renderIntoDocument(<ParentUsingStringRef />);
Warn for callback refs on functional components (Stack + Fiber) (#8635) * Fiber: warn for refs on SFCs * Stateless refs: update warning to use component stack * Warn for function refs (stack and fiber) * Add owner reference to ref warnings * Centralize stack ref warnings in ReactRef.attachRef * Fiber stateless comp ref warning should only do work when necessary * ReactDebugCurrentFiber maybe FunctionalComponents should act this way instead * (chore) scripts/fiber/record-tests * Add component._compositeType to ReactInstance Flow definition * Don't handle 'stack inside fiber' case in the warning We don't have a test for it. It's easy to mess it up and print the wrong thing so instead of verifying it I'll just remove this bit. * Revert the change to getCurrentFiberOwnerName This happened to work, but it is just a coincidence. This change didn’t really match what the function was supposed to be doing. I’m not sure what the correct fix would be yet so this commit breaks tests. * Add component indirection to the tests using owner name This passes in Stack. It helps ensure we supply the correct owner name. * Invalid type invariant should read owner from element This brings the Fiber behavior in line with how Stack does it. The Fiber test was passing accidentally but broke down in more complicated cases (such as when we have an <Indirection> between the owner and the failing element). Now we can also remove the weird cases from getCurrentFiberOwnerName() that didn't really make sense but helped get the (incomplete) tests pass in the past. * Fiber should throw on a string ref inside functional component
2017-01-10 07:09:18 +08:00
expectDev(console.error.calls.count()).toBe(1);
expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: Stateless function components cannot be given refs. ' +
2017-03-14 08:05:18 +08:00
'Attempts to access this ref will fail.\n\nCheck the render method ' +
'of `ParentUsingStringRef`.\n' +
' in StatelessComponent (at **)\n' +
' in div (at **)\n' +
' in Indirection (at **)\n' +
' in ParentUsingStringRef (at **)',
Warn for callback refs on functional components (Stack + Fiber) (#8635) * Fiber: warn for refs on SFCs * Stateless refs: update warning to use component stack * Warn for function refs (stack and fiber) * Add owner reference to ref warnings * Centralize stack ref warnings in ReactRef.attachRef * Fiber stateless comp ref warning should only do work when necessary * ReactDebugCurrentFiber maybe FunctionalComponents should act this way instead * (chore) scripts/fiber/record-tests * Add component._compositeType to ReactInstance Flow definition * Don't handle 'stack inside fiber' case in the warning We don't have a test for it. It's easy to mess it up and print the wrong thing so instead of verifying it I'll just remove this bit. * Revert the change to getCurrentFiberOwnerName This happened to work, but it is just a coincidence. This change didn’t really match what the function was supposed to be doing. I’m not sure what the correct fix would be yet so this commit breaks tests. * Add component indirection to the tests using owner name This passes in Stack. It helps ensure we supply the correct owner name. * Invalid type invariant should read owner from element This brings the Fiber behavior in line with how Stack does it. The Fiber test was passing accidentally but broke down in more complicated cases (such as when we have an <Indirection> between the owner and the failing element). Now we can also remove the weird cases from getCurrentFiberOwnerName() that didn't really make sense but helped get the (incomplete) tests pass in the past. * Fiber should throw on a string ref inside functional component
2017-01-10 07:09:18 +08:00
);
ReactTestUtils.renderIntoDocument(<ParentUsingStringRef />);
expectDev(console.error.calls.count()).toBe(1);
Warn for callback refs on functional components (Stack + Fiber) (#8635) * Fiber: warn for refs on SFCs * Stateless refs: update warning to use component stack * Warn for function refs (stack and fiber) * Add owner reference to ref warnings * Centralize stack ref warnings in ReactRef.attachRef * Fiber stateless comp ref warning should only do work when necessary * ReactDebugCurrentFiber maybe FunctionalComponents should act this way instead * (chore) scripts/fiber/record-tests * Add component._compositeType to ReactInstance Flow definition * Don't handle 'stack inside fiber' case in the warning We don't have a test for it. It's easy to mess it up and print the wrong thing so instead of verifying it I'll just remove this bit. * Revert the change to getCurrentFiberOwnerName This happened to work, but it is just a coincidence. This change didn’t really match what the function was supposed to be doing. I’m not sure what the correct fix would be yet so this commit breaks tests. * Add component indirection to the tests using owner name This passes in Stack. It helps ensure we supply the correct owner name. * Invalid type invariant should read owner from element This brings the Fiber behavior in line with how Stack does it. The Fiber test was passing accidentally but broke down in more complicated cases (such as when we have an <Indirection> between the owner and the failing element). Now we can also remove the weird cases from getCurrentFiberOwnerName() that didn't really make sense but helped get the (incomplete) tests pass in the past. * Fiber should throw on a string ref inside functional component
2017-01-10 07:09:18 +08:00
});
it('should warn when given a function ref', () => {
spyOn(console, 'error');
function Indirection(props) {
return <div>{props.children}</div>;
}
class ParentUsingFunctionRef extends React.Component {
render() {
return (
<Indirection>
2017-03-14 08:05:18 +08:00
<StatelessComponent
name="A"
ref={arg => {
expect(arg).toBe(null);
}}
/>
</Indirection>
);
}
}
ReactTestUtils.renderIntoDocument(<ParentUsingFunctionRef />);
2016-11-14 06:16:38 +08:00
expectDev(console.error.calls.count()).toBe(1);
Warn for callback refs on functional components (Stack + Fiber) (#8635) * Fiber: warn for refs on SFCs * Stateless refs: update warning to use component stack * Warn for function refs (stack and fiber) * Add owner reference to ref warnings * Centralize stack ref warnings in ReactRef.attachRef * Fiber stateless comp ref warning should only do work when necessary * ReactDebugCurrentFiber maybe FunctionalComponents should act this way instead * (chore) scripts/fiber/record-tests * Add component._compositeType to ReactInstance Flow definition * Don't handle 'stack inside fiber' case in the warning We don't have a test for it. It's easy to mess it up and print the wrong thing so instead of verifying it I'll just remove this bit. * Revert the change to getCurrentFiberOwnerName This happened to work, but it is just a coincidence. This change didn’t really match what the function was supposed to be doing. I’m not sure what the correct fix would be yet so this commit breaks tests. * Add component indirection to the tests using owner name This passes in Stack. It helps ensure we supply the correct owner name. * Invalid type invariant should read owner from element This brings the Fiber behavior in line with how Stack does it. The Fiber test was passing accidentally but broke down in more complicated cases (such as when we have an <Indirection> between the owner and the failing element). Now we can also remove the weird cases from getCurrentFiberOwnerName() that didn't really make sense but helped get the (incomplete) tests pass in the past. * Fiber should throw on a string ref inside functional component
2017-01-10 07:09:18 +08:00
expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: Stateless function components cannot be given refs. ' +
2017-03-14 08:05:18 +08:00
'Attempts to access this ref will fail.\n\nCheck the render method ' +
'of `ParentUsingFunctionRef`.\n' +
' in StatelessComponent (at **)\n' +
' in div (at **)\n' +
' in Indirection (at **)\n' +
' in ParentUsingFunctionRef (at **)',
);
ReactTestUtils.renderIntoDocument(<ParentUsingFunctionRef />);
expectDev(console.error.calls.count()).toBe(1);
});
it('deduplicates ref warnings based on element or owner', () => {
spyOn(console, 'error');
// When owner uses JSX, we can use exact line location to dedupe warnings
class AnonymousParentUsingJSX extends React.Component {
render() {
return <StatelessComponent name="A" ref={() => {}} />;
}
}
Object.defineProperty(AnonymousParentUsingJSX, 'name', {value: undefined});
2017-03-14 08:05:18 +08:00
const instance1 = ReactTestUtils.renderIntoDocument(
<AnonymousParentUsingJSX />,
);
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
2017-03-14 08:05:18 +08:00
'Warning: Stateless function components cannot be given refs.',
);
// Should be deduped (offending element is on the same line):
instance1.forceUpdate();
// Should also be deduped (offending element is on the same line):
ReactTestUtils.renderIntoDocument(<AnonymousParentUsingJSX />);
expectDev(console.error.calls.count()).toBe(1);
console.error.calls.reset();
// When owner doesn't use JSX, and is anonymous, we warn once per internal instance.
class AnonymousParentNotUsingJSX extends React.Component {
render() {
2017-03-14 08:05:18 +08:00
return React.createElement(StatelessComponent, {
name: 'A',
ref: () => {},
});
}
}
2017-03-22 09:39:18 +08:00
Object.defineProperty(AnonymousParentNotUsingJSX, 'name', {
value: undefined,
});
2017-03-14 08:05:18 +08:00
const instance2 = ReactTestUtils.renderIntoDocument(
<AnonymousParentNotUsingJSX />,
);
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
2017-03-14 08:05:18 +08:00
'Warning: Stateless function components cannot be given refs.',
);
// Should be deduped (same internal instance):
instance2.forceUpdate();
expectDev(console.error.calls.count()).toBe(1);
// Could not be deduped (different internal instance):
ReactTestUtils.renderIntoDocument(<AnonymousParentNotUsingJSX />);
expectDev(console.error.calls.count()).toBe(2);
expectDev(console.error.calls.argsFor(1)[0]).toContain(
2017-03-14 08:05:18 +08:00
'Warning: Stateless function components cannot be given refs.',
);
console.error.calls.reset();
// When owner doesn't use JSX, but is named, we warn once per owner name
class NamedParentNotUsingJSX extends React.Component {
render() {
2017-03-14 08:05:18 +08:00
return React.createElement(StatelessComponent, {
name: 'A',
ref: () => {},
});
}
}
2017-03-14 08:05:18 +08:00
const instance3 = ReactTestUtils.renderIntoDocument(
<NamedParentNotUsingJSX />,
);
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
2017-03-14 08:05:18 +08:00
'Warning: Stateless function components cannot be given refs.',
);
// Should be deduped (same owner name):
instance3.forceUpdate();
expectDev(console.error.calls.count()).toBe(1);
// Should also be deduped (same owner name):
ReactTestUtils.renderIntoDocument(<NamedParentNotUsingJSX />);
expectDev(console.error.calls.count()).toBe(1);
console.error.calls.reset();
});
2016-09-07 06:18:42 +08:00
it('should provide a null ref', () => {
function Child() {
return <div />;
}
var comp = ReactTestUtils.renderIntoDocument(<Child />);
expect(comp).toBe(null);
});
2016-09-07 06:18:42 +08:00
it('should use correct name in key warning', () => {
function Child() {
return <div>{[<span />]}</div>;
}
spyOn(console, 'error');
ReactTestUtils.renderIntoDocument(<Child />);
2016-11-14 06:16:38 +08:00
expectDev(console.error.calls.count()).toBe(1);
2017-03-14 08:05:18 +08:00
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'a unique "key" prop',
);
2016-11-14 06:16:38 +08:00
expectDev(console.error.calls.argsFor(0)[0]).toContain('Child');
});
2016-09-07 06:18:42 +08:00
it('should support default props and prop types', () => {
function Child(props) {
return <div>{props.test}</div>;
}
Child.defaultProps = {test: 2};
Child.propTypes = {test: PropTypes.string};
spyOn(console, 'error');
ReactTestUtils.renderIntoDocument(<Child />);
2016-11-14 06:16:38 +08:00
expectDev(console.error.calls.count()).toBe(1);
expect(
2017-03-14 08:05:18 +08:00
console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)'),
).toBe(
'Warning: Failed prop type: Invalid prop `test` of type `number` ' +
2017-03-14 08:05:18 +08:00
'supplied to `Child`, expected `string`.\n' +
' in Child (at **)',
);
});
2016-09-07 06:18:42 +08:00
it('should receive context', () => {
class Parent extends React.Component {
static childContextTypes = {
lang: PropTypes.string,
};
getChildContext() {
return {lang: 'en'};
}
render() {
return <Child />;
}
}
function Child(props, context) {
return <div>{context.lang}</div>;
}
Child.contextTypes = {lang: PropTypes.string};
var el = document.createElement('div');
2015-08-25 07:46:11 +08:00
ReactDOM.render(<Parent />, el);
expect(el.textContent).toBe('en');
});
2016-09-07 06:18:42 +08:00
it('should work with arrow functions', () => {
var Child = function() {
return <div />;
};
// Will create a new bound function without a prototype, much like a native
// arrow function.
Child = Child.bind(this);
expect(() => ReactTestUtils.renderIntoDocument(<Child />)).not.toThrow();
});
2016-09-07 06:18:42 +08:00
it('should allow simple functions to return null', () => {
var Child = function() {
return null;
};
expect(() => ReactTestUtils.renderIntoDocument(<Child />)).not.toThrow();
});
2016-09-07 06:18:42 +08:00
it('should allow simple functions to return false', () => {
function Child() {
return false;
}
expect(() => ReactTestUtils.renderIntoDocument(<Child />)).not.toThrow();
});
});