Permalink
Browse files

[Fiber] Implement test renderer (#8628)

* ReactTestRenderer move current impl to stack dir

* ReactTestRenderer on fiber: commence!

* ReactTestRenderer: most non-ref/non-public-instance tests are passing

* Move ReactTestFiberComponent functions from Renderer to Component file

* test renderer: get rid of private root containers and root Maps

* TestRenderer: switch impl based on ReactDOMFeatureFlag.useFiber

* ReactTestRenderer: inline component creation

* ReactTestRenderer: return to pristine original glory (+ Fiber for error order difference)

* TestRendererFiber: use a simple class as TestComponentInstances

* Add `getPublicInstance` to support TestRenderer `createNodeMock`

* Rename files to end. Update for `mountContainer->createContainer` change

* test renderer return same object to prevent unnecessary context pushing/popping

* Fiber HostConfig add getPublicInstance. This should be the identity fn everywhere except the test renderer

* appease flow

* Initial cleanup from sleepy work

* unstable_batchedUpdates

* Stack test renderer: cache nodeMock to not call on unmount

* add public instance type parameter to the reconciler

* test renderer: set _nodeMock when mounted

* More cleanup

* Add test cases for root fragments and (maybe?) root text nodes

* Fix the npm package build

Explicitly require the Stack version by default.
Add a separate entry point for Fiber.

We don't add fiber.js to the package yet since it's considered internal until React 16.

* Relax the ref type from Object to mixed

This seems like the most straightforward way to support getPublicInstance for test renderer.

* Remove accidental newline

* test renderer: unify TestComponent and TestContainer, handle root updates

* Remove string/number serialization attempts since Fiber ensures all textInstances are strings

* Return full fragments in toJSON

* Test Renderer remove TestComponent instances for simple objects

* Update babylon for exact object type syntax

* Use $$typeof because clarity > punching ducks.

* Minor Flow annotation tweaks

* Tweak style, types, and naming

* Fix typo
  • Loading branch information...
1 parent 837835d commit 2da35fcae888752948454dd04e9573cc7bdba8f5 @iamdustan iamdustan committed with gaearon Jan 11, 2017
View
@@ -33,7 +33,7 @@
"babel-plugin-transform-react-jsx-source": "^6.8.0",
"babel-preset-react": "^6.5.0",
"babel-traverse": "^6.9.0",
- "babylon": "6.8.0",
+ "babylon": "6.15.0",
"browserify": "^13.0.0",
"bundle-collapser": "^1.1.1",
"coffee-script": "^1.8.0",
@@ -0,0 +1,3 @@
+'use strict';
+
+module.exports = require('./lib/ReactTestRendererFiber');
@@ -1,3 +1,3 @@
'use strict';
-module.exports = require('./lib/ReactTestRenderer');
+module.exports = require('./lib/ReactTestRendererStack');
@@ -1675,6 +1675,7 @@ src/renderers/testing/__tests__/ReactTestRenderer-test.js
* renders a simple component
* renders a top-level empty component
* exposes a type flag
+* can render a composite component
* renders some basics with an update
* exposes the instance
* updates types
@@ -1687,6 +1688,9 @@ src/renderers/testing/__tests__/ReactTestRenderer-test.js
* supports unmounting inner instances
* supports updates when using refs
* supports error boundaries
+* can update text nodes
+* can update text nodes when rendered as root
+* can render and update root fragments
src/shared/utils/__tests__/KeyEscapeUtils-test.js
* should properly escape and wrap user defined keys
@@ -464,6 +464,10 @@ const ARTRenderer = ReactFiberReconciler({
return false;
},
+ getPublicInstance(instance) {
+ return instance;
+ },
+
insertBefore(parentInstance, child, beforeChild) {
invariant(
child !== beforeChild,
@@ -137,6 +137,10 @@ var DOMRenderer = ReactFiberReconciler({
return getChildNamespace(parentNamespace, type);
},
+ getPublicInstance(instance) {
+ return instance;
+ },
+
prepareForCommit() : void {
eventsEnabled = ReactBrowserEventEmitter.isEnabled();
selectionInformation = ReactInputSelection.getSelectionInformation();
@@ -232,6 +232,10 @@ const NativeRenderer = ReactFiberReconciler({
return emptyObject;
},
+ getPublicInstance(instance) {
+ return instance;
+ },
+
insertBefore(
parentInstance : Instance | Container,
child : Instance | TextInstance,
@@ -54,6 +54,10 @@ var NoopRenderer = ReactFiberReconciler({
return emptyObject;
},
+ getPublicInstance(instance) {
+ return instance;
+ },
+
createInstance(type : string, props : Props) : Instance {
const inst = {
id: instanceCounter++,
@@ -102,7 +102,7 @@ export type Fiber = {
// The ref last used to attach this node.
// I'll avoid adding an owner field for prod and model that as functions.
- ref: null | (((handle : ?Object) => void) & { _stringRef: ?string }),
+ ref: null | (((handle : mixed) => void) & { _stringRef: ?string }),
// Input is the data coming into process this fiber. Arguments. Props.
pendingProps: any, // This type will be more specific once we overload the tag.
@@ -71,8 +71,8 @@ if (__DEV__) {
var warnedAboutStatelessRefs = {};
}
-module.exports = function<T, P, I, TI, C, CX>(
- config : HostConfig<T, P, I, TI, C, CX>,
+module.exports = function<T, P, I, TI, PI, C, CX>(
+ config : HostConfig<T, P, I, TI, PI, C, CX>,
hostContext : HostContext<C, CX>,
scheduleUpdate : (fiber : Fiber, priorityLevel : PriorityLevel) => void,
getPriorityContext : () => PriorityLevel,
@@ -34,8 +34,8 @@ var {
ContentReset,
} = require('ReactTypeOfSideEffect');
-module.exports = function<T, P, I, TI, C, CX>(
- config : HostConfig<T, P, I, TI, C, CX>,
+module.exports = function<T, P, I, TI, PI, C, CX>(
+ config : HostConfig<T, P, I, TI, PI, C, CX>,
hostContext : HostContext<C, CX>,
captureError : (failedFiber : Fiber, error: Error) => ?Fiber
) {
@@ -48,6 +48,7 @@ module.exports = function<T, P, I, TI, C, CX>(
appendChild,
insertBefore,
removeChild,
+ getPublicInstance,
} = config;
const {
@@ -462,7 +463,7 @@ module.exports = function<T, P, I, TI, C, CX>(
}
const ref = finishedWork.ref;
if (ref) {
- const instance = finishedWork.stateNode;
+ const instance = getPublicInstance(finishedWork.stateNode);
ref(instance);
}
}
@@ -46,8 +46,8 @@ if (__DEV__) {
var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber');
}
-module.exports = function<T, P, I, TI, C, CX>(
- config : HostConfig<T, P, I, TI, C, CX>,
+module.exports = function<T, P, I, TI, PI, C, CX>(
+ config : HostConfig<T, P, I, TI, PI, C, CX>,
hostContext : HostContext<C, CX>,
) {
const {
@@ -34,8 +34,8 @@ export type HostContext<C, CX> = {
resetHostContainer() : void,
};
-module.exports = function<T, P, I, TI, C, CX>(
- config : HostConfig<T, P, I, TI, C, CX>
+module.exports = function<T, P, I, TI, PI, C, CX>(
+ config : HostConfig<T, P, I, TI, PI, C, CX>
) : HostContext<C, CX> {
const {
getChildHostContext,
@@ -43,10 +43,11 @@ export type Deadline = {
type OpaqueNode = Fiber;
-export type HostConfig<T, P, I, TI, C, CX> = {
+export type HostConfig<T, P, I, TI, PI, C, CX> = {
getRootHostContext(rootContainerInstance : C) : CX,
getChildHostContext(parentHostContext : CX, type : T) : CX,
+ getPublicInstance(instance : I | TI) : PI,
createInstance(type : T, props : P, rootContainerInstance : C, hostContext : CX, internalInstanceHandle : OpaqueNode) : I,
appendInitialChild(parentInstance : I, child : I | TI) : void,
@@ -101,7 +102,7 @@ getContextForSubtree._injectFiber(function(fiber : Fiber) {
parentContext;
});
-module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C, CX>) : Reconciler<C, I, TI> {
+module.exports = function<T, P, I, TI, PI, C, CX>(config : HostConfig<T, P, I, TI, PI, C, CX>) : Reconciler<C, I, TI> {
var {
scheduleUpdate,
@@ -74,7 +74,7 @@ if (__DEV__) {
var timeHeuristicForUnitOfWork = 1;
-module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C, CX>) {
+module.exports = function<T, P, I, TI, PI, C, CX>(config : HostConfig<T, P, I, TI, PI, C, CX>) {
const hostContext = ReactFiberHostContext(config);
const { popHostContainer, popHostContext, resetHostContainer } = hostContext;
const { beginWork, beginFailedWork } = ReactFiberBeginWork(
@@ -1,157 +1,18 @@
/**
- * 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
* 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 ReactTestRenderer
- * @preventMunge
- * @flow
*/
'use strict';
-var ReactComponentEnvironment = require('ReactComponentEnvironment');
-var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
-var ReactEmptyComponent = require('ReactEmptyComponent');
-var ReactMultiChild = require('ReactMultiChild');
-var ReactHostComponent = require('ReactHostComponent');
-var ReactTestMount = require('ReactTestMount');
-var ReactTestReconcileTransaction = require('ReactTestReconcileTransaction');
-var ReactUpdates = require('ReactUpdates');
-var ReactTestTextComponent = require('ReactTestTextComponent');
-var ReactTestEmptyComponent = require('ReactTestEmptyComponent');
-var invariant = require('invariant');
+const ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
-import type { ReactElement } from 'ReactElementType';
-import type { ReactInstance } from 'ReactInstanceType';
-
-type ReactTestRendererJSON = {
- type: string,
- props: { [propName: string]: string },
- children: null | Array<string | ReactTestRendererJSON>,
- $$typeof?: any
-}
-
-/**
- * Drill down (through composites and empty components) until we get a native or
- * native text component.
- *
- * This is pretty polymorphic but unavoidable with the current structure we have
- * for `_renderedChildren`.
- */
-function getRenderedHostOrTextFromComponent(component) {
- var rendered;
- while ((rendered = component._renderedComponent)) {
- component = rendered;
- }
- return component;
-}
-
-class ReactTestComponent {
- _currentElement: ReactElement;
- _renderedChildren: null | Object;
- _topLevelWrapper: null | ReactInstance;
- _hostContainerInfo: null | Object;
-
- constructor(element: ReactElement) {
- this._currentElement = element;
- this._renderedChildren = null;
- this._topLevelWrapper = null;
- this._hostContainerInfo = null;
- }
-
- mountComponent(
- transaction: ReactTestReconcileTransaction,
- nativeParent: null | ReactTestComponent,
- hostContainerInfo: Object,
- context: Object,
- ) {
- var element = this._currentElement;
- this._hostContainerInfo = hostContainerInfo;
- // $FlowFixMe https://github.com/facebook/flow/issues/1805
- this.mountChildren(element.props.children, transaction, context);
- }
-
- receiveComponent(
- nextElement: ReactElement,
- transaction: ReactTestReconcileTransaction,
- context: Object,
- ) {
- this._currentElement = nextElement;
- // $FlowFixMe https://github.com/facebook/flow/issues/1805
- this.updateChildren(nextElement.props.children, transaction, context);
- }
-
- getPublicInstance(): Object {
- var element = this._currentElement;
- var hostContainerInfo = this._hostContainerInfo;
- invariant(
- hostContainerInfo,
- 'hostContainerInfo should be populated before ' +
- 'getPublicInstance is called.'
- );
- return hostContainerInfo.createNodeMock(element);
- }
-
- toJSON(): ReactTestRendererJSON {
- // not using `children`, but I don't want to rewrite without destructuring
- // eslint-disable-next-line no-unused-vars
- var {children, ...props} = this._currentElement.props;
- var childrenJSON = [];
- for (var key in this._renderedChildren) {
- var inst = this._renderedChildren[key];
- inst = getRenderedHostOrTextFromComponent(inst);
- var json = inst.toJSON();
- if (json !== undefined) {
- childrenJSON.push(json);
- }
- }
- var object: ReactTestRendererJSON = {
- type: this._currentElement.type,
- props: props,
- children: childrenJSON.length ? childrenJSON : null,
- };
- Object.defineProperty(object, '$$typeof', {
- value: Symbol.for('react.test.json'),
- });
- return object;
- }
-
- getHostNode(): void {}
- unmountComponent(safely, skipLifecycle): void {
- // $FlowFixMe https://github.com/facebook/flow/issues/1805
- this.unmountChildren(safely, skipLifecycle);
- }
-}
-
-Object.assign(ReactTestComponent.prototype, ReactMultiChild);
-
-// =============================================================================
-
-ReactUpdates.injection.injectReconcileTransaction(
- ReactTestReconcileTransaction
-);
-ReactUpdates.injection.injectBatchingStrategy(ReactDefaultBatchingStrategy);
-
-ReactHostComponent.injection.injectGenericComponentClass(ReactTestComponent);
-ReactHostComponent.injection.injectTextComponentClass(ReactTestTextComponent);
-ReactEmptyComponent.injection.injectEmptyComponentFactory(function() {
- return new ReactTestEmptyComponent();
-});
-
-ReactComponentEnvironment.injection.injectEnvironment({
- processChildrenUpdates: function() {},
- replaceNodeWithMarkup: function() {},
-});
-
-var ReactTestRenderer = {
- create: ReactTestMount.render,
- /* eslint-disable camelcase */
- unstable_batchedUpdates: ReactUpdates.batchedUpdates,
- /* eslint-enable camelcase */
-};
-
-module.exports = ReactTestRenderer;
+module.exports = ReactDOMFeatureFlags.useFiber
+ ? require('ReactTestRendererFiber')
+ : require('ReactTestRendererStack');
Oops, something went wrong.

0 comments on commit 2da35fc

Please sign in to comment.