packages/render/dom/__tests__/ReactTestUtils-test.js
'use strict';
let createRenderer;
let React;
let ReactDOM;
let ReactDOMServer;
let ReactTestUtils;
function getTestDocument(markup) {
const 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-server-renderer');
ReactTestUtils = require('test-utils');
});
it('Simulate should have locally attached media events', () => {
expect(Object.keys(ReactTestUtils.Simulate).sort()).toMatchSnapshot();
});
it('SimulateNative should have locally attached media events', () => {
expect(Object.keys(ReactTestUtils.SimulateNative).sort()).toMatchSnapshot();
});
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);
const 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();
},
};
spyOnDevAndProd(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();
},
};
spyOnDevAndProd(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(
'第一个参数必须为元素节点',
);
expect(handler).not.toHaveBeenCalled();
});
it('should not warn when used with extra properties', () => {
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,
});
});
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);
});
});