mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
* Enable Yarn workspaces for packages/* * Move src/isomorphic/* into packages/react/src/* * Create index.js stubs for all packages in packages/* This makes the test pass again, but breaks the build because npm/ folders aren't used yet. I'm not sure if we'll keep this structure--I'll just keep working and fix the build after it settles down. * Put FB entry point for react-dom into packages/* * Move src/renderers/testing/* into packages/react-test-renderer/src/* Note that this is currently broken because Jest ignores node_modules, and so Yarn linking makes Jest skip React source when transforming. * Remove src/node_modules It is now unnecessary. Some tests fail though. * Add a hacky workaround for Jest/Workspaces issue Jest sees node_modules and thinks it's third party code. This is a hacky way to teach Jest to still transform anything in node_modules/react* if it resolves outside of node_modules (such as to our packages/*) folder. I'm not very happy with this and we should revisit. * Add a fake react-native package * Move src/renderers/art/* into packages/react-art/src/* * Move src/renderers/noop/* into packages/react-noop-renderer/src/* * Move src/renderers/dom/* into packages/react-dom/src/* * Move src/renderers/shared/fiber/* into packages/react-reconciler/src/* * Move DOM/reconciler tests I previously forgot to move * Move src/renderers/native-*/* into packages/react-native-*/src/* * Move shared code into packages/shared It's not super clear how to organize this properly yet. * Add back files that somehow got lost * Fix the build * Prettier * Add missing license headers * Fix an issue that caused mocks to get included into build * Update other references to src/ * Re-run Prettier * Fix lint * Fix weird Flow violation I didn't change this file but Flow started complaining. Caleb said this annotation was unnecessarily using $Abstract though so I removed it. * Update sizes * Fix stats script * Fix packaging fixtures Use file: instead of NODE_PATH since NODE_PATH. NODE_PATH trick only worked because we had no react/react-dom in root node_modules, but now we do. file: dependency only works as I expect in Yarn, so I moved the packaging fixtures to use Yarn and committed lockfiles. Verified that the page shows up. * Fix art fixture * Fix reconciler fixture * Fix SSR fixture * Rename native packages
439 lines
12 KiB
JavaScript
439 lines
12 KiB
JavaScript
/**
|
|
* 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';
|
|
|
|
let createRenderer;
|
|
let React;
|
|
let ReactDOM;
|
|
let ReactDOMServer;
|
|
let ReactTestUtils;
|
|
|
|
function getTestDocument(markup) {
|
|
var doc = document.implementation.createHTMLDocument('');
|
|
doc.open();
|
|
doc.write(
|
|
markup ||
|
|
'<!doctype html><html><meta charset=utf-8><title>test doc</title>',
|
|
);
|
|
doc.close();
|
|
return doc;
|
|
}
|
|
|
|
describe('ReactTestUtils', () => {
|
|
beforeEach(() => {
|
|
createRenderer = require('react-test-renderer/shallow').createRenderer;
|
|
React = require('react');
|
|
ReactDOM = require('react-dom');
|
|
ReactDOMServer = require('react-dom/server');
|
|
ReactTestUtils = require('react-dom/test-utils');
|
|
});
|
|
|
|
it('gives Jest mocks a passthrough implementation with mockComponent()', () => {
|
|
class MockedComponent extends React.Component {
|
|
render() {
|
|
throw new Error('Should not get here.');
|
|
}
|
|
}
|
|
// This is close enough to what a Jest mock would give us.
|
|
MockedComponent.prototype.render = jest.fn();
|
|
|
|
// Patch it up so it returns its children.
|
|
ReactTestUtils.mockComponent(MockedComponent);
|
|
|
|
var container = document.createElement('div');
|
|
ReactDOM.render(<MockedComponent>Hello</MockedComponent>, container);
|
|
expect(container.textContent).toBe('Hello');
|
|
});
|
|
|
|
it('can scryRenderedComponentsWithType', () => {
|
|
class Child extends React.Component {
|
|
render() {
|
|
return null;
|
|
}
|
|
}
|
|
class Wrapper extends React.Component {
|
|
render() {
|
|
return (
|
|
<div>
|
|
<Child />
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
const renderedComponent = ReactTestUtils.renderIntoDocument(<Wrapper />);
|
|
const scryResults = ReactTestUtils.scryRenderedComponentsWithType(
|
|
renderedComponent,
|
|
Child,
|
|
);
|
|
expect(scryResults.length).toBe(1);
|
|
});
|
|
|
|
it('can scryRenderedDOMComponentsWithClass with TextComponent', () => {
|
|
class Wrapper extends React.Component {
|
|
render() {
|
|
return <div>Hello <span>Jim</span></div>;
|
|
}
|
|
}
|
|
|
|
const renderedComponent = ReactTestUtils.renderIntoDocument(<Wrapper />);
|
|
const scryResults = ReactTestUtils.scryRenderedDOMComponentsWithClass(
|
|
renderedComponent,
|
|
'NonExistentClass',
|
|
);
|
|
expect(scryResults.length).toBe(0);
|
|
});
|
|
|
|
it('can scryRenderedDOMComponentsWithClass with className contains \\n', () => {
|
|
class Wrapper extends React.Component {
|
|
render() {
|
|
return <div>Hello <span className={'x\ny'}>Jim</span></div>;
|
|
}
|
|
}
|
|
|
|
const renderedComponent = ReactTestUtils.renderIntoDocument(<Wrapper />);
|
|
const scryResults = ReactTestUtils.scryRenderedDOMComponentsWithClass(
|
|
renderedComponent,
|
|
'x',
|
|
);
|
|
expect(scryResults.length).toBe(1);
|
|
});
|
|
|
|
it('can scryRenderedDOMComponentsWithClass with multiple classes', () => {
|
|
class Wrapper extends React.Component {
|
|
render() {
|
|
return <div>Hello <span className={'x y z'}>Jim</span></div>;
|
|
}
|
|
}
|
|
|
|
const renderedComponent = ReactTestUtils.renderIntoDocument(<Wrapper />);
|
|
const scryResults1 = ReactTestUtils.scryRenderedDOMComponentsWithClass(
|
|
renderedComponent,
|
|
'x y',
|
|
);
|
|
expect(scryResults1.length).toBe(1);
|
|
|
|
const scryResults2 = ReactTestUtils.scryRenderedDOMComponentsWithClass(
|
|
renderedComponent,
|
|
'x z',
|
|
);
|
|
expect(scryResults2.length).toBe(1);
|
|
|
|
const scryResults3 = ReactTestUtils.scryRenderedDOMComponentsWithClass(
|
|
renderedComponent,
|
|
['x', 'y'],
|
|
);
|
|
expect(scryResults3.length).toBe(1);
|
|
|
|
expect(scryResults1[0]).toBe(scryResults2[0]);
|
|
expect(scryResults1[0]).toBe(scryResults3[0]);
|
|
|
|
const scryResults4 = ReactTestUtils.scryRenderedDOMComponentsWithClass(
|
|
renderedComponent,
|
|
['x', 'a'],
|
|
);
|
|
expect(scryResults4.length).toBe(0);
|
|
|
|
const scryResults5 = ReactTestUtils.scryRenderedDOMComponentsWithClass(
|
|
renderedComponent,
|
|
['x a'],
|
|
);
|
|
expect(scryResults5.length).toBe(0);
|
|
});
|
|
|
|
it('traverses children in the correct order', () => {
|
|
class Wrapper extends React.Component {
|
|
render() {
|
|
return <div>{this.props.children}</div>;
|
|
}
|
|
}
|
|
|
|
const container = document.createElement('div');
|
|
ReactDOM.render(
|
|
<Wrapper>
|
|
{null}
|
|
<div>purple</div>
|
|
</Wrapper>,
|
|
container,
|
|
);
|
|
const tree = ReactDOM.render(
|
|
<Wrapper>
|
|
<div>orange</div>
|
|
<div>purple</div>
|
|
</Wrapper>,
|
|
container,
|
|
);
|
|
|
|
const log = [];
|
|
ReactTestUtils.findAllInRenderedTree(tree, function(child) {
|
|
if (ReactTestUtils.isDOMComponent(child)) {
|
|
log.push(ReactDOM.findDOMNode(child).textContent);
|
|
}
|
|
});
|
|
|
|
// Should be document order, not mount order (which would be purple, orange)
|
|
expect(log).toEqual(['orangepurple', 'orange', 'purple']);
|
|
});
|
|
|
|
it('should support injected wrapper components as DOM components', () => {
|
|
const injectedDOMComponents = [
|
|
'button',
|
|
'form',
|
|
'iframe',
|
|
'img',
|
|
'input',
|
|
'option',
|
|
'select',
|
|
'textarea',
|
|
];
|
|
|
|
injectedDOMComponents.forEach(function(type) {
|
|
const testComponent = ReactTestUtils.renderIntoDocument(
|
|
React.createElement(type),
|
|
);
|
|
expect(testComponent.tagName).toBe(type.toUpperCase());
|
|
expect(ReactTestUtils.isDOMComponent(testComponent)).toBe(true);
|
|
});
|
|
|
|
// Full-page components (html, head, body) can't be rendered into a div
|
|
// directly...
|
|
class Root extends React.Component {
|
|
render() {
|
|
return (
|
|
<html ref="html">
|
|
<head ref="head">
|
|
<title>hello</title>
|
|
</head>
|
|
<body ref="body">
|
|
hello, world
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|
|
}
|
|
|
|
const markup = ReactDOMServer.renderToString(<Root />);
|
|
const testDocument = getTestDocument(markup);
|
|
const component = ReactDOM.hydrate(<Root />, testDocument);
|
|
|
|
expect(component.refs.html.tagName).toBe('HTML');
|
|
expect(component.refs.head.tagName).toBe('HEAD');
|
|
expect(component.refs.body.tagName).toBe('BODY');
|
|
expect(ReactTestUtils.isDOMComponent(component.refs.html)).toBe(true);
|
|
expect(ReactTestUtils.isDOMComponent(component.refs.head)).toBe(true);
|
|
expect(ReactTestUtils.isDOMComponent(component.refs.body)).toBe(true);
|
|
});
|
|
|
|
it('can scry with stateless components involved', () => {
|
|
const Stateless = () => <div><hr /></div>;
|
|
|
|
class SomeComponent extends React.Component {
|
|
render() {
|
|
return (
|
|
<div>
|
|
<Stateless />
|
|
<hr />
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
const inst = ReactTestUtils.renderIntoDocument(<SomeComponent />);
|
|
const hrs = ReactTestUtils.scryRenderedDOMComponentsWithTag(inst, 'hr');
|
|
expect(hrs.length).toBe(2);
|
|
});
|
|
|
|
describe('Simulate', () => {
|
|
it('should change the value of an input field', () => {
|
|
const obj = {
|
|
handler: function(e) {
|
|
e.persist();
|
|
},
|
|
};
|
|
spyOn(obj, 'handler').and.callThrough();
|
|
const container = document.createElement('div');
|
|
const instance = ReactDOM.render(
|
|
<input type="text" onChange={obj.handler} />,
|
|
container,
|
|
);
|
|
|
|
const node = ReactDOM.findDOMNode(instance);
|
|
node.value = 'giraffe';
|
|
ReactTestUtils.Simulate.change(node);
|
|
|
|
expect(obj.handler).toHaveBeenCalledWith(
|
|
jasmine.objectContaining({target: node}),
|
|
);
|
|
});
|
|
|
|
it('should change the value of an input field in a component', () => {
|
|
class SomeComponent extends React.Component {
|
|
render() {
|
|
return (
|
|
<div>
|
|
<input
|
|
type="text"
|
|
ref="input"
|
|
onChange={this.props.handleChange}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
const obj = {
|
|
handler: function(e) {
|
|
e.persist();
|
|
},
|
|
};
|
|
spyOn(obj, 'handler').and.callThrough();
|
|
const container = document.createElement('div');
|
|
const instance = ReactDOM.render(
|
|
<SomeComponent handleChange={obj.handler} />,
|
|
container,
|
|
);
|
|
|
|
const node = ReactDOM.findDOMNode(instance.refs.input);
|
|
node.value = 'zebra';
|
|
ReactTestUtils.Simulate.change(node);
|
|
|
|
expect(obj.handler).toHaveBeenCalledWith(
|
|
jasmine.objectContaining({target: node}),
|
|
);
|
|
});
|
|
|
|
it('should throw when attempting to use a React element', () => {
|
|
class SomeComponent extends React.Component {
|
|
render() {
|
|
return (
|
|
<div onClick={this.props.handleClick}>
|
|
hello, world.
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
const handler = jasmine.createSpy('spy');
|
|
const shallowRenderer = createRenderer();
|
|
const result = shallowRenderer.render(
|
|
<SomeComponent handleClick={handler} />,
|
|
);
|
|
|
|
expect(() => ReactTestUtils.Simulate.click(result)).toThrowError(
|
|
'TestUtils.Simulate expected a DOM node as the first argument but received ' +
|
|
'a React element. Pass the DOM node you wish to simulate the event on instead. ' +
|
|
'Note that TestUtils.Simulate will not work if you are using shallow rendering.',
|
|
);
|
|
expect(handler).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should throw when attempting to use a component instance', () => {
|
|
class SomeComponent extends React.Component {
|
|
render() {
|
|
return (
|
|
<div onClick={this.props.handleClick}>
|
|
hello, world.
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
const handler = jasmine.createSpy('spy');
|
|
const container = document.createElement('div');
|
|
const instance = ReactDOM.render(
|
|
<SomeComponent handleClick={handler} />,
|
|
container,
|
|
);
|
|
|
|
expect(() => ReactTestUtils.Simulate.click(instance)).toThrowError(
|
|
'TestUtils.Simulate expected a DOM node as the first argument but received ' +
|
|
'a component instance. Pass the DOM node you wish to simulate the event on instead.',
|
|
);
|
|
expect(handler).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should not warn when used with extra properties', () => {
|
|
spyOn(console, 'error');
|
|
|
|
const CLIENT_X = 100;
|
|
|
|
class Component extends React.Component {
|
|
handleClick = e => {
|
|
expect(e.clientX).toBe(CLIENT_X);
|
|
};
|
|
|
|
render() {
|
|
return <div onClick={this.handleClick} />;
|
|
}
|
|
}
|
|
|
|
const element = document.createElement('div');
|
|
const instance = ReactDOM.render(<Component />, element);
|
|
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance), {
|
|
clientX: CLIENT_X,
|
|
});
|
|
expectDev(console.error.calls.count()).toBe(0);
|
|
});
|
|
|
|
it('should set the type of the event', () => {
|
|
let event;
|
|
const stub = jest.genMockFn().mockImplementation(e => {
|
|
e.persist();
|
|
event = e;
|
|
});
|
|
|
|
const container = document.createElement('div');
|
|
const instance = ReactDOM.render(<div onKeyDown={stub} />, container);
|
|
const node = ReactDOM.findDOMNode(instance);
|
|
|
|
ReactTestUtils.Simulate.keyDown(node);
|
|
|
|
expect(event.type).toBe('keydown');
|
|
expect(event.nativeEvent.type).toBe('keydown');
|
|
});
|
|
|
|
it('should work with renderIntoDocument', () => {
|
|
const onChange = jest.fn();
|
|
|
|
class MyComponent extends React.Component {
|
|
render() {
|
|
return <div><input type="text" onChange={onChange} /></div>;
|
|
}
|
|
}
|
|
|
|
const instance = ReactTestUtils.renderIntoDocument(<MyComponent />);
|
|
const input = ReactTestUtils.findRenderedDOMComponentWithTag(
|
|
instance,
|
|
'input',
|
|
);
|
|
input.value = 'giraffe';
|
|
ReactTestUtils.Simulate.change(input);
|
|
|
|
expect(onChange).toHaveBeenCalledWith(
|
|
jasmine.objectContaining({target: input}),
|
|
);
|
|
});
|
|
});
|
|
|
|
it('should call setState callback with no arguments', () => {
|
|
let mockArgs;
|
|
class Component extends React.Component {
|
|
componentDidMount() {
|
|
this.setState({}, (...args) => (mockArgs = args));
|
|
}
|
|
render() {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ReactTestUtils.renderIntoDocument(<Component />);
|
|
expect(mockArgs.length).toEqual(0);
|
|
});
|
|
});
|