RubyLouvre/anu

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

Summary

Maintainability
D
2 days
Test Coverage
/**
 * 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 React;
let ReactDOM;
let ReactTestUtils;

describe('ReactElement', () => {
    let ComponentClass;
    let originalSymbol;

    beforeEach(() => {
        jest.resetModules();

        // Delete the native Symbol if we have one to ensure we test the
        // unpolyfilled environment.
        originalSymbol = global.Symbol;
        global.Symbol = undefined;

        React = require('react');
        ReactDOM = require('react-dom');
        ReactTestUtils = require('test-utils');
        // NOTE: We're explicitly not using JSX here. This is intended to test
        // classic JS without JSX.
        ComponentClass = class extends React.Component {
            render() {
                return React.createElement('div');
            }
        };
    });

    afterEach(() => {
        global.Symbol = originalSymbol;
    });

    it('uses the fallback value when in an environment without Symbol', () => {
        expect(<div />.$$typeof).toBe(0xeac7);
    });

    it('returns a complete element according to spec', () => {
        const element = React.createFactory(ComponentClass)();
        expect(element.type).toBe(ComponentClass);
        expect(element.key).toBe(null);
        expect(element.ref).toBe(null);
        if (__DEV__) {
            expect(Object.isFrozen(element)).toBe(true);
            expect(Object.isFrozen(element.props)).toBe(true);
        }
        expect(element.props).toEqual({});
    });

    it('should warn when `key` is being accessed on composite element', () => {
        const container = document.createElement('div');
        class Child extends React.Component {
            render() {
                return <div> {this.props.key} </div>;
            }
        }
        class Parent extends React.Component {
            render() {
                return (
                    <div>
                        <Child key="0" />
                        <Child key="1" />
                        <Child key="2" />
                    </div>
                );
            }
        }
        expect(() => ReactDOM.render(<Parent />, container)).toWarnDev(
            'Child: `key` is not a prop. Trying to access it will result ' +
            'in `undefined` being returned. If you need to access the same ' +
            'value within the child component, you should pass it as a different ' +
            'prop. (https://fb.me/react-special-props)',
        );
    });

    it('should warn when `key` is being accessed on a host element', () => {
        const element = <div key="3" />;
        expect(() => void element.props.key).toWarnDev(
            'div: `key` is not a prop. Trying to access it will result ' +
            'in `undefined` being returned. If you need to access the same ' +
            'value within the child component, you should pass it as a different ' +
            'prop. (https://fb.me/react-special-props)',
        );
    });

    it('should warn when `ref` is being accessed', () => {
        const container = document.createElement('div');
        class Child extends React.Component {
            render() {
                return <div> {this.props.ref} </div>;
            }
        }
        class Parent extends React.Component {
            render() {
                return (
                    <div>
                        <Child ref="childElement" />
                    </div>
                );
            }
        }
        expect(() => ReactDOM.render(<Parent />, container)).toWarnDev(
            'Child: `ref` is not a prop. Trying to access it will result ' +
            'in `undefined` being returned. If you need to access the same ' +
            'value within the child component, you should pass it as a different ' +
            'prop. (https://fb.me/react-special-props)',
        );
    });

    it('allows a string to be passed as the type', () => {
        const element = React.createFactory('div')();
        expect(element.type).toBe('div');
        expect(element.key).toBe(null);
        expect(element.ref).toBe(null);
        if (__DEV__) {
            expect(Object.isFrozen(element)).toBe(true);
            expect(Object.isFrozen(element.props)).toBe(true);
        }
        expect(element.props).toEqual({});
    });

    it('returns an immutable element', () => {
        const element = React.createFactory(ComponentClass)();
        if (__DEV__) {
            expect(() => (element.type = 'div')).toThrow();
        } else {
            expect(() => (element.type = 'div')).not.toThrow();
        }
    });

    it('does not reuse the original config object', () => {
        const config = { foo: 1 };
        const element = React.createFactory(ComponentClass)(config);
        expect(element.props.foo).toBe(1);
        config.foo = 2;
        expect(element.props.foo).toBe(1);
    });

    it('does not fail if config has no prototype', () => {
        const config = Object.create(null, { foo: { value: 1, enumerable: true } });
        const element = React.createFactory(ComponentClass)(config);
        expect(element.props.foo).toBe(1);
    });

    it('extracts key and ref from the config', () => {
        const element = React.createFactory(ComponentClass)({
            key: '12',
            ref: '34',
            foo: '56',
        });
        expect(element.type).toBe(ComponentClass);
        expect(element.key).toBe('12');
        expect(element.ref).toBe('34');
        if (__DEV__) {
            expect(Object.isFrozen(element)).toBe(true);
            expect(Object.isFrozen(element.props)).toBe(true);
        }
        expect(element.props).toEqual({ foo: '56' });
    });

    it('extracts null key and ref', () => {
        const element = React.createFactory(ComponentClass)({
            key: null,
            ref: null,
            foo: '12',
        });
        expect(element.type).toBe(ComponentClass);
        expect(element.key).toBe('null');
        expect(element.ref).toBe(null);
        if (__DEV__) {
            expect(Object.isFrozen(element)).toBe(true);
            expect(Object.isFrozen(element.props)).toBe(true);
        }
        expect(element.props).toEqual({ foo: '12' });
    });

    it('ignores undefined key and ref', () => {
        const props = {
            foo: '56',
            key: undefined,
            ref: undefined,
        };
        const element = React.createFactory(ComponentClass)(props);
        expect(element.type).toBe(ComponentClass);
        expect(element.key).toBe(null);
        expect(element.ref).toBe(null);
        if (__DEV__) {
            expect(Object.isFrozen(element)).toBe(true);
            expect(Object.isFrozen(element.props)).toBe(true);
        }
        expect(element.props).toEqual({ foo: '56' });
    });

    it('ignores key and ref warning getters', () => {
        const elementA = React.createElement('div');
        const elementB = React.createElement('div', elementA.props);
        expect(elementB.key).toBe(null);
        expect(elementB.ref).toBe(null);
    });

    it('coerces the key to a string', () => {
        const element = React.createFactory(ComponentClass)({
            key: 12,
            foo: '56',
        });
        expect(element.type).toBe(ComponentClass);
        expect(element.key).toBe('12');
        expect(element.ref).toBe(null);
        if (__DEV__) {
            expect(Object.isFrozen(element)).toBe(true);
            expect(Object.isFrozen(element.props)).toBe(true);
        }
        expect(element.props).toEqual({ foo: '56' });
    });

    it('preserves the owner on the element', () => {
        return
        const Component = React.createFactory(ComponentClass);
        let element;

        class Wrapper extends React.Component {
            render() {
                element = Component();
                return element;
            }
        }

        const instance = ReactTestUtils.renderIntoDocument(
            React.createElement(Wrapper),
        );
        expect(element._owner.stateNode).toBe(instance);
    });

    it('merges an additional argument onto the children prop', () => {
        const a = 1;
        const element = React.createFactory(ComponentClass)(
            {
                children: 'text',
            },
            a,
        );
        expect(element.props.children).toBe(a);
    });

    it('does not override children if no rest args are provided', () => {
        const element = React.createFactory(ComponentClass)({
            children: 'text',
        });
        expect(element.props.children).toBe('text');
    });

    it('overrides children if null is provided as an argument', () => {
        const element = React.createFactory(ComponentClass)(
            {
                children: 'text',
            },
            null,
        );
        expect(element.props.children).toBe(null);
    });

    it('merges rest arguments onto the children prop in an array', () => {
        const a = 1;
        const b = 2;
        const c = 3;
        const element = React.createFactory(ComponentClass)(null, a, b, c);
        expect(element.props.children).toEqual([1, 2, 3]);
    });

    // NOTE: We're explicitly not using JSX here. This is intended to test
    // classic JS without JSX.
    it('allows static methods to be called using the type property', () => {
        class StaticMethodComponentClass extends React.Component {
            render() {
                return React.createElement('div');
            }
        }
        StaticMethodComponentClass.someStaticMethod = () => 'someReturnValue';

        const element = React.createElement(StaticMethodComponentClass);
        expect(element.type.someStaticMethod()).toBe('someReturnValue');
    });

    // NOTE: We're explicitly not using JSX here. This is intended to test
    // classic JS without JSX.
    it('identifies valid elements', () => {
        class Component extends React.Component {
            render() {
                return React.createElement('div');
            }
        }

        expect(React.isValidElement(React.createElement('div'))).toEqual(true);
        expect(React.isValidElement(React.createElement(Component))).toEqual(true);

        expect(React.isValidElement(null)).toEqual(false);
        expect(React.isValidElement(true)).toEqual(false);
        expect(React.isValidElement({})).toEqual(false);
        expect(React.isValidElement('string')).toEqual(false);
        expect(React.isValidElement(React.createFactory('div'))).toEqual(false);
        expect(React.isValidElement(Component)).toEqual(false);
        expect(React.isValidElement({ type: 'div', props: {} })).toEqual(false);

        const jsonElement = JSON.stringify(React.createElement('div'));
        expect(React.isValidElement(JSON.parse(jsonElement))).toBe(true);
    });

    // NOTE: We're explicitly not using JSX here. This is intended to test
    // classic JS without JSX.
    it('is indistinguishable from a plain object', () => {
        const element = React.createElement('div', { className: 'foo' });
        const object = {};
        expect(element.constructor).toBe(object.constructor);
    });

    // NOTE: We're explicitly not using JSX here. This is intended to test
    // classic JS without JSX.
    it('should use default prop value when removing a prop', () => {
        class Component extends React.Component {
            render() {
                return React.createElement('span');
            }
        }
        Component.defaultProps = { fruit: 'persimmon' };

        const container = document.createElement('div');
        const instance = ReactDOM.render(
            React.createElement(Component, { fruit: 'mango' }),
            container,
        );
        expect(instance.props.fruit).toBe('mango');

        ReactDOM.render(React.createElement(Component), container);
        expect(instance.props.fruit).toBe('persimmon');
    });

    // NOTE: We're explicitly not using JSX here. This is intended to test
    // classic JS without JSX.
    it('should normalize props with default values', () => {
        return
        class Component extends React.Component {
            render() {
                return React.createElement('span', null, this.props.prop);
            }
        }
        Component.defaultProps = { prop: 'testKey' };

        const instance = ReactTestUtils.renderIntoDocument(
            React.createElement(Component),
        );
        expect(instance.props.prop).toBe('testKey');

        const inst2 = ReactTestUtils.renderIntoDocument(
            React.createElement(Component, { prop: null }),
        );
        expect(inst2.props.prop).toBe(null);
    });

    it('throws when changing a prop (in dev) after element creation', () => {
        return
        class Outer extends React.Component {
            render() {
                const el = <div className="moo" />;

                if (__DEV__) {
                    expect(function () {
                        el.props.className = 'quack';
                    }).toThrow();
                    expect(el.props.className).toBe('moo');
                } else {
                    el.props.className = 'quack';
                    expect(el.props.className).toBe('quack');
                }

                return el;
            }
        }
        const outer = ReactTestUtils.renderIntoDocument(<Outer color="orange" />);
        if (__DEV__) {
            expect(ReactDOM.findDOMNode(outer).className).toBe('moo');
        } else {
            expect(ReactDOM.findDOMNode(outer).className).toBe('quack');
        }
    });

    it('throws when adding a prop (in dev) after element creation', () => {
        return
        const container = document.createElement('div');
        class Outer extends React.Component {
            render() {
                const el = <div>{this.props.sound}</div>;

                if (__DEV__) {
                    expect(function () {
                        el.props.className = 'quack';
                    }).toThrow();
                    expect(el.props.className).toBe(undefined);
                } else {
                    el.props.className = 'quack';
                    expect(el.props.className).toBe('quack');
                }

                return el;
            }
        }
        Outer.defaultProps = { sound: 'meow' };
        const outer = ReactDOM.render(<Outer />, container);
        expect(ReactDOM.findDOMNode(outer).textContent).toBe('meow');
        if (__DEV__) {
            expect(ReactDOM.findDOMNode(outer).className).toBe('');
        } else {
            expect(ReactDOM.findDOMNode(outer).className).toBe('quack');
        }
    });

    it('does not warn for NaN props', () => {
        return
        class Test extends React.Component {
            render() {
                return <div />;
            }
        }
        const test = ReactTestUtils.renderIntoDocument(<Test value={+undefined} />);
        expect(test.props.value).toBeNaN();
    });

    // NOTE: We're explicitly not using JSX here. This is intended to test
    // classic JS without JSX.
    it('identifies elements, but not JSON, if Symbols are supported', () => {
        // Rudimentary polyfill
        // Once all jest engines support Symbols natively we can swap this to test
        // WITH native Symbols by default.
        const REACT_ELEMENT_TYPE = function () { }; // fake Symbol
        const OTHER_SYMBOL = function () { }; // another fake Symbol
        global.Symbol = function (name) {
            return OTHER_SYMBOL;
        };
        global.Symbol.for = function (key) {
            if (key === 'react.element') {
                return REACT_ELEMENT_TYPE;
            }
            return OTHER_SYMBOL;
        };

        jest.resetModules();

        React = require('react');

        class Component extends React.Component {
            render() {
                return React.createElement('div');
            }
        }

        expect(React.isValidElement(React.createElement('div'))).toEqual(true);
        expect(React.isValidElement(React.createElement(Component))).toEqual(true);

        expect(React.isValidElement(null)).toEqual(false);
        expect(React.isValidElement(true)).toEqual(false);
        expect(React.isValidElement({})).toEqual(false);
        expect(React.isValidElement('string')).toEqual(false);
        expect(React.isValidElement(React.createFactory('div'))).toEqual(false);
        expect(React.isValidElement(Component)).toEqual(false);
        expect(React.isValidElement({ type: 'div', props: {} })).toEqual(false);

        const jsonElement = JSON.stringify(React.createElement('div'));
        if (__DEV__) { //这个在不同nodejs环境有问题
            expect(React.isValidElement(JSON.parse(jsonElement))).toBe(false);
        }
    });
});