import PropTypes from '../../lib/ReactPropTypes'; import ReactTestUtils from "../../lib/ReactTestUtils"; import React from '../../src/May'; import { render, unmountComponentAtNode, findDOMNode } from '../../src/may-dom/MayDom' var ReactDOM = { render: render, unmountComponentAtNode: unmountComponentAtNode, findDOMNode: findDOMNode } React.render = render; // import React from "../../dist/ReactANU"; // var ReactDOM = React; // https://github.com/facebook/react/blob/master/src/renderers/__tests__/EventPluginHub-test.js describe("ReactCompositeComponentDOMMinimalism", function() { // this.timeout(200000); var TestComponent = class extends React.Component { constructor(props) { super(props); this.peekAtState("getInitialState", undefined, props); this.state = { color: "red" }; } peekAtState = (from, state = this.state, props = this.props) => { props.stateListener(from, state && state.color); }; peekAtCallback = from => { return () => this.peekAtState(from); }; setFavoriteColor(nextColor) { this.setState({ color: nextColor }, this.peekAtCallback("setFavoriteColor")); } render() { this.peekAtState("render"); return
{this.state.color}
; } componentWillMount() { this.peekAtState("componentWillMount-start"); this.setState(function(state) { this.peekAtState("before-setState-sunrise", state); }); this.setState({ color: "sunrise" }, this.peekAtCallback("setState-sunrise")); this.setState(function(state) { this.peekAtState("after-setState-sunrise", state); }); this.peekAtState("componentWillMount-after-sunrise"); this.setState({ color: "orange" }, this.peekAtCallback("setState-orange")); this.setState(function(state) { this.peekAtState("after-setState-orange", state); }); this.peekAtState("componentWillMount-end"); } componentDidMount() { this.peekAtState("componentDidMount-start"); this.setState({ color: "yellow" }, this.peekAtCallback("setState-yellow")); this.peekAtState("componentDidMount-end"); } componentWillReceiveProps(newProps) { this.peekAtState("componentWillReceiveProps-start"); if (newProps.nextColor) { this.setState(function(state) { this.peekAtState("before-setState-receiveProps", state); return { color: newProps.nextColor }; }); // No longer a public API, but we can test that it works internally by // reaching into the updater. this.setState({ color: undefined }); this.setState(function(state) { this.peekAtState("before-setState-again-receiveProps", state); return { color: newProps.nextColor }; }, this.peekAtCallback("setState-receiveProps")); this.setState(function(state) { this.peekAtState("after-setState-receiveProps", state); }); } this.peekAtState("componentWillReceiveProps-end"); } shouldComponentUpdate(nextProps, nextState) { this.peekAtState("shouldComponentUpdate-currentState"); this.peekAtState("shouldComponentUpdate-nextState", nextState); return true; } componentWillUpdate(nextProps, nextState) { this.peekAtState("componentWillUpdate-currentState"); this.peekAtState("componentWillUpdate-nextState", nextState); } componentDidUpdate(prevProps, prevState) { this.peekAtState("componentDidUpdate-currentState"); this.peekAtState("componentDidUpdate-prevState", prevState); } componentWillUnmount() { this.peekAtState("componentWillUnmount"); } }; /*it("should support setting state", () => { var container = document.createElement("div"); document.body.appendChild(container); var stateListener = spyOn.createSpy(); var instance = ReactDOM.render(, container, function peekAtInitialCallback() { this.peekAtState("initial-callback"); }); ReactDOM.render(, container, instance.peekAtCallback("setProps")); instance.setFavoriteColor("blue"); instance.forceUpdate(instance.peekAtCallback("forceUpdate")); ReactDOM.unmountComponentAtNode(container); let expected = [ // there is no state when getInitialState() is called ["getInitialState", null], ["componentWillMount-start", "red"], // setState()'s only enqueue pending states. ["componentWillMount-after-sunrise", "red"], ["componentWillMount-end", "red"], // pending state queue is processed ["before-setState-sunrise", "red"], ["after-setState-sunrise", "sunrise"], ["after-setState-orange", "orange"], // pending state has been applied ["render", "orange"], ["componentDidMount-start", "orange"], // setState-sunrise and setState-orange should be called here, // after the bug in #1740 // componentDidMount() called setState({color:'yellow'}), which is async. // The update doesn't happen until the next flush. ["componentDidMount-end", "orange"], ["shouldComponentUpdate-currentState", "orange"], ["shouldComponentUpdate-nextState", "yellow"], ["componentWillUpdate-currentState", "orange"], ["componentWillUpdate-nextState", "yellow"], ["render", "yellow"], ["componentDidUpdate-currentState", "yellow"], ["componentDidUpdate-prevState", "orange"], ["setState-sunrise", "yellow"], ["setState-orange", "yellow"], ["setState-yellow", "yellow"], ["initial-callback", "yellow"], ["componentWillReceiveProps-start", "yellow"], // setState({color:'green'}) only enqueues a pending state. ["componentWillReceiveProps-end", "yellow"], // pending state queue is processed // We keep updates in the queue to support // replaceState(prevState => newState). ["before-setState-receiveProps", "yellow"], ["before-setState-again-receiveProps", void 666], ["after-setState-receiveProps", "green"], ["shouldComponentUpdate-currentState", "yellow"], ["shouldComponentUpdate-nextState", "green"], ["componentWillUpdate-currentState", "yellow"], ["componentWillUpdate-nextState", "green"], // setFavoriteColor('blue') ["render", "green"], ["componentDidUpdate-currentState", "green"], ["componentDidUpdate-prevState", "yellow"], ["setState-receiveProps", "green"], ["setProps", "green"], // setFavoriteColor('blue') ["shouldComponentUpdate-currentState", "green"], ["shouldComponentUpdate-nextState", "blue"], ["componentWillUpdate-currentState", "green"], ["componentWillUpdate-nextState", "blue"], ["render", "blue"], ["componentDidUpdate-currentState", "blue"], ["componentDidUpdate-prevState", "green"], ["setFavoriteColor", "blue"], // forceUpdate() ["componentWillUpdate-currentState", "blue"], ["componentWillUpdate-nextState", "blue"], ["render", "blue"], ["componentDidUpdate-currentState", "blue"], ["componentDidUpdate-prevState", "blue"], ["forceUpdate", "blue"], // unmountComponent() // state is available within `componentWillUnmount()` ["componentWillUnmount", "blue"] ]; expect(stateListener.calls.join("\n")).toEqual(expected.join("\n")); });*/ it("should call componentDidUpdate of children first", () => {}); it("should batch unmounts", () => { var outer; class Inner extends React.Component { render() { return
; } componentWillUnmount() { // This should get silently ignored (maybe with a warning), but it // shouldn't break React. outer.setState({ showInner: false }); } } class Outer extends React.Component { state = { showInner: true }; render() { return
{this.state.showInner && }
; } } var container = document.createElement("div"); outer = ReactDOM.render(, container); expect(() => { ReactDOM.unmountComponentAtNode(container); }).not.toThrow(); }); it("should update state when called from child cWRP", function() { const log = []; class Parent extends React.Component { state = { value: "one" }; render() { log.push("parent render " + this.state.value); return ; } } let updated = false; class Child extends React.Component { componentWillReceiveProps() { if (updated) { return; } log.push("child componentWillReceiveProps " + this.props.value); this.props.parent.setState({ value: "two" }); log.push("child componentWillReceiveProps done " + this.props.value); updated = true; } render() { log.push("child render " + this.props.value); return
{this.props.value}
; } } var container = document.createElement("div"); ReactDOM.render(, container); ReactDOM.render(, container); expect(log).toEqual([ "parent render one", "child render one", "parent render one", "child componentWillReceiveProps one", "child componentWillReceiveProps done one", "child render one", "parent render two", "child render two" ]); }); it("should merge state when sCU returns false", function() { const log = []; class Test extends React.Component { state = { a: 0 }; render() { return null; } shouldComponentUpdate(nextProps, nextState) { log.push("scu from " + Object.keys(this.state) + " to " + Object.keys(nextState)); return false; } } const container = document.createElement("div"); const test = ReactDOM.render(, container); test.setState({ b: 0 }); expect(log.length).toBe(1); test.setState({ c: 0 }); expect(log.length).toBe(2); expect(log).toEqual(["scu from a to a,b", "scu from a,b to a,b,c"]); }); it("should treat assigning to this.state inside cWRP as a replaceState, with a warning", () => { spyOn(console, "error"); let ops = []; class Test extends React.Component { state = { step: 1, extra: true }; componentWillReceiveProps() { this.setState({ step: 2 }, () => { // Tests that earlier setState callbacks are not dropped ops.push(`callback -- step: ${this.state.step}, extra: ${!!this.state.extra}`); }); // Treat like replaceState this.state = { step: 3 }; } render() { ops.push(`render -- step: ${this.state.step}, extra: ${!!this.state.extra}`); return null; } } // Mount const container = document.createElement("div"); ReactDOM.render(, container); // Update ReactDOM.render(, container); expect(ops).toEqual(["render -- step: 1, extra: true", "render -- step: 2, extra: false", "callback -- step: 2, extra: false"]); /* expect(console.error.calls.count()).toEqual(1); expect(console.error.calls.argsFor(0)[0]).toEqual( 'Warning: Test.componentWillReceiveProps(): Assigning directly to ' + "this.state is deprecated (except inside a component's constructor). " + 'Use setState instead.', );/**/ }); });