diff --git a/scripts/fiber/tests-failing.txt b/scripts/fiber/tests-failing.txt index 884b8c3dc5eb5..0fa1de3ee5bee 100644 --- a/scripts/fiber/tests-failing.txt +++ b/scripts/fiber/tests-failing.txt @@ -66,3 +66,8 @@ src/renderers/shared/shared/__tests__/ReactUpdates-test.js src/renderers/shared/shared/__tests__/refs-test.js * Should increase refs with an increase in divs + +src/test/__tests__/ReactTestUtils-test.js +* can access the mounted component instance +* can setState in componentWillMount when shallow rendering +* can pass context when shallowly rendering diff --git a/scripts/fiber/tests-passing.txt b/scripts/fiber/tests-passing.txt index e1415a100ab38..4a89e34a63074 100644 --- a/scripts/fiber/tests-passing.txt +++ b/scripts/fiber/tests-passing.txt @@ -1736,11 +1736,8 @@ src/test/__tests__/ReactTestUtils-test.js * can shallow render to null * can shallow render with a ref * lets you update shallowly rendered components -* can access the mounted component instance * can shallowly render components with contextTypes * can shallowly render components with ref as function -* can setState in componentWillMount when shallow rendering -* can pass context when shallowly rendering * can fail context when shallowly rendering * can scryRenderedDOMComponentsWithClass with TextComponent * can scryRenderedDOMComponentsWithClass with className contains \n diff --git a/src/test/ReactShallowRenderer.js b/src/test/ReactShallowRenderer.js index ee50d9d6efc66..64f21ad0322e0 100644 --- a/src/test/ReactShallowRenderer.js +++ b/src/test/ReactShallowRenderer.js @@ -1,5 +1,5 @@ /** - * Copyright 2013-present, Facebook, Inc. + * Copyright (c) 2013-present Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the @@ -7,139 +7,12 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule ReactShallowRenderer - * @preventMunge */ 'use strict'; -var React = require('React'); -var ReactDOMInjection = require('ReactDOMInjection'); -var ReactDOMStackInjection = require('ReactDOMStackInjection'); -var ReactCompositeComponent = require('ReactCompositeComponent'); -var ReactReconciler = require('ReactReconciler'); -var ReactUpdates = require('ReactUpdates'); +const ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); -var emptyObject = require('emptyObject'); -var getNextDebugID = require('getNextDebugID'); -var invariant = require('invariant'); - -class NoopInternalComponent { - constructor(element) { - this._renderedOutput = element; - this._currentElement = element; - - if (__DEV__) { - this._debugID = getNextDebugID(); - } - } - mountComponent() {} - receiveComponent(element) { - this._renderedOutput = element; - this._currentElement = element; - } - unmountComponent() {} - getHostNode() { - return undefined; - } - getPublicInstance() { - return null; - } -} - -var ShallowComponentWrapper = function(element) { - // TODO: Consolidate with instantiateReactComponent - if (__DEV__) { - this._debugID = getNextDebugID(); - } - - this.construct(element); -}; -Object.assign( - ShallowComponentWrapper.prototype, - ReactCompositeComponent, { - _constructComponent: - ReactCompositeComponent._constructComponentWithoutOwner, - _instantiateReactComponent: function(element) { - return new NoopInternalComponent(element); - }, - _replaceNodeWithMarkup: function() {}, - _renderValidatedComponent: - ReactCompositeComponent - ._renderValidatedComponentWithoutOwnerOrContext, - } -); - -function _batchedRender(renderer, element, context) { - var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(true); - renderer._render(element, transaction, context); - ReactUpdates.ReactReconcileTransaction.release(transaction); -} - -class ReactShallowRenderer { - _instance = null; - getMountedInstance() { - return this._instance ? this._instance._instance : null; - } - render(element, context) { - // Ensure we've done the default injections. This might not be true in the - // case of a simple test that only requires React and the TestUtils in - // conjunction with an inline-requires transform. - ReactDOMInjection.inject(); - ReactDOMStackInjection.inject(); - - invariant( - React.isValidElement(element), - 'ReactShallowRenderer render(): Invalid component element.%s', - typeof element === 'function' ? - ' Instead of passing a component class, make sure to instantiate ' + - 'it by passing it to React.createElement.' : - '' - ); - invariant( - typeof element.type !== 'string', - 'ReactShallowRenderer render(): Shallow rendering works only with custom ' + - 'components, not primitives (%s). Instead of calling `.render(el)` and ' + - 'inspecting the rendered output, look at `el.props` directly instead.', - element.type - ); - - if (!context) { - context = emptyObject; - } - ReactUpdates.batchedUpdates(_batchedRender, this, element, context); - - return this.getRenderOutput(); - } - getRenderOutput() { - return ( - (this._instance && this._instance._renderedComponent && - this._instance._renderedComponent._renderedOutput) - || null - ); - } - unmount() { - if (this._instance) { - ReactReconciler.unmountComponent( - this._instance, - false, /* safely */ - false /* skipLifecycle */ - ); - } - } - _render(element, transaction, context) { - if (this._instance) { - ReactReconciler.receiveComponent( - this._instance, - element, - transaction, - context - ); - } else { - var instance = new ShallowComponentWrapper(element); - ReactReconciler.mountComponent(instance, transaction, null, null, context, 0); - this._instance = instance; - } - } -} - -module.exports = ReactShallowRenderer; +module.exports = ReactDOMFeatureFlags.useFiber + ? require('ReactShallowRendererFiber') + : require('ReactShallowRendererStack'); diff --git a/src/test/ReactShallowRendererFiber.js b/src/test/ReactShallowRendererFiber.js new file mode 100644 index 0000000000000..41d610e0962e2 --- /dev/null +++ b/src/test/ReactShallowRendererFiber.js @@ -0,0 +1,165 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactShallowRendererFiber + * @flow + */ + +'use strict'; + +const React = require('React'); +const emptyObject = require('emptyObject'); +const ReactFiberReconciler = require('ReactFiberReconciler'); +const ReactElementSymbol = require('ReactElementSymbol'); +const invariant = require('invariant'); + +type Props = { + children : Array, +}; + +type Container = { + element : Instance | TextInstance | {}, + context : Object, +}; + +type Instance = {| + type : string, + $$typeof : number, + props : Object, + ref : null, + key : null, + _store : Object, + _owner : null, +|}; + +type TextInstance = {| + text : string, +|}; + +const reconciler = ReactFiberReconciler({ + getRootHostContext(rootContainerInstance : Container) { + return rootContainerInstance.context || emptyObject; + }, + + getChildHostContext(parentHostContext, type) { + return parentHostContext; + }, + + getPublicInstance(instance) { + return instance; + }, + + createInstance(type : string, props : Props, hostContext, internalInstanceHandle) : Instance { + const { children, ...instanceProps } = props; + + if (children) { + instanceProps.children = []; + } + + return { + type, + $$typeof: ReactElementSymbol, + props: instanceProps, + key: null, + _owner: null, + _store: {}, + ref: null, + }; + }, + + appendInitialChild(parentInstance, child) { + parentInstance.props.children.push(child); + }, + finalizeInitialChildren(parentInstance, type, props, rootContainerInstance) : boolean { + return false; + }, + + prepareUpdate(instance, type, oldProps, newProps, hostContext) : boolean { + return true; + }, + commitUpdate(instance, type, oldProps, newProps, rootContainerInstance, internalInstanceHandle) { + instance.props = newProps; + }, + commitMount(instance, type, newProps, rootContainerInstance, internalInstanceHandle) {}, + + shouldSetTextContent(props : Props) : boolean { + return ( + typeof props.children === 'string' || + typeof props.children === 'number' + ); + }, + resetTextContent(instance) {}, + + createTextInstance(text : string) : TextInstance { + return { text }; + }, + commitTextUpdate(textInstance, oldText, newText) {}, + + appendChild(parentInstance : Container | Instance, child : Instance | TextInstance) { + if (parentInstance.element) { + parentInstance.element = child; + } + }, + insertBefore(parentInstance, child, beforeChild) {}, + removeChild(parentInstance, child) {}, + + prepareForCommit() {}, + resetAfterCommit(commitInfo) {}, + + scheduleAnimationCallback: window.requestAnimaionFrame, + scheduleDeferredCallback: window.requestIdleCallback, + useSyncScheduling: true, +}); + +class ReactShallowRendererFiber { + root_ = (null : ?Object) + + render(element : React$Element<*>, context = emptyObject) { + invariant( + React.isValidElement(element), + 'ReactShallowRenderer render(): Invalid component element.%s', + typeof element === 'function' ? + ' Instead of passing a component class, make sure to instantiate ' + + 'it by passing it to React.createElement.' : + '' + ); + invariant( + typeof element.type !== 'string', + 'ReactShallowRenderer render(): Shallow rendering works only with custom ' + + 'components, not primitives (%s). Instead of calling `.render(el)` and ' + + 'inspecting the rendered output, look at `el.props` directly instead.', + element.type + ); + + if (this.root_ == null) { + this.root_ = reconciler.createContainer({ + context, + element: emptyObject, + }); + } + + reconciler.updateContainer(element, this.root_, null, null); + return this.getRenderOutput(); + } + + unmount() { + if (this.root_ != null) { + reconciler.updateContainer(null, this.root_, null, () => { + this.root_ = null; + }); + } + } + + getRenderOutput() { + return this.root_ != null + ? reconciler.findHostInstance(this.root_) + : null; + } +} + +module.exports = ReactShallowRendererFiber; diff --git a/src/test/ReactShallowRendererStack.js b/src/test/ReactShallowRendererStack.js new file mode 100644 index 0000000000000..81f787b33360d --- /dev/null +++ b/src/test/ReactShallowRendererStack.js @@ -0,0 +1,143 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactShallowRendererStack + * @preventMunge + */ + +'use strict'; + +var React = require('React'); +var ReactDOMInjection = require('ReactDOMInjection'); +var ReactDOMStackInjection = require('ReactDOMStackInjection'); +var ReactCompositeComponent = require('ReactCompositeComponent'); +var ReactReconciler = require('ReactReconciler'); +var ReactUpdates = require('ReactUpdates'); + +var emptyObject = require('emptyObject'); +var getNextDebugID = require('getNextDebugID'); +var invariant = require('invariant'); + +class NoopInternalComponent { + constructor(element) { + this._renderedOutput = element; + this._currentElement = element; + + if (__DEV__) { + this._debugID = getNextDebugID(); + } + } + mountComponent() {} + receiveComponent(element) { + this._renderedOutput = element; + this._currentElement = element; + } + unmountComponent() {} + getHostNode() { + return undefined; + } + getPublicInstance() { + return null; + } +} + +var ShallowComponentWrapper = function(element) { + // TODO: Consolidate with instantiateReactComponent + if (__DEV__) { + this._debugID = getNextDebugID(); + } + + this.construct(element); +}; +Object.assign( + ShallowComponentWrapper.prototype, + ReactCompositeComponent, { + _constructComponent: ReactCompositeComponent._constructComponentWithoutOwner, + _instantiateReactComponent(element) { + return new NoopInternalComponent(element); + }, + _replaceNodeWithMarkup() {}, + _renderValidatedComponent: ReactCompositeComponent + ._renderValidatedComponentWithoutOwnerOrContext, + } +); + +function _batchedRender(renderer, element, context) { + var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(true); + renderer._render(element, transaction, context); + ReactUpdates.ReactReconcileTransaction.release(transaction); +} + +class ReactShallowRendererStack { + _instance = null; + getMountedInstance() { + return this._instance ? this._instance._instance : null; + } + render(element, context) { + // Ensure we've done the default injections. This might not be true in the + // case of a simple test that only requires React and the TestUtils in + // conjunction with an inline-requires transform. + ReactDOMInjection.inject(); + ReactDOMStackInjection.inject(); + + invariant( + React.isValidElement(element), + 'ReactShallowRenderer render(): Invalid component element.%s', + typeof element === 'function' ? + ' Instead of passing a component class, make sure to instantiate ' + + 'it by passing it to React.createElement.' : + '' + ); + invariant( + typeof element.type !== 'string', + 'ReactShallowRenderer render(): Shallow rendering works only with custom ' + + 'components, not primitives (%s). Instead of calling `.render(el)` and ' + + 'inspecting the rendered output, look at `el.props` directly instead.', + element.type + ); + + if (!context) { + context = emptyObject; + } + ReactUpdates.batchedUpdates(_batchedRender, this, element, context); + + return this.getRenderOutput(); + } + getRenderOutput() { + return ( + (this._instance && this._instance._renderedComponent && + this._instance._renderedComponent._renderedOutput) + || null + ); + } + unmount() { + if (this._instance) { + ReactReconciler.unmountComponent( + this._instance, + false, /* safely */ + false /* skipLifecycle */ + ); + } + } + _render(element, transaction, context) { + if (this._instance) { + ReactReconciler.receiveComponent( + this._instance, + element, + transaction, + context + ); + } else { + var instance = new ShallowComponentWrapper(element); + ReactReconciler.mountComponent(instance, transaction, null, null, context, 0); + this._instance = instance; + } + } +} + +module.exports = ReactShallowRendererStack;