Browse Source

fixing android e2e

Daniel Zlotin 7 years ago
parent
commit
8011daebbf

android/app/src/main/java/com/reactnativenavigation/controllers/NavigationActivity.java → android/app/src/main/java/com/reactnativenavigation/NavigationActivity.java View File

@@ -1,4 +1,4 @@
1
-package com.reactnativenavigation.controllers;
1
+package com.reactnativenavigation;
2 2
 
3 3
 import android.os.Bundle;
4 4
 import android.support.annotation.Nullable;
@@ -69,7 +69,9 @@ public class NavigationActivity extends AppCompatActivity implements DefaultHard
69 69
         if (ReactDevPermission.shouldAskPermission()) {
70 70
             ReactDevPermission.askPermission(this);
71 71
         } else {
72
-            host.getReactInstanceManager().createReactContextInBackground();
72
+            if (!host.getReactInstanceManager().hasStartedCreatingInitialContext()) {
73
+                host.getReactInstanceManager().createReactContextInBackground();
74
+            }
73 75
             host.getReactInstanceManager().onHostResume(this, this);
74 76
         }
75 77
     }
@@ -88,6 +90,8 @@ public class NavigationActivity extends AppCompatActivity implements DefaultHard
88 90
         if (host.getReactInstanceManager().hasStartedCreatingInitialContext()) {
89 91
             host.getReactInstanceManager().onHostDestroy(this);
90 92
         }
93
+        host.clear();
94
+        android.util.Log.d("DebuggingIsHell", "NavigationActivity:onDestroy() ");
91 95
     }
92 96
 
93 97
     @Override

android/app/src/main/java/com/reactnativenavigation/controllers/NavigationApplication.java → android/app/src/main/java/com/reactnativenavigation/NavigationApplication.java View File

@@ -1,4 +1,4 @@
1
-package com.reactnativenavigation.controllers;
1
+package com.reactnativenavigation;
2 2
 
3 3
 import android.app.Application;
4 4
 import android.os.Handler;
@@ -19,5 +19,9 @@ public abstract class NavigationApplication extends Application {
19 19
         handler.post(runnable);
20 20
     }
21 21
 
22
+    public void runOnUiThreadDelayed(Runnable runnable, long delayMillis) {
23
+        handler.postDelayed(runnable, delayMillis);
24
+    }
25
+
22 26
     public abstract boolean isDebug();
23 27
 }

+ 3
- 2
android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java View File

@@ -10,8 +10,8 @@ import com.facebook.react.bridge.ReactMethod;
10 10
 import com.facebook.react.bridge.ReadableArray;
11 11
 import com.facebook.react.bridge.ReadableMap;
12 12
 import com.facebook.react.bridge.ReadableMapKeySetIterator;
13
-import com.reactnativenavigation.controllers.NavigationActivity;
14
-import com.reactnativenavigation.controllers.NavigationApplication;
13
+import com.reactnativenavigation.NavigationActivity;
14
+import com.reactnativenavigation.NavigationApplication;
15 15
 import com.reactnativenavigation.layout.LayoutFactory;
16 16
 import com.reactnativenavigation.layout.LayoutNode;
17 17
 import com.reactnativenavigation.layout.StackLayout;
@@ -48,6 +48,7 @@ public class NavigationModule extends ReactContextBaseJavaModule {
48 48
                                     @Override
49 49
                                     public View create(String id, String name) {
50 50
                                         ReactRootView rootView = new ReactRootView(getActivity());
51
+                                        rootView.setId(1234567);
51 52
                                         Bundle opts = new Bundle();
52 53
                                         opts.putString("id", id);
53 54
                                         rootView.startReactApplication(getActivity().getHost().getReactInstanceManager(), name, opts);

+ 1
- 1
android/app/src/main/java/com/reactnativenavigation/react/NavigationPackage.java View File

@@ -5,7 +5,7 @@ import com.facebook.react.bridge.JavaScriptModule;
5 5
 import com.facebook.react.bridge.NativeModule;
6 6
 import com.facebook.react.bridge.ReactApplicationContext;
7 7
 import com.facebook.react.uimanager.ViewManager;
8
-import com.reactnativenavigation.controllers.NavigationActivity;
8
+import com.reactnativenavigation.NavigationActivity;
9 9
 
10 10
 import java.util.Arrays;
11 11
 import java.util.Collections;

+ 1
- 1
android/app/src/main/java/com/reactnativenavigation/react/ReactDevPermission.java View File

@@ -9,7 +9,7 @@ import android.util.Log;
9 9
 import android.widget.Toast;
10 10
 
11 11
 import com.facebook.react.common.ReactConstants;
12
-import com.reactnativenavigation.controllers.NavigationApplication;
12
+import com.reactnativenavigation.NavigationApplication;
13 13
 
14 14
 public class ReactDevPermission {
15 15
 

+ 46
- 0
android/app/src/main/java/com/reactnativenavigation/utils/ReflectionUtils.java View File

@@ -0,0 +1,46 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+import android.support.annotation.Nullable;
4
+
5
+import java.lang.reflect.Field;
6
+
7
+public class ReflectionUtils {
8
+
9
+    public static void setField(Object obj, String name, Object value) {
10
+        try {
11
+            Field field = getField(obj.getClass(), name);
12
+            if (field == null) {
13
+                return;
14
+            }
15
+            field.setAccessible(true);
16
+            field.set(obj, value);
17
+        } catch (Exception e) {
18
+            e.printStackTrace();
19
+        }
20
+    }
21
+
22
+    @Nullable
23
+    public static Object getDeclaredField(Object obj, String fieldName) {
24
+        try {
25
+            Field f = getField(obj.getClass(), fieldName);
26
+            if (f == null) {
27
+                return null;
28
+            }
29
+            f.setAccessible(true);
30
+            return f.get(obj);
31
+        } catch (Exception e) {
32
+            e.printStackTrace();
33
+        }
34
+        return null;
35
+    }
36
+
37
+    private static Field getField(Class clazz, String name) {
38
+        try {
39
+            return clazz.getDeclaredField(name);
40
+        } catch (NoSuchFieldException nsfe) {
41
+            return getField(clazz.getSuperclass(), name);
42
+        } catch (Exception e) {
43
+            return null;
44
+        }
45
+    }
46
+}

+ 0
- 1
android/app/src/main/java/com/reactnativenavigation/utils/ViewIdGenerator.java View File

@@ -1,7 +1,6 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3 3
 import android.os.Build;
4
-import android.support.v4.view.ViewCompat;
5 4
 import android.view.View;
6 5
 
7 6
 import java.util.concurrent.atomic.AtomicInteger;

+ 66
- 45
playground/android/app/src/androidTest/java/com/reactnativenavigation/playground/ApplicationLifecycleTest.java View File

@@ -1,21 +1,21 @@
1 1
 package com.reactnativenavigation.playground;
2 2
 
3
+import android.annotation.TargetApi;
3 4
 import android.provider.Settings;
4 5
 import android.support.test.espresso.Espresso;
5
-import android.support.test.espresso.IdlingResource;
6 6
 import android.support.test.filters.SdkSuppress;
7 7
 import android.support.test.rule.ActivityTestRule;
8 8
 import android.support.test.runner.AndroidJUnit4;
9 9
 import android.support.test.uiautomator.UiDevice;
10 10
 import android.support.test.uiautomator.UiSelector;
11 11
 
12
-import com.facebook.react.ReactNativeHost;
13
-import com.reactnativenavigation.controllers.NavigationActivity;
14 12
 import com.reactnativenavigation.views.NavigationSplashView;
15 13
 
14
+import org.junit.FixMethodOrder;
16 15
 import org.junit.Rule;
17 16
 import org.junit.Test;
18 17
 import org.junit.runner.RunWith;
18
+import org.junit.runners.MethodSorters;
19 19
 
20 20
 import static android.support.test.InstrumentationRegistry.getInstrumentation;
21 21
 import static android.support.test.espresso.Espresso.onView;
@@ -26,77 +26,98 @@ import static org.assertj.core.api.Java6Assertions.assertThat;
26 26
 
27 27
 @RunWith(AndroidJUnit4.class)
28 28
 @SdkSuppress(minSdkVersion = 23)
29
+@TargetApi(23)
30
+@FixMethodOrder(value = MethodSorters.NAME_ASCENDING)
29 31
 public class ApplicationLifecycleTest {
30 32
 
33
+    private ReactIdlingResource reactIdlingResource;
34
+
31 35
     @Rule
32 36
     public ActivityTestRule<MainActivity> rule = new ActivityTestRule<MainActivity>(MainActivity.class, false, false) {
33 37
         @Override
34 38
         protected void afterActivityLaunched() {
35 39
             super.afterActivityLaunched();
36
-            registerReactIdlingResource(rule.getActivity());
40
+            reactIdlingResource = new ReactIdlingResource(getActivity());
41
+            Espresso.registerIdlingResources(reactIdlingResource);
37 42
         }
38
-    };
39 43
 
40
-    private void registerReactIdlingResource(final NavigationActivity activity) {
41
-        Espresso.registerIdlingResources(new IdlingResource() {
42
-            @Override
43
-            public String getName() {
44
-                return "React Bridge";
45
-            }
46
-
47
-            @Override
48
-            public boolean isIdleNow() {
49
-                ReactNativeHost host = activity.getHost();
50
-                return host != null
51
-                        && host.hasInstance()
52
-                        && host.getReactInstanceManager().hasStartedCreatingInitialContext()
53
-                        && host.getReactInstanceManager().getCurrentReactContext() != null;
54
-            }
55
-
56
-            @Override
57
-            public void registerIdleTransitionCallback(final ResourceCallback callback) {
58
-                new Thread(new Runnable() {
59
-                    @Override
60
-                    public void run() {
61
-                        while (!isIdleNow()) {
62
-                            try {
63
-                                Thread.sleep(1000);
64
-                            } catch (InterruptedException e) {
65
-                                e.printStackTrace();
66
-                            }
67
-                        }
68
-                        callback.onTransitionToIdle();
69
-                    }
70
-                }).start();
71
-            }
72
-        });
73
-    }
44
+        @Override
45
+        protected void afterActivityFinished() {
46
+            super.afterActivityFinished();
47
+            Espresso.unregisterIdlingResources(reactIdlingResource);
48
+        }
49
+    };
74 50
 
75 51
     private void launchActivity() {
76 52
         rule.launchActivity(null);
77 53
     }
78 54
 
55
+    private UiDevice uiDevice() {
56
+        return UiDevice.getInstance(getInstrumentation());
57
+    }
58
+
79 59
     private void acceptOverlayPermissionIfNeeded() throws Exception {
80 60
         if (Settings.canDrawOverlays(getInstrumentation().getContext())) {
81 61
             return;
82 62
         }
83
-        UiDevice.getInstance(getInstrumentation()).findObject(new UiSelector().text("Playground")).click();
84
-        UiDevice.getInstance(getInstrumentation()).findObject(new UiSelector().text("Permit drawing over other apps")).click();
85
-        UiDevice.getInstance(getInstrumentation()).pressBack();
86
-        UiDevice.getInstance(getInstrumentation()).pressBack();
63
+        uiDevice().findObject(new UiSelector().text("Playground")).click();
64
+        uiDevice().findObject(new UiSelector().text("Permit drawing over other apps")).click();
65
+        uiDevice().pressBack();
66
+        uiDevice().pressBack();
87 67
     }
88 68
 
89 69
     @Test
90
-    public void acceptsOverlayPermissions_ShowsWelcomeScreen() throws Exception {
70
+    public void _1_acceptsOverlayPermissions_ShowsWelcomeScreen() throws Exception {
91 71
         launchActivity();
92 72
         acceptOverlayPermissionIfNeeded();
93 73
         onView(withText("React Native Navigation!")).check(matches(isDisplayed()));
94 74
     }
95 75
 
96 76
     @Test
97
-    public void showsSplashOnStartup() throws Exception {
77
+    public void _2_showsSplashOnStartup() throws Exception {
98 78
         launchActivity();
99 79
         assertThat(rule.getActivity().getContentView()).isNotNull().isInstanceOf(NavigationSplashView.class);
100 80
         acceptOverlayPermissionIfNeeded();
101 81
     }
82
+
83
+    @Test
84
+    public void _3_relaunchFromBackground() throws Exception {
85
+        launchActivity();
86
+        acceptOverlayPermissionIfNeeded();
87
+        onView(withText("React Native Navigation!")).check(matches(isDisplayed()));
88
+
89
+        uiDevice().pressHome();
90
+        uiDevice().pressRecentApps();
91
+        uiDevice().findObject(new UiSelector().text("Playground")).click();
92
+
93
+        onView(withText("React Native Navigation!")).check(matches(isDisplayed()));
94
+    }
102 95
 }
96
+//    xdescribe('android application lifecycle', () => {
97
+////launch, pause, and resume
98
+//
99
+//        it('launch already running in background', () => {
100
+//        //
101
+//        });
102
+//
103
+//        it('launch after activity killed by system', () => {
104
+//        //
105
+//        });
106
+//
107
+//        it('launch and ask react overlay permissions', () => {
108
+//        //
109
+//        });
110
+//
111
+//        it('launch after reactContext killed by system', () => {
112
+//        //
113
+//        });
114
+//
115
+//        it('launch from push notification', () => {
116
+//        //
117
+//        });
118
+//
119
+//        it('launch from intent filter', () => {
120
+//        //
121
+//        });
122
+//        });
123
+

+ 75
- 0
playground/android/app/src/androidTest/java/com/reactnativenavigation/playground/ReactIdlingResource.java View File

@@ -0,0 +1,75 @@
1
+package com.reactnativenavigation.playground;
2
+
3
+import android.support.test.espresso.IdlingResource;
4
+
5
+import com.facebook.react.ReactNativeHost;
6
+import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener;
7
+import com.reactnativenavigation.NavigationActivity;
8
+import com.reactnativenavigation.NavigationApplication;
9
+
10
+import java.util.concurrent.atomic.AtomicBoolean;
11
+
12
+class ReactIdlingResource implements IdlingResource, NotThreadSafeBridgeIdleDebugListener {
13
+    private final NavigationActivity activity;
14
+    private ResourceCallback callback;
15
+
16
+    ReactIdlingResource(NavigationActivity activity) {
17
+        android.util.Log.d("DebuggingIsHell", "------------------------------");
18
+        this.activity = activity;
19
+        NavigationApplication.instance.runOnUiThreadDelayed(new Runnable() {
20
+            @Override
21
+            public void run() {
22
+                if (!isIdleNow()) {
23
+                    NavigationApplication.instance.runOnUiThreadDelayed(this, 10);
24
+                }
25
+            }
26
+        }, 10);
27
+    }
28
+
29
+    private AtomicBoolean registered = new AtomicBoolean(false);
30
+    private AtomicBoolean bridgeIdle = new AtomicBoolean(false);
31
+
32
+    @Override
33
+    public String getName() {
34
+        return "ReactIdlingResource";
35
+    }
36
+
37
+    @Override
38
+    public boolean isIdleNow() {
39
+        ReactNativeHost host = activity.getHost();
40
+        boolean hasContext = host != null
41
+                && host.getReactInstanceManager() != null
42
+                && host.getReactInstanceManager().getCurrentReactContext() != null;
43
+        if (!hasContext) {
44
+            return false;
45
+        }
46
+
47
+        if (registered.compareAndSet(false, true)) {
48
+            host.getReactInstanceManager().getCurrentReactContext().getCatalystInstance().addBridgeIdleDebugListener(this);
49
+        }
50
+
51
+        boolean idle = bridgeIdle.get();
52
+        android.util.Log.d("DebuggingIsHell", "idle " + idle);
53
+        if (idle) {
54
+            callback.onTransitionToIdle();
55
+        }
56
+        return idle;
57
+    }
58
+
59
+    @Override
60
+    public void registerIdleTransitionCallback(final ResourceCallback callback) {
61
+        this.callback = callback;
62
+    }
63
+
64
+    @Override
65
+    public void onTransitionToBridgeIdle() {
66
+        android.util.Log.d("DebuggingIsHell", "IDLE");
67
+        bridgeIdle.set(true);
68
+    }
69
+
70
+    @Override
71
+    public void onTransitionToBridgeBusy() {
72
+        android.util.Log.d("DebuggingIsHell", "busy");
73
+        bridgeIdle.set(false);
74
+    }
75
+}

+ 1
- 1
playground/android/app/src/main/java/com/reactnativenavigation/playground/MainActivity.java View File

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.playground;
2 2
 
3
-import com.reactnativenavigation.controllers.NavigationActivity;
3
+import com.reactnativenavigation.NavigationActivity;
4 4
 
5 5
 public class MainActivity extends NavigationActivity {
6 6
 

+ 1
- 1
playground/android/app/src/main/java/com/reactnativenavigation/playground/MainApplication.java View File

@@ -1,6 +1,6 @@
1 1
 package com.reactnativenavigation.playground;
2 2
 
3
-import com.reactnativenavigation.controllers.NavigationApplication;
3
+import com.reactnativenavigation.NavigationApplication;
4 4
 
5 5
 public class MainApplication extends NavigationApplication {
6 6
 

+ 0
- 29
playground/e2e/androidLifecycle.test.js View File

@@ -1,29 +0,0 @@
1
-xdescribe('android application lifecycle', () => {
2
-  it('normal launch', () => {
3
-    //
4
-  });
5
-
6
-  it('launch already running in background', () => {
7
-    //
8
-  });
9
-
10
-  it('launch after activity killed by system', () => {
11
-    //
12
-  });
13
-
14
-  it('launch and ask react overlay permissions', () => {
15
-    //
16
-  });
17
-
18
-  it('launch after reactContext killed by system', () => {
19
-    //
20
-  });
21
-
22
-  it('launch from push notification', () => {
23
-    //
24
-  });
25
-
26
-  it('launch from intent filter', () => {
27
-    //
28
-  });
29
-});