1 package com.testrunner;
2 
3 import static androidx.test.espresso.Espresso.onView;
4 import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
5 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
6 
7 import android.app.Activity;
8 import android.content.Context;
9 import android.content.ContextWrapper;
10 import android.view.View;
11 import android.view.ViewGroup;
12 
13 import androidx.test.ext.junit.runners.AndroidJUnit4;
14 import androidx.test.filters.LargeTest;
15 import androidx.test.rule.ActivityTestRule;
16 
17 import com.facebook.react.ReactApplication;
18 import com.facebook.react.ReactNativeHost;
19 import com.wix.detox.Detox;
20 import com.wix.detox.config.DetoxConfig;
21 
22 import org.junit.Rule;
23 import org.junit.Test;
24 import org.junit.runner.RunWith;
25 
26 import expo.modules.devlauncher.DevLauncherController;
27 import expo.modules.devlauncher.launcher.DevLauncherActivity;
28 import expo.modules.devmenu.DevMenuManager;
29 
30 // We need this class to pass dev launcher host to detox.
31 // Otherwise it won't detect that the app has been started.
32 class ReactNativeHolder extends ContextWrapper implements ReactApplication {
33   public ReactNativeHolder(Context base) {
34     super(base);
35   }
36 
37   @Override
38   public ReactNativeHost getReactNativeHost() {
39     return DevLauncherController.getInstance().getDevClientHost();
40   }
41 }
42 
43 class DevClientDetoxHelper {
44   public static Activity getCurrentActivity() {
45     final Activity[] activity = new Activity[1];
46 
47     onView(isRoot()).check((view, noViewFoundException) -> {
48 
49       View checkedView = view;
50 
51       while (checkedView instanceof ViewGroup && ((ViewGroup) checkedView).getChildCount() > 0) {
52 
53         checkedView = ((ViewGroup) checkedView).getChildAt(0);
54 
55         if (checkedView.getContext() instanceof Activity) {
56           activity[0] = (Activity) checkedView.getContext();
57           return;
58         }
59       }
60     });
61     return activity[0];
62   }
63 
64   public static void openMenu() throws InterruptedException {
65     getInstrumentation().waitForIdleSync();
66     Activity activity = getCurrentActivity();
67     int counter = 10;
68     while (counter-- > 0 && activity == null) {
69       Thread.sleep(100);
70       activity = getCurrentActivity();
71     }
72 
73     DevMenuManager.INSTANCE.openMenu(activity, null);
74   }
75 }
76 
77 @RunWith(AndroidJUnit4.class)
78 @LargeTest
79 public class DetoxTest {
80   @Rule
81   public ActivityTestRule<DevLauncherActivity> mActivityRule = new ActivityTestRule<>(DevLauncherActivity.class, false, false);
82 
83   @Test
84   public void runDetoxTests() {
85     DetoxConfig detoxConfig = new DetoxConfig();
86     detoxConfig.idlePolicyConfig.masterTimeoutSec = 90;
87     detoxConfig.rnContextLoadTimeoutSec = 180;
88 
89     ReactNativeHolder reactNativeHolder = new ReactNativeHolder(getInstrumentation().getTargetContext().getApplicationContext());
90     Detox.runTests(mActivityRule, reactNativeHolder, detoxConfig);
91   }
92 }
93