diff --git a/Libraries/ReactNative/AppRegistry.js b/Libraries/ReactNative/AppRegistry.js index 87bd07a6dd..21fdca68a4 100644 --- a/Libraries/ReactNative/AppRegistry.js +++ b/Libraries/ReactNative/AppRegistry.js @@ -20,6 +20,10 @@ const infoLog = require('infoLog'); const invariant = require('fbjs/lib/invariant'); const renderApplication = require('renderApplication'); +// Renderer provider must be supplied by each app. If none, traditional +// renderApplication() will be used. +let fabricRendererProvider: ?() => typeof renderApplication = null; + type Task = (taskData: any) => Promise; type TaskProvider = () => Task; export type ComponentProvider = () => React$ComponentType; @@ -31,6 +35,7 @@ export type AppConfig = { component?: ComponentProvider, run?: Function, section?: boolean, + fabric?: boolean, }; export type Runnable = { component?: ComponentProvider, @@ -80,6 +85,7 @@ const AppRegistry = { appConfig.appKey, appConfig.component, appConfig.section, + appConfig.fabric, ); } }); @@ -94,16 +100,26 @@ const AppRegistry = { appKey: string, componentProvider: ComponentProvider, section?: boolean, + fabric?: boolean, ): string { runnables[appKey] = { componentProvider, - run: appParameters => - renderApplication( + run: appParameters => { + let renderFunc = renderApplication; + if (fabric) { + invariant( + fabricRendererProvider != null, + 'A Fabric renderer provider must be set to render Fabric components', + ); + renderFunc = fabricRendererProvider(); + } + renderFunc( componentProviderInstrumentationHook(componentProvider), appParameters.initialProps, appParameters.rootTag, wrapperComponentProvider && wrapperComponentProvider(appParameters), - ), + ); + }, }; if (section) { sections[appKey] = runnables[appKey]; @@ -236,6 +252,10 @@ const AppRegistry = { NativeModules.HeadlessJsTaskSupport.notifyTaskFinished(taskId); }); }, + + setFabricRendererProvider(provider: () => typeof renderApplication): void { + fabricRendererProvider = provider; + }, }; BatchedBridge.registerCallableModule('AppRegistry', AppRegistry); diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK index 9654ee5408..7d96b9b3bb 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/BUCK @@ -27,6 +27,8 @@ rn_android_library( react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/devsupport:interfaces"), + react_native_target("java/com/facebook/react/fabric:fabric"), + react_native_target("java/com/facebook/react/fabric/jsc:jsc"), react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("java/com/facebook/react/module/model:model"), react_native_target("java/com/facebook/react/modules/core:core"), diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java index 8b93eeb8b1..863e249cbf 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppInstrumentationTestCase.java @@ -7,19 +7,17 @@ package com.facebook.react.testing; -import javax.annotation.Nullable; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - +import android.content.Intent; import android.graphics.Bitmap; import android.test.ActivityInstrumentationTestCase2; import android.view.View; import android.view.ViewGroup; - import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.ReactContext; import com.facebook.react.testing.idledetection.IdleWaiter; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import javax.annotation.Nullable; /** * Base class for instrumentation tests that runs React based react application in UI mode @@ -35,6 +33,9 @@ public abstract class ReactAppInstrumentationTestCase extends protected void setUp() throws Exception { super.setUp(); + Intent intent = new Intent(); + intent.putExtra(ReactAppTestActivity.EXTRA_IS_FABRIC_TEST, isFabricTest()); + setActivityIntent(intent); final ReactAppTestActivity activity = getActivity(); try { runTestOnUiThread(new Runnable() { @@ -144,6 +145,10 @@ public abstract class ReactAppInstrumentationTestCase extends return false; } + protected boolean isFabricTest() { + return false; + } + /** * Override this method to provide extra native modules to be loaded before the app starts */ diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java index 96c1803063..0a68ad1ed5 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java @@ -7,6 +7,7 @@ package com.facebook.react.testing; +import android.content.Intent; import android.graphics.Bitmap; import android.os.Bundle; import android.support.v4.app.FragmentActivity; @@ -17,8 +18,16 @@ import com.facebook.infer.annotation.Assertions; import com.facebook.react.ReactInstanceManager; import com.facebook.react.ReactInstanceManagerBuilder; import com.facebook.react.ReactRootView; +import com.facebook.react.bridge.JSIModule; +import com.facebook.react.bridge.JSIModuleHolder; +import com.facebook.react.bridge.JSIModulesProvider; +import com.facebook.react.bridge.JavaScriptContextHolder; +import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.UIManager; import com.facebook.react.common.LifecycleState; +import com.facebook.react.fabric.FabricUIManager; +import com.facebook.react.fabric.jsc.FabricJSCBinding; import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; import com.facebook.react.modules.core.PermissionAwareActivity; import com.facebook.react.modules.core.PermissionListener; @@ -26,6 +35,10 @@ import com.facebook.react.shell.MainReactPackage; import com.facebook.react.testing.idledetection.ReactBridgeIdleSignaler; import com.facebook.react.testing.idledetection.ReactIdleDetectionUtil; import com.facebook.react.uimanager.UIImplementationProvider; +import com.facebook.react.uimanager.ViewManager; +import com.facebook.react.uimanager.ViewManagerRegistry; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; @@ -33,6 +46,8 @@ import javax.annotation.Nullable; public class ReactAppTestActivity extends FragmentActivity implements DefaultHardwareBackBtnHandler, PermissionAwareActivity { + public static final String EXTRA_IS_FABRIC_TEST = "is_fabric_test"; + private static final String DEFAULT_BUNDLE_NAME = "AndroidTestBundle.js"; private static final int ROOT_VIEW_ID = 8675309; // we need a bigger timeout for CI builds because they run on a slow emulator @@ -62,6 +77,11 @@ public class ReactAppTestActivity extends FragmentActivity rootView.addView(mScreenshotingFrameLayout); mReactRootView = new ReactRootView(this); + Intent intent = getIntent(); + if (intent != null && intent.getBooleanExtra(EXTRA_IS_FABRIC_TEST, false)) { + mReactRootView.setIsFabric(true); + } + mScreenshotingFrameLayout.addView(mReactRootView); } @@ -179,6 +199,36 @@ public class ReactAppTestActivity extends FragmentActivity .setUseDeveloperSupport(useDevSupport) .setBridgeIdleDebugListener(mBridgeIdleSignaler) .setInitialLifecycleState(mLifecycleState) + .setJSIModulesProvider( + new JSIModulesProvider() { + @Override + public List getJSIModules( + final ReactApplicationContext reactApplicationContext, + final JavaScriptContextHolder jsContext) { + + List modules = new ArrayList<>(); + modules.add( + new JSIModuleHolder() { + + @Override + public Class getJSIModuleClass() { + return UIManager.class; + } + + @Override + public FabricUIManager getJSIModule() { + List viewManagers = + getReactInstanceManager().getOrCreateViewManagers(reactApplicationContext); + FabricUIManager fabricUIManager = + new FabricUIManager( + reactApplicationContext, new ViewManagerRegistry(viewManagers)); + new FabricJSCBinding().installFabric(jsContext, fabricUIManager); + return fabricUIManager; + } + }); + + return modules; + }}) .setUIImplementationProvider(uiImplementationProvider); mReactInstanceManager = builder.build(); @@ -195,6 +245,10 @@ public class ReactAppTestActivity extends FragmentActivity .startReactApplication(mReactInstanceManager, appKey, initialProps); } + private ReactInstanceManager getReactInstanceManager() { + return mReactInstanceManager; + } + public boolean waitForLayout(long millis) throws InterruptedException { return mLayoutEvent.await(millis, TimeUnit.MILLISECONDS); }