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 0a68ad1ed..341c87814 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java @@ -176,8 +176,36 @@ public class ReactAppTestActivity extends FragmentActivity String bundleName, boolean useDevSupport, UIImplementationProvider uiImplementationProvider) { + loadBundle(spec, bundleName, useDevSupport, uiImplementationProvider); + renderComponent(appKey, initialProps); + } + public void renderComponent(String appKey, @Nullable Bundle initialProps) { final CountDownLatch currentLayoutEvent = mLayoutEvent = new CountDownLatch(1); + Assertions.assertNotNull(mReactRootView).getViewTreeObserver().addOnGlobalLayoutListener( + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + currentLayoutEvent.countDown(); + } + }); + Assertions.assertNotNull(mReactRootView) + .startReactApplication(mReactInstanceManager, appKey, initialProps); + } + + public void loadBundle( + ReactInstanceSpecForTest spec, + String bundleName, + boolean useDevSupport) { + loadBundle(spec, bundleName, useDevSupport, null); + } + + public void loadBundle( + ReactInstanceSpecForTest spec, + String bundleName, + boolean useDevSupport, + UIImplementationProvider uiImplementationProvider) { + mBridgeIdleSignaler = new ReactBridgeIdleSignaler(); ReactInstanceManagerBuilder builder = @@ -200,49 +228,39 @@ public class ReactAppTestActivity extends FragmentActivity .setBridgeIdleDebugListener(mBridgeIdleSignaler) .setInitialLifecycleState(mLifecycleState) .setJSIModulesProvider( - new JSIModulesProvider() { - @Override - public List getJSIModules( - final ReactApplicationContext reactApplicationContext, - final JavaScriptContextHolder jsContext) { + new JSIModulesProvider() { + @Override + public List getJSIModules( + final ReactApplicationContext reactApplicationContext, + final JavaScriptContextHolder jsContext) { - List modules = new ArrayList<>(); - modules.add( - new JSIModuleHolder() { + List modules = new ArrayList<>(); + modules.add( + new JSIModuleHolder() { - @Override - public Class getJSIModuleClass() { - return UIManager.class; - } + @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; - } - }); + @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; - }}) + return modules; + }}) .setUIImplementationProvider(uiImplementationProvider); mReactInstanceManager = builder.build(); mReactInstanceManager.onHostResume(this, this); - - Assertions.assertNotNull(mReactRootView).getViewTreeObserver().addOnGlobalLayoutListener( - new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - currentLayoutEvent.countDown(); - } - }); - Assertions.assertNotNull(mReactRootView) - .startReactApplication(mReactInstanceManager, appKey, initialProps); } private ReactInstanceManager getReactInstanceManager() { diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactInstrumentationTest.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactInstrumentationTest.java new file mode 100644 index 000000000..fe6c7c687 --- /dev/null +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactInstrumentationTest.java @@ -0,0 +1,124 @@ +/** + * Copyright (c) 2014-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. + */ + +package com.facebook.react.testing; + +import android.content.Intent; +import android.test.ActivityInstrumentationTestCase2; +import android.view.View; +import android.view.ViewGroup; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.testing.idledetection.IdleWaiter; + +/** + * Base class for instrumentation tests that runs React based application. + * + * This is similar to ReactAppInstrumentationTestCase except ReactInstrumentationTest allows + * optional rendering of components. A test case can render no components or render multiple + * components. + */ +public abstract class ReactInstrumentationTest extends + ActivityInstrumentationTestCase2 implements IdleWaiter { + + public ReactInstrumentationTest() { + super(ReactAppTestActivity.class); + } + + @Override + 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() { + @Override + public void run() { + activity.loadBundle( + createReactInstanceSpecForTest(), + getBundleName(), + getEnableDevSupport()); + } + }); + } catch (Throwable t) { + throw new Exception("Unable to load react bundle " + getBundleName(), t); + } + } + + /** + * Renders this component within this test's activity + */ + public void renderComponent(final String componentName) throws Exception { + final ReactAppTestActivity activity = getActivity(); + try { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + activity.renderComponent(componentName, null); + } + }); + } catch (Throwable t) { + throw new Exception("Unable to render component " + componentName, t); + } + assertTrue("Layout never occurred!", activity.waitForLayout(5000)); + waitForBridgeAndUIIdle(); + } + + @Override + protected void tearDown() throws Exception { + ReactAppTestActivity activity = getActivity(); + super.tearDown(); + activity.waitForDestroy(5000); + } + + public ViewGroup getRootView() { + return (ViewGroup) getActivity().getRootView(); + } + + public T getViewByTestId(String testID) { + return (T) ReactTestHelper + .getViewWithReactTestId((ViewGroup) getRootView().getParent(), testID); + } + + public SingleTouchGestureGenerator createGestureGenerator() { + return new SingleTouchGestureGenerator(getRootView(), this); + } + + public void waitForBridgeAndUIIdle() { + getActivity().waitForBridgeAndUIIdle(); + } + + public void waitForBridgeAndUIIdle(long timeoutMs) { + getActivity().waitForBridgeAndUIIdle(timeoutMs); + } + + protected boolean getEnableDevSupport() { + return false; + } + + protected boolean isFabricTest() { + return false; + } + + /** + * Override this method to provide extra native modules to be loaded before the app starts + */ + protected ReactInstanceSpecForTest createReactInstanceSpecForTest() { + return new ReactInstanceSpecForTest(); + } + + /** + * Implement this method to provide the bundle for this test + */ + protected abstract String getBundleName(); + + protected ReactContext getReactContext() { + return getActivity().getReactContext(); + } +}