RubyLouvre/anu

View on GitHub
packages/core/__tests__/ReactContextValidator-test.js

Summary

Maintainability
C
1 day
Test Coverage
// This test doesn't really have a good home yet. I'm leaving it here since this
// behavior belongs to the old propTypes system yet is currently implemented
// in the core ReactCompositeComponent. It should technically live in core's
// test suite but I'll leave it here to indicate that this is an issue that
// needs to be fixed.

'use strict';

let PropTypes;
let React;
let ReactDOM;
let ReactTestUtils;

describe('ReactContextValidator', () => {
    beforeEach(() => {
        jest.resetModules();

        PropTypes = require('prop-types');
        React = require('react');
        ReactDOM = require('react-dom');
        ReactTestUtils = require('test-utils');
    });

    // TODO: This behavior creates a runtime dependency on propTypes. We should
    // ensure that this is not required for ES6 classes with Flow.

    it('should filter out context not in contextTypes', () => {
        class Component extends React.Component {
            render() {
                return <div />;
            }
        }
        Component.contextTypes = {
            foo: PropTypes.string,
        };

        class ComponentInFooBarContext extends React.Component {
            getChildContext() {
                return {
                    foo: 'abc',
                    bar: 123,
                };
            }

            render() {
                return <Component ref="child" />;
            }
        }
        ComponentInFooBarContext.childContextTypes = {
            foo: PropTypes.string,
            bar: PropTypes.number,
        };

        const instance = ReactTestUtils.renderIntoDocument(<ComponentInFooBarContext />);
        expect(instance.refs.child.context).toEqual({ foo: 'abc' });
    });

    it('should pass next context to lifecycles', () => {
        let actualComponentWillReceiveProps;
        let actualShouldComponentUpdate;
        let actualComponentWillUpdate;

        class Parent extends React.Component {
            getChildContext() {
                return {
                    foo: this.props.foo,
                    bar: 'bar',
                };
            }

            render() {
                return <Component />;
            }
        }
        Parent.childContextTypes = {
            foo: PropTypes.string.isRequired,
            bar: PropTypes.string.isRequired,
        };

        class Component extends React.Component {
            UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
                actualComponentWillReceiveProps = nextContext;
                return true;
            }

            shouldComponentUpdate(nextProps, nextState, nextContext) {
                actualShouldComponentUpdate = nextContext;
                return true;
            }

            UNSAFE_componentWillUpdate(nextProps, nextState, nextContext) {
                actualComponentWillUpdate = nextContext;
            }

            render() {
                return <div />;
            }
        }
        Component.contextTypes = {
            foo: PropTypes.string,
        };

        const container = document.createElement('div');
        ReactDOM.render(<Parent foo="abc" />, container);
        ReactDOM.render(<Parent foo="def" />, container);
        expect(actualComponentWillReceiveProps).toEqual({ foo: 'def' });
        expect(actualShouldComponentUpdate).toEqual({ foo: 'def' });
        expect(actualComponentWillUpdate).toEqual({ foo: 'def' });
    });
    it('React.createContext 通过 Provider 传入数据', () => {
    const container = document.createElement('div');
        const ThemeContext = React.createContext('light');

        class CreateContext extends React.Component {
            render() {
                // Use a Provider to pass the current theme to the tree below.
                // Any component can read it, no matter how deep it is.
                // In this example, we're passing "dark" as the current value.
                return (
                    <ThemeContext.Provider value="dark">
                        <Toolbar />
                    </ThemeContext.Provider>
                );
            }
        }

        // A component in the middle doesn't have to
        // pass the theme down explicitly anymore.
        function Toolbar(props) {
            return (
                <div class="toolbar">
                    <ThemedButton />
                </div>
            );
        }
    var theme = "light"
        function ThemedButton(props) {
            // Use a Consumer to read the current theme context.
            // React will find the closest theme Provider above and use its value.
      // In this example, the current theme is "dark".
     
            return <ThemeContext.Consumer>{theme => <Button {...props} theme={theme} />}</ThemeContext.Consumer>;
        }

        function Button(props) {
      theme = props.theme;
            return (
                <div>
                    正确值应该是传入的dark,现在传入值为:
                    <span style={{ color: 'red' }}>{props.theme}</span>
                </div>
            );
        }
    ReactDOM.render(<CreateContext />, container);
    expect(theme).toBe("dark")
    
    });
    it('should check context types', () => {
        class Component extends React.Component {
            render() {
                return <div />;
            }
        }
        Component.contextTypes = {
            foo: PropTypes.string.isRequired,
        };

        expect(() => ReactTestUtils.renderIntoDocument(<Component />)).toWarnDev(
            'Warning: Failed context type: ' +
                'The context `foo` is marked as required in `Component`, but its value ' +
                'is `undefined`.\n' +
                '    in Component (at **)'
        );

        class ComponentInFooStringContext extends React.Component {
            getChildContext() {
                return {
                    foo: this.props.fooValue,
                };
            }

            render() {
                return <Component />;
            }
        }
        ComponentInFooStringContext.childContextTypes = {
            foo: PropTypes.string,
        };

        // No additional errors expected
        ReactTestUtils.renderIntoDocument(<ComponentInFooStringContext fooValue={'bar'} />);

        class ComponentInFooNumberContext extends React.Component {
            getChildContext() {
                return {
                    foo: this.props.fooValue,
                };
            }

            render() {
                return <Component />;
            }
        }
        ComponentInFooNumberContext.childContextTypes = {
            foo: PropTypes.number,
        };

        expect(() => ReactTestUtils.renderIntoDocument(<ComponentInFooNumberContext fooValue={123} />)).toWarnDev(
            'Warning: Failed context type: ' +
                'Invalid context `foo` of type `number` supplied ' +
                'to `Component`, expected `string`.\n' +
                '    in Component (at **)\n' +
                '    in ComponentInFooNumberContext (at **)'
        );
    });

    it('should check child context types', () => {
        class Component extends React.Component {
            getChildContext() {
                return this.props.testContext;
            }

            render() {
                return <div />;
            }
        }
        Component.childContextTypes = {
            foo: PropTypes.string.isRequired,
            bar: PropTypes.number,
        };

        expect(() => ReactTestUtils.renderIntoDocument(<Component testContext={{ bar: 123 }} />)).toWarnDev(
            'Warning: Failed child context type: ' +
                'The child context `foo` is marked as required in `Component`, but its ' +
                'value is `undefined`.\n' +
                '    in Component (at **)'
        );

        expect(() => ReactTestUtils.renderIntoDocument(<Component testContext={{ foo: 123 }} />)).toWarnDev(
            'Warning: Failed child context type: ' +
                'Invalid child context `foo` of type `number` ' +
                'supplied to `Component`, expected `string`.\n' +
                '    in Component (at **)'
        );

        // No additional errors expected
        ReactTestUtils.renderIntoDocument(<Component testContext={{ foo: 'foo', bar: 123 }} />);

        ReactTestUtils.renderIntoDocument(<Component testContext={{ foo: 'foo' }} />);
    });

    // TODO (bvaughn) Remove this test and the associated behavior in the future.
    // It has only been added in Fiber to match the (unintentional) behavior in Stack.
    it('should warn (but not error) if getChildContext method is missing', () => {
        class ComponentA extends React.Component {
            static childContextTypes = {
                foo: PropTypes.string.isRequired,
            };
            render() {
                return <div />;
            }
        }
        class ComponentB extends React.Component {
            static childContextTypes = {
                foo: PropTypes.string.isRequired,
            };
            render() {
                return <div />;
            }
        }

        expect(() => ReactTestUtils.renderIntoDocument(<ComponentA />)).toWarnDev(
            'Warning: ComponentA.childContextTypes is specified but there is no ' +
                'getChildContext() method on the instance. You can either define ' +
                'getChildContext() on ComponentA or remove childContextTypes from it.'
        );

        // Warnings should be deduped by component type
        ReactTestUtils.renderIntoDocument(<ComponentA />);

        expect(() => ReactTestUtils.renderIntoDocument(<ComponentB />)).toWarnDev(
            'Warning: ComponentB.childContextTypes is specified but there is no ' +
                'getChildContext() method on the instance. You can either define ' +
                'getChildContext() on ComponentB or remove childContextTypes from it.'
        );
    });

    // TODO (bvaughn) Remove this test and the associated behavior in the future.
    // It has only been added in Fiber to match the (unintentional) behavior in Stack.
    it('should pass parent context if getChildContext method is missing', () => {
        class ParentContextProvider extends React.Component {
            static childContextTypes = {
                foo: PropTypes.string,
            };
            getChildContext() {
                return {
                    foo: 'FOO',
                };
            }
            render() {
                return <MiddleMissingContext />;
            }
        }

        class MiddleMissingContext extends React.Component {
            static childContextTypes = {
                bar: PropTypes.string.isRequired,
            };
            render() {
                return <ChildContextConsumer />;
            }
        }

        let childContext;
        class ChildContextConsumer extends React.Component {
            render() {
                childContext = this.context;
                return <div />;
            }
        }
        ChildContextConsumer.contextTypes = {
            bar: PropTypes.string.isRequired,
            foo: PropTypes.string.isRequired,
        };

        expect(() => ReactTestUtils.renderIntoDocument(<ParentContextProvider />)).toWarnDev([
            'Warning: MiddleMissingContext.childContextTypes is specified but there is no getChildContext() method on the ' +
                'instance. You can either define getChildContext() on MiddleMissingContext or remove childContextTypes from ' +
                'it.',
            'Warning: Failed context type: The context `bar` is marked as required in `ChildContextConsumer`, but its ' +
                'value is `undefined`.',
        ]);
        expect(childContext.bar).toBeUndefined();
        expect(childContext.foo).toBe('FOO');
    });
    it('setState后子组件的context处理', () => {
        const container = document.createElement('div');

        class App extends React.Component {
            state = {
                text: 111,
            };
            getChildContext() {
                return {
                    table: {
                        title: 'title',
                    },
                };
            }
            static childContextTypes = {
                table: function() {
                    return null;
                },
            };
            render() {
                return (
                    <div>
                        <Head ref="head" className="xxx" />
                    </div>
                );
            }
        }

        class Head extends React.Component {
            constructor(a, b) {
                super(a, b);
                this.state = {
                    a: 3333,
                };
            }
            render() {
                return (
                    <strong>
                        {this.state.a}
                        <b>{this.context.table.title}</b>
                    </strong>
                );
            }
        }

        Head.contextTypes = {
            table: function() {
                return null;
            },
        };

        var a = ReactDOM.render(<App id="app" />, container);
        expect(container.textContent.trim()).toBe('3333title');
        a.refs.head.setState({
            a: 4444,
        });

        expect(container.textContent.trim()).toBe('4444title');
    });
});