diff --git a/flow/react-native-host-hooks.js b/flow/react-native-host-hooks.js index 557fa44145ab..9b225116a647 100644 --- a/flow/react-native-host-hooks.js +++ b/flow/react-native-host-hooks.js @@ -86,3 +86,35 @@ declare module 'UIManager' { declare module 'View' { declare var exports : typeof React$Component; } + +declare module 'RTManager' { + declare function createNode( + tag : number, + classType : string, + props : ?Object, + ) : void; + declare function appendChildToDetachedParent( + parentTag : number, + childTag : number, + ) : void; + + declare function beginUpdates() : void; + + declare function appendChild( + parentTag : number, + childTag : number, + ) : void; + declare function prependChild( + childTag : number, + beforeTag : number, + ) : void; + declare function deleteChild( + childTag : number, + ) : void; + declare function updateNode( + tag : number, + props : ?Object, + ) : void; + + declare function completeUpdates() : void; +} diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js index c31c81991724..0966367f274b 100644 --- a/scripts/rollup/bundles.js +++ b/scripts/rollup/bundles.js @@ -288,6 +288,40 @@ const bundles = [ useFiber: true, }, + /******* React Native *******/ + { + babelOpts: babelOptsReact, + bundleTypes: [RN_DEV, RN_PROD], + config: { + destDir: 'build/', + moduleName: 'ReactNativeRTFiber', + sourceMap: false, + }, + entry: 'src/renderers/native-rt/ReactNativeRTFiberEntry', + externals: [ + 'ExceptionsManager', + 'InitializeCore', + 'Platform', + 'RCTEventEmitter', + 'RTManager', + 'prop-types/checkPropTypes', + ], + hasteName: 'ReactNativeRTFiber', + isRenderer: true, + label: 'native-rt-fiber', + manglePropertiesOnProd: false, + name: 'react-native-rt-renderer', + paths: [ + 'src/renderers/native/**/*.js', // This is used since we reuse the error dialog code + 'src/renderers/native-rt/**/*.js', + 'src/renderers/shared/**/*.js', + + 'src/ReactVersion.js', + 'src/shared/**/*.js', + ], + useFiber: true, + }, + /******* React Test Renderer *******/ { babelOpts: babelOptsReact, diff --git a/scripts/rollup/results.json b/scripts/rollup/results.json index 11255f58593e..4b5bf0ea2ac2 100644 --- a/scripts/rollup/results.json +++ b/scripts/rollup/results.json @@ -25,28 +25,28 @@ "gzip": 6703 }, "react-dom.development.js (UMD_DEV)": { - "size": 613875, - "gzip": 141842 + "size": 614249, + "gzip": 141958 }, "react-dom.production.min.js (UMD_PROD)": { - "size": 99469, - "gzip": 31443 + "size": 99500, + "gzip": 31459 }, "react-dom.development.js (NODE_DEV)": { - "size": 576136, - "gzip": 133165 + "size": 576510, + "gzip": 133279 }, "react-dom.production.min.js (NODE_PROD)": { - "size": 104422, - "gzip": 32815 + "size": 104453, + "gzip": 32832 }, "ReactDOMFiber-dev.js (FB_DEV)": { - "size": 573085, - "gzip": 132629 + "size": 573391, + "gzip": 132712 }, "ReactDOMFiber-prod.js (FB_PROD)": { - "size": 409433, - "gzip": 91568 + "size": 409575, + "gzip": 91600 }, "react-dom-test-utils.development.js (NODE_DEV)": { "size": 41660, @@ -81,36 +81,36 @@ "gzip": 15465 }, "react-dom-server.browser.development.js (UMD_DEV)": { - "size": 129517, - "gzip": 33434 + "size": 129604, + "gzip": 33468 }, "react-dom-server.browser.production.min.js (UMD_PROD)": { - "size": 14948, - "gzip": 5844 + "size": 14959, + "gzip": 5848 }, "react-dom-server.browser.development.js (NODE_DEV)": { - "size": 99439, - "gzip": 26329 + "size": 99526, + "gzip": 26362 }, "react-dom-server.browser.production.min.js (NODE_PROD)": { - "size": 14878, + "size": 14889, "gzip": 5844 }, "ReactDOMServer-dev.js (FB_DEV)": { - "size": 98860, - "gzip": 26238 + "size": 98947, + "gzip": 26272 }, "ReactDOMServer-prod.js (FB_PROD)": { - "size": 42180, - "gzip": 11786 + "size": 42275, + "gzip": 11813 }, "react-dom-server.node.development.js (NODE_DEV)": { - "size": 101717, - "gzip": 26882 + "size": 101804, + "gzip": 26916 }, "react-dom-server.node.production.min.js (NODE_PROD)": { - "size": 15803, - "gzip": 6168 + "size": 15814, + "gzip": 6172 }, "react-art.development.js (UMD_DEV)": { "size": 366731, @@ -187,6 +187,14 @@ "react-dom-node-stream.production.min.js (NODE_PROD)": { "size": 19585, "gzip": 7520 + }, + "ReactNativeRTFiber-dev.js (RN_DEV)": { + "size": 218534, + "gzip": 37944 + }, + "ReactNativeRTFiber-prod.js (RN_PROD)": { + "size": 167912, + "gzip": 28774 } } } \ No newline at end of file diff --git a/src/__mocks__/RTManager.js b/src/__mocks__/RTManager.js new file mode 100644 index 000000000000..f4bb15fbca20 --- /dev/null +++ b/src/__mocks__/RTManager.js @@ -0,0 +1,26 @@ +/** + * 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. + */ + +'use strict'; + +// Mock of the Native Hooks + +var RCTRTManager = { + createNode: jest.fn(function createView(tag, classType, props) {}), + appendChildToDetachedParent: jest.fn(function appendChildToDetachedParent( + parentTag, + childTag, + ) {}), + beginUpdates: jest.fn(function beginUpdates() {}), + appendChild: jest.fn(function appendChild(parentTag, childTag) {}), + prependChild: jest.fn(function prependChild(childTag, beforeTag) {}), + deleteChild: jest.fn(function deleteChild(childTag) {}), + updateNode: jest.fn(function updateNode(tag, props) {}), + completeUpdates: jest.fn(function completeUpdates() {}), +}; + +module.exports = RCTRTManager; diff --git a/src/renderers/native-rt/ReactNativeRTComponentTree.js b/src/renderers/native-rt/ReactNativeRTComponentTree.js new file mode 100644 index 000000000000..bd212575e904 --- /dev/null +++ b/src/renderers/native-rt/ReactNativeRTComponentTree.js @@ -0,0 +1,47 @@ +/** + * 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. + * + * @providesModule ReactNativeRTComponentTree + * @flow + */ + +'use strict'; + +import type {Fiber} from 'ReactFiber'; + +var instanceCache: {[key: number]: Fiber} = {}; +var instanceProps: {[key: number]: Object} = {}; + +function precacheFiberNode(fiber: Fiber, tag: number): void { + instanceCache[tag] = fiber; +} + +function getFiberFromTag(tag: number): null | Fiber { + return instanceCache[tag] || null; +} + +function uncacheFiberNode(tag: number): void { + delete instanceCache[tag]; + delete instanceProps[tag]; +} + +function getFiberCurrentPropsFromTag(tag: number): null | Object { + return instanceProps[tag] || null; +} + +function updateFiberProps(tag: number, props: Object): void { + instanceProps[tag] = props; +} + +var ReactNativeRTComponentTree = { + precacheFiberNode, + uncacheFiberNode, + getFiberFromTag, + getFiberCurrentPropsFromTag, + updateFiberProps, +}; + +module.exports = ReactNativeRTComponentTree; diff --git a/src/renderers/native-rt/ReactNativeRTEventEmitter.js b/src/renderers/native-rt/ReactNativeRTEventEmitter.js new file mode 100644 index 000000000000..c3a5a53960ca --- /dev/null +++ b/src/renderers/native-rt/ReactNativeRTEventEmitter.js @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2015-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. + * + * @providesModule ReactNativeRTEventEmitter + * @flow + */ +'use strict'; + +var ReactNativeRTComponentTree = require('ReactNativeRTComponentTree'); +var ReactGenericBatching = require('ReactGenericBatching'); + +var ReactNativeRTEventEmitter = { + /** + * Publicly exposed method on module for native objc to invoke when a top + * level event is extracted. + * @param {rootNodeID} rootNodeID React root node ID that event occurred on. + * @param {TopLevelType} topLevelType Top level type of event. + * @param {object} nativeEventParam Object passed from native. + */ + receiveEvent: function( + rootNodeID: number, + topLevelType: string, + nativeEventParam: Object, + ) { + var nativeEvent = nativeEventParam; + var props = ReactNativeRTComponentTree.getFiberCurrentPropsFromTag( + rootNodeID, + ); + if (props == null) { + return; + } + var eventHandler = props[topLevelType]; + if (typeof eventHandler !== 'function') { + return; + } + ReactGenericBatching.batchedUpdates(function() { + eventHandler(nativeEvent); + }); + }, +}; + +module.exports = ReactNativeRTEventEmitter; diff --git a/src/renderers/native-rt/ReactNativeRTFiberEntry.js b/src/renderers/native-rt/ReactNativeRTFiberEntry.js new file mode 100644 index 000000000000..5ef222389311 --- /dev/null +++ b/src/renderers/native-rt/ReactNativeRTFiberEntry.js @@ -0,0 +1,103 @@ +/** + * 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. + * + * @providesModule ReactNativeRTFiberEntry + * @flow + */ + +'use strict'; + +const ReactFiberErrorLogger = require('ReactFiberErrorLogger'); +const ReactGenericBatching = require('ReactGenericBatching'); +const ReactNativeFiberErrorDialog = require('ReactNativeFiberErrorDialog'); // Reused from RN, seems fine? +const ReactPortal = require('ReactPortal'); +const ReactNativeRTComponentTree = require('ReactNativeRTComponentTree'); +const ReactNativeRTFiberRenderer = require('ReactNativeRTFiberRenderer'); +const ReactNativeRTFiberInspector = require('ReactNativeRTFiberInspector'); +const ReactVersion = require('ReactVersion'); + +const {injectInternals} = require('ReactFiberDevToolsHook'); + +import type {ReactNativeRTType} from 'ReactNativeRTTypes'; +import type {ReactNodeList} from 'ReactTypes'; + +/** + * Make sure essential globals are available and are patched correctly. Please don't remove this + * line. Bundles created by react-packager `require` it before executing any application code. This + * ensures it exists in the dependency graph and can be `require`d. + * TODO: require this in packager, not in React #10932517 + */ +require('InitializeCore'); + +var RCTEventEmitter = require('RCTEventEmitter'); +var ReactNativeEventEmitter = require('ReactNativeEventEmitter'); + +/** + * Register the event emitter with the native bridge + */ +RCTEventEmitter.register(ReactNativeEventEmitter); + +ReactGenericBatching.injection.injectFiberBatchedUpdates( + ReactNativeRTFiberRenderer.batchedUpdates, +); + +const roots = new Map(); + +// Intercept lifecycle errors and ensure they are shown with the correct stack +// trace within the native redbox component. +ReactFiberErrorLogger.injection.injectDialog( + ReactNativeFiberErrorDialog.showDialog, +); + +const ReactNativeRTFiber: ReactNativeRTType = { + render(element: React$Element, containerTag: any, callback: ?Function) { + let root = roots.get(containerTag); + + if (!root) { + // TODO (bvaughn): If we decide to keep the wrapper component, + // We could create a wrapper for containerTag as well to reduce special casing. + root = ReactNativeRTFiberRenderer.createContainer(containerTag); + roots.set(containerTag, root); + } + ReactNativeRTFiberRenderer.updateContainer(element, root, null, callback); + + return ReactNativeRTFiberRenderer.getPublicRootInstance(root); + }, + + unmountComponentAtNode(containerTag: number) { + const root = roots.get(containerTag); + if (root) { + // TODO: Is it safe to reset this now or should I wait since this unmount could be deferred? + ReactNativeRTFiberRenderer.updateContainer(null, root, null, () => { + roots.delete(containerTag); + }); + } + }, + + createPortal( + children: ReactNodeList, + containerTag: number, + key: ?string = null, + ) { + return ReactPortal.createPortal(children, containerTag, null, key); + }, + + unstable_batchedUpdates: ReactGenericBatching.batchedUpdates, + + flushSync: ReactNativeRTFiberRenderer.flushSync, +}; + +injectInternals({ + findFiberByHostInstance: ReactNativeRTComponentTree.getFiberFromTag, + findHostInstanceByFiber: ReactNativeRTFiberRenderer.findHostInstance, + getInspectorDataForViewTag: ReactNativeRTFiberInspector.getInspectorDataForViewTag, + // This is an enum because we may add more (e.g. profiler build) + bundleType: __DEV__ ? 1 : 0, + version: ReactVersion, + rendererPackageName: 'react-native-rt', +}); + +module.exports = ReactNativeRTFiber; diff --git a/src/renderers/native-rt/ReactNativeRTFiberInspector.js b/src/renderers/native-rt/ReactNativeRTFiberInspector.js new file mode 100644 index 000000000000..e5f9baeafe64 --- /dev/null +++ b/src/renderers/native-rt/ReactNativeRTFiberInspector.js @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2015-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. + * + * @providesModule ReactNativeRTFiberInspector + * @flow + */ +'use strict'; + +const ReactNativeRTComponentTree = require('ReactNativeRTComponentTree'); +const ReactFiberTreeReflection = require('ReactFiberTreeReflection'); +const getComponentName = require('getComponentName'); +const emptyObject = require('fbjs/lib/emptyObject'); +const ReactTypeOfWork = require('ReactTypeOfWork'); +const invariant = require('fbjs/lib/invariant'); + +const {getFiberFromTag} = ReactNativeRTComponentTree; +const {findCurrentFiberUsingSlowPath} = ReactFiberTreeReflection; +const {HostComponent} = ReactTypeOfWork; + +let getInspectorDataForViewTag; + +if (__DEV__) { + var traverseOwnerTreeUp = function(hierarchy, instance: any) { + if (instance) { + hierarchy.unshift(instance); + traverseOwnerTreeUp(hierarchy, instance._debugOwner); + } + }; + + var getOwnerHierarchy = function(instance: any) { + var hierarchy = []; + traverseOwnerTreeUp(hierarchy, instance); + return hierarchy; + }; + + var lastNonHostInstance = function(hierarchy) { + for (let i = hierarchy.length - 1; i > 1; i--) { + const instance = hierarchy[i]; + + if (instance.tag !== HostComponent) { + return instance; + } + } + return hierarchy[0]; + }; + + var getHostProps = function(fiber) { + const host = ReactFiberTreeReflection.findCurrentHostFiber(fiber); + if (host) { + return host.memoizedProps || emptyObject; + } + return emptyObject; + }; + + var createHierarchy = function(fiberHierarchy) { + return fiberHierarchy.map(fiber => ({ + name: getComponentName(fiber), + getInspectorData: findNodeHandle => ({ + measure: callback => invariant(false, 'Measure not implemented yet'), + props: getHostProps(fiber), + source: fiber._debugSource, + }), + })); + }; + + getInspectorDataForViewTag = function(viewTag: number): Object { + const closestInstance = getFiberFromTag(viewTag); + + // Handle case where user clicks outside of ReactNative + if (!closestInstance) { + return { + hierarchy: [], + props: emptyObject, + selection: null, + source: null, + }; + } + + const fiber = findCurrentFiberUsingSlowPath(closestInstance); + const fiberHierarchy = getOwnerHierarchy(fiber); + const instance = lastNonHostInstance(fiberHierarchy); + const hierarchy = createHierarchy(fiberHierarchy); + const props = getHostProps(instance); + const source = instance._debugSource; + const selection = fiberHierarchy.indexOf(instance); + + return { + hierarchy, + props, + selection, + source, + }; + }; +} else { + getInspectorDataForViewTag = () => { + invariant( + false, + 'getInspectorDataForViewTag() is not available in production', + ); + }; +} + +module.exports = { + getInspectorDataForViewTag, +}; diff --git a/src/renderers/native-rt/ReactNativeRTFiberRenderer.js b/src/renderers/native-rt/ReactNativeRTFiberRenderer.js new file mode 100644 index 000000000000..12d55f141410 --- /dev/null +++ b/src/renderers/native-rt/ReactNativeRTFiberRenderer.js @@ -0,0 +1,186 @@ +/** + * 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. + * + * @providesModule ReactNativeRTFiberRenderer + * @flow + */ + +'use strict'; + +const ReactFiberReconciler = require('ReactFiberReconciler'); +const ReactNativeRTComponentTree = require('ReactNativeRTComponentTree'); +const ReactNativeRTTagHandles = require('ReactNativeRTTagHandles'); +const RTManager = require('RTManager'); + +const emptyObject = require('fbjs/lib/emptyObject'); +const invariant = require('fbjs/lib/invariant'); + +export type Container = number; +export type Instance = number; +export type Props = Object; +export type TextInstance = number; + +const {precacheFiberNode, updateFiberProps} = ReactNativeRTComponentTree; + +const NativeRTRenderer = ReactFiberReconciler({ + appendChild(parentInstance: Instance, child: Instance | TextInstance): void { + RTManager.appendChild(parentInstance, child); + }, + + appendChildToContainer( + parentInstance: Container, + child: Instance | TextInstance, + ): void { + RTManager.appendChild(parentInstance, child); + }, + + appendInitialChild( + parentInstance: Instance, + child: Instance | TextInstance, + ): void { + RTManager.appendChildToDetachedParent(parentInstance, child); + }, + + commitTextUpdate( + textInstance: TextInstance, + oldText: string, + newText: string, + ): void { + invariant(false, 'Text components are not yet supported.'); + }, + + commitMount( + instance: Instance, + type: string, + newProps: Props, + internalInstanceHandle: Object, + ): void { + // Noop + }, + + commitUpdate( + instance: Instance, + updatePayload: Object, + type: string, + oldProps: Props, + newProps: Props, + internalInstanceHandle: Object, + ): void { + updateFiberProps(instance, newProps); + RTManager.updateNode(instance, newProps); + }, + + createInstance( + type: string, + props: Props, + rootContainerInstance: Container, + hostContext: {}, + internalInstanceHandle: Object, + ): Instance { + const tag = ReactNativeRTTagHandles.allocateTag(); + RTManager.createNode(tag, type, props); + precacheFiberNode(internalInstanceHandle, tag); + updateFiberProps(tag, props); + return tag; + }, + + createTextInstance( + text: string, + rootContainerInstance: Container, + hostContext: {}, + internalInstanceHandle: Object, + ): TextInstance { + invariant(false, 'Text components are not supported for now.'); + }, + + finalizeInitialChildren( + parentInstance: Instance, + type: string, + props: Props, + rootContainerInstance: Container, + ): boolean { + return false; + }, + + getRootHostContext(): {} { + return emptyObject; + }, + + getChildHostContext(): {} { + return emptyObject; + }, + + getPublicInstance(instance) { + return instance; + }, + + insertBefore( + parentInstance: Instance, + child: Instance | TextInstance, + beforeChild: Instance | TextInstance, + ): void { + RTManager.prependChild(child, beforeChild); + }, + + insertInContainerBefore( + parentInstance: Container, + child: Instance | TextInstance, + beforeChild: Instance | TextInstance, + ): void { + RTManager.prependChild(child, beforeChild); + }, + + prepareForCommit(): void { + RTManager.beginUpdates(); + }, + + prepareUpdate( + instance: Instance, + type: string, + oldProps: Props, + newProps: Props, + rootContainerInstance: Container, + hostContext: {}, + ): null | Object { + return emptyObject; + }, + + removeChild(parentInstance: Instance, child: Instance | TextInstance): void { + // TODO: recursively uncache, by traversing fibers, this will currently leak + RTManager.deleteChild(child); + }, + + removeChildFromContainer( + parentInstance: Container, + child: Instance | TextInstance, + ): void { + // TODO: recursively uncache, by traversing fibers, this will currently leak + RTManager.deleteChild(child); + }, + + resetAfterCommit(): void { + RTManager.completeUpdates(); + }, + + resetTextContent(instance: Instance): void { + // Noop + }, + + shouldDeprioritizeSubtree(type: string, props: Props): boolean { + return false; + }, + + scheduleDeferredCallback: global.requestIdleCallback, + + shouldSetTextContent(type: string, props: Props): boolean { + // TODO: Figure out when we should allow text content. + return false; + }, + + useSyncScheduling: true, +}); + +module.exports = NativeRTRenderer; diff --git a/src/renderers/native-rt/ReactNativeRTTagHandles.js b/src/renderers/native-rt/ReactNativeRTTagHandles.js new file mode 100644 index 000000000000..6f09dff04e50 --- /dev/null +++ b/src/renderers/native-rt/ReactNativeRTTagHandles.js @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2015-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. + * + * @providesModule ReactNativeRTTagHandles + * @flow + */ +'use strict'; + +var invariant = require('fbjs/lib/invariant'); + +/** + * Keeps track of allocating and associating native "tags" which are numeric, + * unique view IDs. All the native tags are negative numbers, to avoid + * collisions, but in the JS we keep track of them as positive integers to store + * them effectively in Arrays. So we must refer to them as "inverses" of the + * native tags (that are * normally negative). + * + * It *must* be the case that every `rootNodeID` always maps to the exact same + * `tag` forever. The easiest way to accomplish this is to never delete + * anything from this table. + * Why: Because `dangerouslyReplaceNodeWithMarkupByID` relies on being able to + * unmount a component with a `rootNodeID`, then mount a new one in its place, + */ +var INITIAL_TAG_COUNT = 1; +var ReactNativeRTTagHandles = { + tagsStartAt: INITIAL_TAG_COUNT, + tagCount: INITIAL_TAG_COUNT, + + allocateTag: function(): number { + // Skip over root IDs as those are reserved for native + var tag = ReactNativeRTTagHandles.tagCount; + ReactNativeRTTagHandles.tagCount++; + return tag; + }, + + assertRootTag: function(tag: number): void { + invariant( + ReactNativeRTTagHandles.reactTagIsNativeID(tag), + 'Expect a native root tag, instead got %s', + tag, + ); + }, + + reactTagIsNativeID: function(reactTag: number): boolean { + // We reserve all tags that are 1 mod 10 for native view creation + return reactTag % 10 === 1; + }, +}; + +module.exports = ReactNativeRTTagHandles; diff --git a/src/renderers/native-rt/ReactNativeRTTypes.js b/src/renderers/native-rt/ReactNativeRTTypes.js new file mode 100644 index 000000000000..df7cb8e7d389 --- /dev/null +++ b/src/renderers/native-rt/ReactNativeRTTypes.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2015-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. + * + * @providesModule ReactNativeRTTypes + * @flow + */ +'use strict'; + +/** + * Flat RT renderer bundles are too big for Flow to parse efficiently. + * Provide minimal Flow typing for the high-level RN API and call it a day. + */ +export type ReactNativeRTType = { + render( + element: React$Element, + containerTag: any, + callback: ?Function, + ): any, + unmountComponentAtNode(containerTag: number): any, + unstable_batchedUpdates: any, // TODO (bvaughn) Add types +}; diff --git a/src/renderers/native-rt/__tests__/ReactNativeRT-test.js b/src/renderers/native-rt/__tests__/ReactNativeRT-test.js new file mode 100644 index 000000000000..6cf7d713e50a --- /dev/null +++ b/src/renderers/native-rt/__tests__/ReactNativeRT-test.js @@ -0,0 +1,46 @@ +/** + * 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'; + +var React; +var ReactNativeRT; +var RTManager; + +describe('ReactNativeRT', () => { + beforeEach(() => { + jest.resetModules(); + + React = require('react'); + ReactNativeRT = require('ReactNativeRTFiberEntry'); + RTManager = require('RTManager'); + }); + + it('should be able to create and render a native component', () => { + ReactNativeRT.render(, 1); + expect(RTManager.createNode).toBeCalled(); + expect(RTManager.appendChild).toBeCalled(); + expect(RTManager.updateNode).not.toBeCalled(); + }); + + it('should be able to create and update a native component', () => { + ReactNativeRT.render(, 11); + + expect(RTManager.createNode.mock.calls.length).toBe(1); + expect(RTManager.createNode).toBeCalledWith(1, 'rt-box', {foo: 'foo'}); + expect(RTManager.appendChild.mock.calls.length).toBe(1); + expect(RTManager.updateNode).not.toBeCalled(); + + ReactNativeRT.render(, 11); + + expect(RTManager.createNode.mock.calls.length).toBe(1); + expect(RTManager.appendChild.mock.calls.length).toBe(1); + expect(RTManager.updateNode).toBeCalledWith(1, {foo: 'bar'}); + }); +});