mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-06 09:31:11 +08:00
Android Instrumentations tests are ready to be run in github/CI open source environment
Reviewed By: mkonicek Differential Revision: D2769217 fb-gh-sync-id: 7469af816241d8b642753cca21f6542b971e9572
This commit is contained in:
committed by
facebook-github-bot-9
parent
040909904c
commit
a99c5160ee
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* Copyright (c) 2014-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.
|
||||
*/
|
||||
|
||||
package com.facebook.react.testing;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Base class for instrumentation tests that runs React based react application in UI mode
|
||||
*/
|
||||
public abstract class ReactAppInstrumentationTestCase extends
|
||||
ActivityInstrumentationTestCase2<ReactAppTestActivity> implements IdleWaiter {
|
||||
|
||||
public ReactAppInstrumentationTestCase() {
|
||||
super(ReactAppTestActivity.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
final ReactAppTestActivity activity = getActivity();
|
||||
try {
|
||||
runTestOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
activity.loadApp(
|
||||
getReactApplicationKeyUnderTest(),
|
||||
createReactInstanceSpecForTest(),
|
||||
getEnableDevSupport());
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
throw new Exception("Unable to load react app", t);
|
||||
}
|
||||
waitForBridgeAndUIIdle();
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method isn't safe since it doesn't factor in layout-only view removal. Use
|
||||
* {@link #getViewByTestId(String)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public <T extends View> T getViewAtPath(int... path) {
|
||||
return ReactTestHelper.getViewAtPath((ViewGroup) getRootView().getParent(), path);
|
||||
}
|
||||
|
||||
public <T extends View> 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 Bitmap getScreenshot() {
|
||||
// Wait for the UI to settle. If the UI is doing animations, this may be unsafe!
|
||||
getInstrumentation().waitForIdleSync();
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final BitmapHolder bitmapHolder = new BitmapHolder();
|
||||
final Runnable getScreenshotRunnable = new Runnable() {
|
||||
|
||||
private static final int MAX_TRIES = 1000;
|
||||
// This is the constant used in the support library for APIs that don't have Choreographer
|
||||
private static final int FRAME_DELAY_MS = 10;
|
||||
|
||||
private int mNumRuns = 0;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mNumRuns++;
|
||||
ReactAppTestActivity activity = getActivity();
|
||||
if (!activity.isScreenshotReady()) {
|
||||
if (mNumRuns > MAX_TRIES) {
|
||||
throw new RuntimeException(
|
||||
"Waited " + MAX_TRIES + " frames to get screenshot but it's still not ready!");
|
||||
}
|
||||
activity.postDelayed(this, FRAME_DELAY_MS);
|
||||
return;
|
||||
}
|
||||
|
||||
bitmapHolder.bitmap = getActivity().getCurrentScreenshot();
|
||||
latch.countDown();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
getActivity().runOnUiThread(getScreenshotRunnable);
|
||||
try {
|
||||
if (!latch.await(5000, TimeUnit.MILLISECONDS)) {
|
||||
throw new RuntimeException("Timed out waiting for screenshot runnable to run!");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return Assertions.assertNotNull(bitmapHolder.bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this method to provide application key to be launched. List of available
|
||||
* application is located in TestBundle.js file
|
||||
*/
|
||||
protected abstract String getReactApplicationKeyUnderTest();
|
||||
|
||||
protected boolean getEnableDevSupport() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to provide extra native modules to be loaded before the app starts
|
||||
*/
|
||||
protected ReactInstanceSpecForTest createReactInstanceSpecForTest() {
|
||||
return new ReactInstanceSpecForTest();
|
||||
}
|
||||
|
||||
protected ReactContext getReactContext() {
|
||||
return getActivity().getReactContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to pass the bitmap between execution scopes in {@link #getScreenshot()}.
|
||||
*/
|
||||
private static class BitmapHolder {
|
||||
|
||||
public @Nullable volatile Bitmap bitmap;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user