Ran Greenberg vor 7 Jahren
Ursprung
Commit
15705fba96
30 geänderte Dateien mit 610 neuen und 90 gelöschten Zeilen
  1. 1
    0
      android/app/build.gradle
  2. 7
    7
      android/app/deprecated/java/com/reactnativenavigation/utils/ViewUtils.java
  3. 0
    2
      android/app/src/main/AndroidManifest.xml
  4. 37
    11
      android/app/src/main/java/com/reactnativenavigation/layout/LayoutFactory.java
  5. 89
    0
      android/app/src/main/java/com/reactnativenavigation/layout/bottomtabs/BottomTabs.java
  6. 63
    0
      android/app/src/main/java/com/reactnativenavigation/layout/bottomtabs/BottomTabsContainer.java
  7. 8
    0
      android/app/src/main/java/com/reactnativenavigation/layout/bottomtabs/BottomTabsCreator.java
  8. 4
    0
      android/app/src/main/java/com/reactnativenavigation/layout/bottomtabs/TooManyTabsException.java
  9. 2
    11
      android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java
  10. 2
    4
      android/app/src/main/java/com/reactnativenavigation/react/ReactDevPermission.java
  11. 2
    1
      android/app/src/main/res/values/styles.xml
  12. 6
    0
      android/app/src/test/RoboManifest.xml
  13. 128
    0
      android/app/src/test/java/com/reactnativenavigation/BottomTabsContainerTest.java
  14. 96
    42
      android/app/src/test/java/com/reactnativenavigation/LayoutFactoryTest.java
  15. 27
    0
      android/app/src/test/java/com/reactnativenavigation/TestUtils.java
  16. 4
    0
      playground/android/app/build.gradle
  17. 9
    0
      playground/android/app/src/androidTest/AndroidManifest.xml
  18. 32
    5
      playground/android/app/src/androidTest/java/com/reactnativenavigation/playground/ApplicationTest.java
  19. 2
    1
      playground/android/app/src/main/AndroidManifest.xml
  20. 4
    0
      playground/android/app/src/main/res/values/styles.xml
  21. 5
    1
      playground/e2e/app.test.js
  22. 1
    0
      playground/scripts/e2e.android.js
  23. 1
    0
      playground/scripts/e2e.ios.js
  24. 9
    4
      playground/src/containers/WelcomeScreen.js
  25. 4
    0
      src/Navigation.js
  26. 9
    0
      src/Navigation.test.js
  27. 5
    0
      src/adapters/NativeCommandsSender.js
  28. 8
    1
      src/adapters/NativeCommandsSender.test.js
  29. 7
    0
      src/commands/AppCommands.js
  30. 38
    0
      src/commands/AppCommands.test.js

+ 1
- 0
android/app/build.gradle Datei anzeigen

@@ -48,6 +48,7 @@ dependencies {
48 48
     compile fileTree(include: ['*.jar'], dir: 'libs')
49 49
     compile 'com.android.support:design:25.1.1'
50 50
     compile 'com.android.support:appcompat-v7:25.1.1'
51
+    compile "com.android.support:support-v4:25.1.1"
51 52
 
52 53
     // node_modules
53 54
     compile 'com.facebook.react:react-native:+'

+ 7
- 7
android/app/deprecated/java/com/reactnativenavigation/utils/ViewUtils.java Datei anzeigen

@@ -57,6 +57,13 @@ public class ViewUtils {
57 57
         return pixels * scaledDensity;
58 58
     }
59 59
 
60
+    public static float getScreenHeight() {
61
+        WindowManager wm = (WindowManager) NavigationApplication.instance.getSystemService(Context.WINDOW_SERVICE);
62
+        DisplayMetrics metrics = new DisplayMetrics();
63
+        wm.getDefaultDisplay().getMetrics(metrics);
64
+        return metrics.heightPixels;
65
+    }
66
+
60 67
     public static int generateViewId() {
61 68
         if (Build.VERSION.SDK_INT >= 17) {
62 69
             return View.generateViewId();
@@ -65,13 +72,6 @@ public class ViewUtils {
65 72
         }
66 73
     }
67 74
 
68
-    public static float getScreenHeight() {
69
-        WindowManager wm = (WindowManager) NavigationApplication.instance.getSystemService(Context.WINDOW_SERVICE);
70
-        DisplayMetrics metrics = new DisplayMetrics();
71
-        wm.getDefaultDisplay().getMetrics(metrics);
72
-        return metrics.heightPixels;
73
-    }
74
-
75 75
     private static int compatGenerateViewId() {
76 76
         for (; ; ) {
77 77
             final int result = viewId.get();

+ 0
- 2
android/app/src/main/AndroidManifest.xml Datei anzeigen

@@ -2,8 +2,6 @@
2 2
     package="com.reactnativenavigation">
3 3
 
4 4
     <application>
5
-        <activity android:name=".controllers.NavigationActivity" />
6
-
7 5
         <activity
8 6
             android:name="com.facebook.react.devsupport.DevSettingsActivity"
9 7
             android:exported="false" />

+ 37
- 11
android/app/src/main/java/com/reactnativenavigation/layout/LayoutFactory.java Datei anzeigen

@@ -3,35 +3,61 @@ package com.reactnativenavigation.layout;
3 3
 import android.app.Activity;
4 4
 import android.view.View;
5 5
 
6
+import com.reactnativenavigation.layout.bottomtabs.BottomTabsContainer;
7
+import com.reactnativenavigation.layout.bottomtabs.BottomTabsCreator;
8
+
6 9
 import java.util.List;
7 10
 
8 11
 public class LayoutFactory {
12
+
9 13
     public interface RootViewCreator {
10 14
         View createRootView(String id, String name);
11 15
     }
12 16
 
13
-    private Activity activity;
14
-    private RootViewCreator rootViewCreator;
17
+    private final Activity activity;
18
+    private final RootViewCreator rootViewCreator;
19
+    private final BottomTabsCreator bottomTabsCreator; // TODO: revisit this, may not be needed
15 20
 
16
-    public LayoutFactory(Activity activity, RootViewCreator rootViewCreator) {
21
+    public LayoutFactory(Activity activity, RootViewCreator rootViewCreator, BottomTabsCreator bottomTabsCreator) {
17 22
         this.activity = activity;
18 23
         this.rootViewCreator = rootViewCreator;
24
+        this.bottomTabsCreator = bottomTabsCreator;
19 25
     }
20 26
 
21 27
     public View create(LayoutNode node) {
22 28
         switch (node.type) {
23 29
             case "Container":
24
-                String name = (String) node.data.get("name");
25
-                return new Container(activity, rootViewCreator, node.id, name);
26
-
30
+                return createContainerView(node);
27 31
             case "ContainerStack":
28
-                ContainerStack containerStack = new ContainerStack(activity);
29
-                List<LayoutNode> children = node.children;
30
-                addChildrenNodes(containerStack, children);
31
-                return containerStack;
32
+                return createContainerStackView(node);
33
+            case "BottomTabs":
34
+                return createBottomTabs(node);
35
+            default:
36
+                throw new IllegalArgumentException("Invalid node type: "+node.type);
32 37
         }
38
+    }
39
+
40
+    private View createContainerView(LayoutNode node) {
41
+        final String name = (String) node.data.get("name");
42
+        return new Container(activity, rootViewCreator, node.id, name);
43
+    }
44
+
45
+    private View createContainerStackView(LayoutNode node) {
46
+        final ContainerStack containerStack = new ContainerStack(activity);
47
+        addChildrenNodes(containerStack, node.children);
48
+        return containerStack;
49
+    }
33 50
 
34
-        return null;
51
+    private View createBottomTabs(LayoutNode node) {
52
+        final BottomTabsContainer tabsContainer = new BottomTabsContainer(activity, bottomTabsCreator.create());
53
+
54
+        int i = 0;
55
+        for (LayoutNode child : node.children) {
56
+            final View tabContent = create(child);
57
+            tabsContainer.addTabContent("#" + i, tabContent);
58
+            i++;
59
+        }
60
+        return tabsContainer;
35 61
     }
36 62
 
37 63
     private void addChildrenNodes(ContainerStack containerStack, List<LayoutNode> children) {

+ 89
- 0
android/app/src/main/java/com/reactnativenavigation/layout/bottomtabs/BottomTabs.java Datei anzeigen

@@ -0,0 +1,89 @@
1
+package com.reactnativenavigation.layout.bottomtabs;
2
+
3
+import android.graphics.Color;
4
+import android.os.Build;
5
+import android.support.annotation.NonNull;
6
+import android.support.design.widget.BottomNavigationView;
7
+import android.view.Menu;
8
+import android.view.MenuItem;
9
+import android.view.View;
10
+import android.widget.RelativeLayout;
11
+import android.widget.RelativeLayout.LayoutParams;
12
+
13
+import java.util.concurrent.atomic.AtomicInteger;
14
+
15
+import static android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM;
16
+
17
+public class BottomTabs implements BottomNavigationView.OnNavigationItemSelectedListener {
18
+
19
+    interface BottomTabsSelectionListener {
20
+        void onTabSelected(int index);
21
+    }
22
+
23
+    private static final AtomicInteger viewId = new AtomicInteger(1);
24
+
25
+    private BottomNavigationView bottomNavigationView;
26
+    private BottomTabsSelectionListener listener;
27
+
28
+    public void attach(RelativeLayout parentLayout) {
29
+        createBottomNavigation(parentLayout);
30
+        addButtomNavigationToParent(parentLayout);
31
+    }
32
+
33
+    public void setSelectionListener(BottomTabsSelectionListener listener) {
34
+        this.listener = listener;
35
+    }
36
+
37
+    public void add(String label) {
38
+        int tabId = size();
39
+        bottomNavigationView.getMenu().add(0, tabId, Menu.NONE, label);
40
+    }
41
+
42
+    public int size() {
43
+        return bottomNavigationView.getMenu().size();
44
+    }
45
+
46
+    int getViewId() {
47
+        return bottomNavigationView.getId();
48
+    }
49
+
50
+    @Override
51
+    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
52
+        listener.onTabSelected(item.getItemId());
53
+        return true;
54
+    }
55
+
56
+    private void createBottomNavigation(RelativeLayout parentLayout) {
57
+        bottomNavigationView = new BottomNavigationView(parentLayout.getContext());
58
+        bottomNavigationView.setId(generateViewId());
59
+        bottomNavigationView.setBackgroundColor(Color.DKGRAY);
60
+        bottomNavigationView.setOnNavigationItemSelectedListener(this);
61
+    }
62
+
63
+    private void addButtomNavigationToParent(RelativeLayout parentLayout) {
64
+        LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
65
+        lp.addRule(ALIGN_PARENT_BOTTOM);
66
+        bottomNavigationView.setLayoutParams(lp);
67
+        parentLayout.addView(bottomNavigationView, lp);
68
+    }
69
+
70
+    private int generateViewId() {
71
+        if (Build.VERSION.SDK_INT >= 17) {
72
+            return View.generateViewId();
73
+        } else {
74
+            return compatGenerateViewId();
75
+        }
76
+    }
77
+
78
+    private int compatGenerateViewId() {
79
+        while(true) {
80
+            final int result = viewId.get();
81
+            // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
82
+            int newValue = result + 1;
83
+            if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
84
+            if (viewId.compareAndSet(result, newValue)) {
85
+                return result;
86
+            }
87
+        }
88
+    }
89
+}

+ 63
- 0
android/app/src/main/java/com/reactnativenavigation/layout/bottomtabs/BottomTabsContainer.java Datei anzeigen

@@ -0,0 +1,63 @@
1
+package com.reactnativenavigation.layout.bottomtabs;
2
+
3
+import android.app.Activity;
4
+import android.view.View;
5
+import android.widget.RelativeLayout;
6
+
7
+import java.util.ArrayList;
8
+import java.util.List;
9
+
10
+public class BottomTabsContainer extends RelativeLayout implements BottomTabs.BottomTabsSelectionListener {
11
+
12
+    private List<View> tabsContent;
13
+    private BottomTabs bottomTabs;
14
+    private int currentTab;
15
+
16
+    public BottomTabsContainer(Activity activity, BottomTabs bottomTabs) {
17
+        super(activity);
18
+        initBottomTabs(bottomTabs);
19
+    }
20
+
21
+    public void addTabContent(String label, View tabContent) {
22
+        if (tabsContent.size() > 5) {
23
+            throw new TooManyTabsException();
24
+        }
25
+        bottomTabs.add(label);
26
+        attachTabContent(tabContent);
27
+        tabsContent.add(tabContent);
28
+
29
+        if (tabsContent.size() > 1) {
30
+            tabContent.setVisibility(View.GONE);
31
+        }
32
+    }
33
+
34
+    @Override
35
+    public void onTabSelected(int index) {
36
+        hideTab(currentTab);
37
+        currentTab = index;
38
+        showTab(currentTab);
39
+    }
40
+
41
+    private void initBottomTabs(BottomTabs bottomTabs) {
42
+        this.bottomTabs = bottomTabs;
43
+        this.bottomTabs.attach(this);
44
+        this.bottomTabs.setSelectionListener(this);
45
+
46
+        tabsContent = new ArrayList<>();
47
+    }
48
+
49
+    private void attachTabContent(View tabContent) {
50
+        LayoutParams tabParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
51
+        tabParams.addRule(ABOVE, bottomTabs.getViewId());
52
+        addView(tabContent, tabParams);
53
+    }
54
+
55
+    private void showTab(int tabId) {
56
+        tabsContent.get(tabId).setVisibility(View.VISIBLE);
57
+    }
58
+
59
+    private void hideTab(int tabId) {
60
+        tabsContent.get(tabId).setVisibility(View.GONE);
61
+    }
62
+
63
+}

+ 8
- 0
android/app/src/main/java/com/reactnativenavigation/layout/bottomtabs/BottomTabsCreator.java Datei anzeigen

@@ -0,0 +1,8 @@
1
+package com.reactnativenavigation.layout.bottomtabs;
2
+
3
+public class BottomTabsCreator {
4
+
5
+    public BottomTabs create() {
6
+        return new BottomTabs();
7
+    }
8
+}

+ 4
- 0
android/app/src/main/java/com/reactnativenavigation/layout/bottomtabs/TooManyTabsException.java Datei anzeigen

@@ -0,0 +1,4 @@
1
+package com.reactnativenavigation.layout.bottomtabs;
2
+
3
+public class TooManyTabsException extends RuntimeException {
4
+}

+ 2
- 11
android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java Datei anzeigen

@@ -13,6 +13,7 @@ import com.facebook.react.bridge.ReadableMapKeySetIterator;
13 13
 import com.reactnativenavigation.controllers.NavigationActivity;
14 14
 import com.reactnativenavigation.layout.LayoutFactory;
15 15
 import com.reactnativenavigation.layout.LayoutNode;
16
+import com.reactnativenavigation.layout.bottomtabs.BottomTabsCreator;
16 17
 
17 18
 import java.util.ArrayList;
18 19
 import java.util.HashMap;
@@ -44,19 +45,11 @@ public class NavigationModule extends ReactContextBaseJavaModule {
44 45
                         rootView.startReactApplication(NavigationActivity.instance.getHost().getReactInstanceManager(), name, opts);
45 46
                         return rootView;
46 47
                     }
47
-                });
48
+                }, new BottomTabsCreator());
48 49
 
49 50
                 final LayoutNode layoutTreeRoot = readableMapToLayoutNode(layoutTree);
50 51
                 final View rootView = factory.create(layoutTreeRoot);
51 52
                 NavigationActivity.instance.setContentView(rootView);
52
-
53
-//                Map<String, Object> node = new HashMap<String, Object>();
54
-//                node.put("id", container.getString("id"));
55
-//                HashMap<String, Object> data = new HashMap<>();
56
-//                data.put("name", container.getMap("data").getString("name"));
57
-//                node.put("data", data);
58
-//                View rootView = factory.create(node);
59
-//                NavigationActivity.instance.setContentView(rootView);
60 53
             }
61 54
         });
62 55
     }
@@ -88,8 +81,6 @@ public class NavigationModule extends ReactContextBaseJavaModule {
88 81
                 case Map:
89 82
                     map.put(key, readableMapToJavaMap(readableMap.getMap(key)));
90 83
                     break;
91
-                default:
92
-                    throw new IllegalArgumentException("WTF?!");
93 84
             }
94 85
         }
95 86
         return map;

+ 2
- 4
android/app/src/main/java/com/reactnativenavigation/react/ReactDevPermission.java Datei anzeigen

@@ -16,7 +16,7 @@ public class ReactDevPermission {
16 16
     public static boolean shouldAskPermission() {
17 17
         return NavigationApplication.instance.isDebug() &&
18 18
                 Build.VERSION.SDK_INT >= 23 &&
19
-               !Settings.canDrawOverlays(NavigationApplication.instance);
19
+                !Settings.canDrawOverlays(NavigationApplication.instance);
20 20
     }
21 21
 
22 22
     @TargetApi(23)
@@ -28,9 +28,7 @@ public class ReactDevPermission {
28 28
             Log.w(ReactConstants.TAG, "======================================\n\n");
29 29
             Log.w(ReactConstants.TAG, msg);
30 30
             Log.w(ReactConstants.TAG, "\n\n======================================");
31
-            for (int i = 0; i < 5; i++) {
32
-                Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
33
-            }
31
+            Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
34 32
         }
35 33
     }
36 34
 }

+ 2
- 1
android/app/src/main/res/values/styles.xml Datei anzeigen

@@ -1,5 +1,6 @@
1 1
 <?xml version="1.0" encoding="utf-8"?>
2 2
 <resources>
3
+
3 4
     <style name="Modal" parent="@android:style/Theme.Translucent.NoTitleBar">
4 5
         <item name="android:windowAnimationStyle">@style/modalAnimations</item>
5 6
     </style>
@@ -7,4 +8,4 @@
7 8
     <style name="modalAnimations">
8 9
         <item name="android:windowExitAnimation">@anim/slide_down</item>
9 10
     </style>
10
-</resources>
11
+</resources>

+ 6
- 0
android/app/src/test/RoboManifest.xml Datei anzeigen

@@ -0,0 +1,6 @@
1
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+          package="com.reactnativenavigation">
3
+    <application android:theme="@style/Theme.AppCompat.Light">
4
+        <activity android:name=".RoboActivity" android:theme="@style/Theme.AppCompat.Light"/>
5
+    </application>
6
+</manifest>

+ 128
- 0
android/app/src/test/java/com/reactnativenavigation/BottomTabsContainerTest.java Datei anzeigen

@@ -0,0 +1,128 @@
1
+package com.reactnativenavigation;
2
+
3
+import android.app.Activity;
4
+import android.view.View;
5
+
6
+import com.reactnativenavigation.layout.bottomtabs.BottomTabs;
7
+import com.reactnativenavigation.layout.bottomtabs.BottomTabsContainer;
8
+import com.reactnativenavigation.layout.bottomtabs.TooManyTabsException;
9
+
10
+import org.junit.Before;
11
+import org.junit.Test;
12
+import org.junit.runner.RunWith;
13
+import org.robolectric.Robolectric;
14
+import org.robolectric.RobolectricTestRunner;
15
+
16
+import static org.assertj.core.api.Java6Assertions.assertThat;
17
+import static org.mockito.Mockito.mock;
18
+import static org.mockito.Mockito.verify;
19
+
20
+@RunWith(RobolectricTestRunner.class)
21
+public class BottomTabsContainerTest {
22
+
23
+    private static final String TAB_NAME = "myTab";
24
+    private static final String OTHER_TAB_NAME = "myOtherTab";
25
+
26
+    private BottomTabs bottomTabs;
27
+    private Activity activity;
28
+
29
+    @Before
30
+    public void setUp() throws Exception {
31
+        bottomTabs = mock(BottomTabs.class);
32
+        activity = Robolectric.buildActivity(Activity.class).get();
33
+    }
34
+
35
+    @Test
36
+    public void addsTabToBottomTabs() throws Exception {
37
+        View tabContent = new View(activity);
38
+
39
+        BottomTabsContainer bottomTabsContainer = createBottomTabsContainer();
40
+        bottomTabsContainer.addTabContent(TAB_NAME, tabContent);
41
+
42
+        verify(bottomTabs).add(TAB_NAME);
43
+    }
44
+
45
+    @Test
46
+    public void addsTabContentToLayout() throws Exception {
47
+        View tabContent = new View(activity);
48
+
49
+        BottomTabsContainer bottomTabsContainer = createBottomTabsContainer();
50
+        bottomTabsContainer.addTabContent(TAB_NAME, tabContent);
51
+
52
+        verify(bottomTabs).attach(bottomTabsContainer);
53
+        TestUtils.assertViewChildren(bottomTabsContainer, tabContent);
54
+    }
55
+
56
+    @Test
57
+    public void addsTwoTabsContentToLayout() throws Exception {
58
+        View tabContent = new View(activity);
59
+        View otherTabContent = new View(activity);
60
+
61
+        BottomTabsContainer bottomTabsContainer = createBottomTabsContainer();
62
+        bottomTabsContainer.addTabContent(TAB_NAME, tabContent);
63
+        bottomTabsContainer.addTabContent(OTHER_TAB_NAME, otherTabContent);
64
+
65
+        TestUtils.assertViewChildren(bottomTabsContainer, tabContent, otherTabContent);
66
+    }
67
+
68
+    @Test (expected = TooManyTabsException.class)
69
+    public void throwsExceptionWhenMoreThenFiveTabs() throws Exception {
70
+        BottomTabsContainer bottomTabsContainer = createBottomTabsContainer();
71
+        for (int i = 0; i <= 6; i++) {
72
+            View content = new View(activity);
73
+            bottomTabsContainer.addTabContent("#" + i, content);
74
+        }
75
+    }
76
+
77
+    @Test
78
+    public void onlyFirstTabShouldBeVisible() throws Exception {
79
+        View tabContent = new View(activity);
80
+        View otherTabContent = new View(activity);
81
+
82
+        BottomTabsContainer bottomTabsContainer = createBottomTabsContainer();
83
+        bottomTabsContainer.addTabContent(TAB_NAME, tabContent);
84
+        bottomTabsContainer.addTabContent(OTHER_TAB_NAME, otherTabContent);
85
+
86
+        assertThat(tabContent.getVisibility()).isEqualTo(View.VISIBLE);
87
+        assertThat(otherTabContent.getVisibility()).isEqualTo(View.GONE);
88
+    }
89
+
90
+    @Test
91
+    public void listensToTabsSwitchingEvents() throws Exception {
92
+        BottomTabsContainer bottomTabsContainer = createBottomTabsContainer();
93
+        verify(bottomTabs).setSelectionListener(bottomTabsContainer);
94
+    }
95
+
96
+    @Test
97
+    public void switchesBetweenFirstAndSecondTabs() throws Exception {
98
+        View tabContent = new View(activity);
99
+        View otherTabContent = new View(activity);
100
+
101
+        BottomTabsContainer bottomTabsContainer = createBottomTabsContainer();
102
+        bottomTabsContainer.addTabContent(TAB_NAME, tabContent);
103
+        bottomTabsContainer.addTabContent(OTHER_TAB_NAME, otherTabContent);
104
+        bottomTabsContainer.onTabSelected(1);
105
+
106
+        assertThat(tabContent.getVisibility()).isEqualTo(View.GONE);
107
+        assertThat(otherTabContent.getVisibility()).isEqualTo(View.VISIBLE);
108
+    }
109
+
110
+    @Test
111
+    public void switchesBetweenSecondAndFirstTabs() throws Exception {
112
+        View tabContent = new View(activity);
113
+        View otherTabContent = new View(activity);
114
+
115
+        BottomTabsContainer bottomTabsContainer = createBottomTabsContainer();
116
+        bottomTabsContainer.addTabContent(TAB_NAME, tabContent);
117
+        bottomTabsContainer.addTabContent(OTHER_TAB_NAME, otherTabContent);
118
+        bottomTabsContainer.onTabSelected(1);
119
+        bottomTabsContainer.onTabSelected(0);
120
+
121
+        assertThat(tabContent.getVisibility()).isEqualTo(View.VISIBLE);
122
+        assertThat(otherTabContent.getVisibility()).isEqualTo(View.GONE);
123
+    }
124
+
125
+    private BottomTabsContainer createBottomTabsContainer() {
126
+        return new BottomTabsContainer(activity, bottomTabs);
127
+    }
128
+}

+ 96
- 42
android/app/src/test/java/com/reactnativenavigation/LayoutFactoryTest.java Datei anzeigen

@@ -1,6 +1,7 @@
1 1
 package com.reactnativenavigation;
2 2
 
3 3
 import android.app.Activity;
4
+import android.support.v7.app.AppCompatActivity;
4 5
 import android.view.View;
5 6
 import android.view.ViewGroup;
6 7
 
@@ -8,6 +9,9 @@ import com.reactnativenavigation.layout.Container;
8 9
 import com.reactnativenavigation.layout.ContainerStack;
9 10
 import com.reactnativenavigation.layout.LayoutFactory;
10 11
 import com.reactnativenavigation.layout.LayoutNode;
12
+import com.reactnativenavigation.layout.bottomtabs.BottomTabs;
13
+import com.reactnativenavigation.layout.bottomtabs.BottomTabsContainer;
14
+import com.reactnativenavigation.layout.bottomtabs.BottomTabsCreator;
11 15
 
12 16
 import org.junit.Before;
13 17
 import org.junit.Test;
@@ -15,17 +19,17 @@ import org.junit.runner.RunWith;
15 19
 import org.robolectric.Robolectric;
16 20
 import org.robolectric.RobolectricTestRunner;
17 21
 
18
-import java.util.ArrayList;
19 22
 import java.util.Arrays;
23
+import java.util.Collections;
20 24
 import java.util.HashMap;
21 25
 import java.util.List;
22 26
 
23 27
 import static org.assertj.core.api.Java6Assertions.assertThat;
24 28
 import static org.mockito.ArgumentMatchers.eq;
25 29
 import static org.mockito.Mockito.mock;
30
+import static org.mockito.Mockito.verify;
26 31
 import static org.mockito.Mockito.when;
27 32
 
28
-
29 33
 @RunWith(RobolectricTestRunner.class)
30 34
 public class LayoutFactoryTest {
31 35
 
@@ -35,95 +39,145 @@ public class LayoutFactoryTest {
35 39
     private final static String OTHER_VIEW_ID = "anotherUniqueId";
36 40
     private final static String OTHER_VIEW_NAME = "anotherName";
37 41
 
42
+    private Activity activity;
38 43
     private View mockView;
44
+    private View otherMockView;
39 45
     private LayoutFactory.RootViewCreator rootViewCreator;
40 46
 
41 47
     @Before
42 48
     public void setUp() {
43
-        mockView = new View(Robolectric.setupActivity(Activity.class));
49
+        activity = Robolectric.buildActivity(AppCompatActivity.class).get();
50
+        mockView = new View(activity);
51
+        otherMockView = new View(activity);
44 52
         rootViewCreator = mock(LayoutFactory.RootViewCreator.class);
45 53
     }
46 54
 
47 55
     @Test
48
-    public void returnsContainerThatHoldsTheRootView() {
56
+    public void returnsContainerThatHoldsTheRootView() throws Exception {
49 57
         when(rootViewCreator.createRootView(eq(VIEW_ID), eq(VIEW_NAME))).thenReturn(mockView);
50 58
         final LayoutNode node = createContainerNode();
51 59
 
52 60
         final ViewGroup result = (ViewGroup) createLayoutFactory().create(node);
53 61
 
54 62
         assertThat(result).isInstanceOf(Container.class);
55
-        assertViewChildren(result, mockView);
63
+        TestUtils.assertViewChildren(result, mockView);
56 64
     }
57 65
 
58 66
     @Test
59
-    public void returnsContainerStack() {
67
+    public void returnsContainerStack() throws Exception {
60 68
         when(rootViewCreator.createRootView(eq(VIEW_ID), eq(VIEW_NAME))).thenReturn(mockView);
61
-        final LayoutNode node = createContainerNode();
62
-        final LayoutNode outerNode = getContainerStackNode(node);
69
+        final LayoutNode containerNode = createContainerNode();
70
+        final LayoutNode stackNode = createContainerStackNode(containerNode);
63 71
 
64
-        final ViewGroup result = (ViewGroup) createLayoutFactory().create(outerNode);
72
+        final ViewGroup result = (ViewGroup) createLayoutFactory().create(stackNode);
65 73
 
66 74
         assertThat(result).isInstanceOf(ContainerStack.class);
67
-        ViewGroup container = (ViewGroup) assertViewChildrenCount(result, 1).get(0);
68
-        assertViewChildren(container, mockView);
75
+        ViewGroup container = (ViewGroup) TestUtils.assertViewChildrenCount(result, 1).get(0);
76
+        TestUtils.assertViewChildren(container, mockView);
69 77
     }
70 78
 
71 79
     @Test
72
-    public void returnsContainerStackWithMultipleViews() {
80
+    public void returnsContainerStackWithMultipleViews() throws Exception {
73 81
         final View mockView1 = mock(View.class);
74 82
         final View mockView2 = mock(View.class);
75 83
         when(rootViewCreator.createRootView(eq(VIEW_ID), eq(VIEW_NAME))).thenReturn(mockView1);
76 84
         when(rootViewCreator.createRootView(eq(OTHER_VIEW_ID), eq(OTHER_VIEW_NAME))).thenReturn(mockView2);
77 85
 
78
-        final LayoutNode node1 = createContainerNode(VIEW_ID, VIEW_NAME);
79
-        final LayoutNode node2 = createContainerNode(OTHER_VIEW_ID, OTHER_VIEW_NAME);
80
-        final LayoutNode outerNode = getContainerStackNode(Arrays.asList(node1, node2));
86
+        final LayoutNode containerNode1 = createContainerNode(VIEW_ID, VIEW_NAME);
87
+        final LayoutNode containerNode2 = createContainerNode(OTHER_VIEW_ID, OTHER_VIEW_NAME);
88
+        final LayoutNode stackNode = createContainerStackNode(containerNode1, containerNode2);
81 89
 
82
-        final ViewGroup result = (ViewGroup) createLayoutFactory().create(outerNode);
90
+        final ViewGroup result = (ViewGroup) createLayoutFactory().create(stackNode);
83 91
 
84 92
         assertThat(result).isInstanceOf(ContainerStack.class);
85
-        List<View> containers = assertViewChildrenCount(result, 2);
93
+        List<View> containers = TestUtils.assertViewChildrenCount(result, 2);
86 94
         ViewGroup container1 = (ViewGroup) containers.get(0);
87 95
         ViewGroup container2 = (ViewGroup) containers.get(1);
88
-        assertViewChildren(container1, mockView1);
89
-        assertViewChildren(container2, mockView2);
96
+        TestUtils.assertViewChildren(container1, mockView1);
97
+        TestUtils.assertViewChildren(container2, mockView2);
90 98
     }
91 99
 
92
-    private LayoutFactory createLayoutFactory() {
93
-        return new LayoutFactory(Robolectric.buildActivity(Activity.class).get(), rootViewCreator);
100
+    @Test
101
+    public void returnsSingleTabContent() throws Exception {
102
+        BottomTabs bottomTabsMock = mock(BottomTabs.class);
103
+        when(bottomTabsMock.size()).thenReturn(0);
104
+
105
+        when(rootViewCreator.createRootView(eq(VIEW_ID), eq(VIEW_NAME))).thenReturn(mockView);
106
+        final LayoutNode containerNode = createContainerNode();
107
+        final LayoutNode tabNode = createTabNode(containerNode);
108
+
109
+        final View result = createLayoutFactory(bottomTabsMock).create(tabNode);
110
+
111
+        verify(bottomTabsMock).add("#0");
112
+
113
+        assertThat(result).isInstanceOf(BottomTabsContainer.class);
114
+        Container container = (Container) TestUtils.assertViewChildrenCount((BottomTabsContainer) result, 1).get(0);
115
+        View view = TestUtils.assertViewChildrenCount(container, 1).get(0);
116
+        assertThat(view).isEqualTo(mockView);
94 117
     }
95 118
 
96
-    private LayoutNode createContainerNode() {
97
-        return createContainerNode(VIEW_ID, VIEW_NAME);
119
+    @Test
120
+    public void returnsTwoTabContent() throws Exception {
121
+        BottomTabs bottomTabsMock = mock(BottomTabs.class);
122
+        when(bottomTabsMock.size()).thenReturn(0, 1);
123
+
124
+        when(rootViewCreator.createRootView(eq(VIEW_ID), eq(VIEW_NAME))).thenReturn(mockView);
125
+        final LayoutNode firstTabRootNode = createContainerNode(VIEW_ID, VIEW_NAME);
126
+
127
+        when(rootViewCreator.createRootView(eq(OTHER_VIEW_ID), eq(OTHER_VIEW_NAME))).thenReturn(otherMockView);
128
+        final LayoutNode secondTabRootNode = createContainerStackNode(createContainerNode(OTHER_VIEW_ID, OTHER_VIEW_NAME));
129
+
130
+        final LayoutNode tabNode = createTabNode(firstTabRootNode, secondTabRootNode);
131
+
132
+        final View result = createLayoutFactory(bottomTabsMock).create(tabNode);
133
+
134
+        assertThat(result).isInstanceOf(BottomTabsContainer.class);
135
+        verify(bottomTabsMock).add(eq("#0"));
136
+        verify(bottomTabsMock).add(eq("#1"));
98 137
     }
99 138
 
100
-    private LayoutNode createContainerNode(final String id, final String name) {
101
-        return new LayoutNode(id, "Container", new HashMap<String, Object>() {{ put("name", name); }});
139
+
140
+    @Test(expected = IllegalArgumentException.class)
141
+    public void throwsExceptionForUnknownType() throws Exception {
142
+        when(rootViewCreator.createRootView(eq(VIEW_ID), eq(VIEW_NAME))).thenReturn(mockView);
143
+        final LayoutNode node = new LayoutNode(VIEW_ID, "***unknownType***", Collections.<String, Object>emptyMap());
144
+
145
+        createLayoutFactory().create(node);
102 146
     }
103 147
 
104
-    private LayoutNode getContainerStackNode(LayoutNode innerNode) {
105
-        return getContainerStackNode(Arrays.asList(innerNode));
148
+    private LayoutFactory createLayoutFactory() {
149
+        return createLayoutFactory(null);
106 150
     }
107 151
 
108
-    private LayoutNode getContainerStackNode(List<LayoutNode> children) {
109
-        LayoutNode outerNode = new LayoutNode();
110
-        outerNode.type = "ContainerStack";
111
-        outerNode.children = children;
112
-        return outerNode;
152
+    private LayoutFactory createLayoutFactory(BottomTabs bottomTabs) {
153
+        BottomTabsCreator bottomTabsCreator = null;
154
+        if (bottomTabs != null) {
155
+            bottomTabsCreator = mock(BottomTabsCreator.class);
156
+            when(bottomTabsCreator.create()).thenReturn(bottomTabs);
157
+        }
158
+
159
+        return new LayoutFactory(activity, rootViewCreator, bottomTabsCreator);
113 160
     }
114 161
 
115
-    private List<View> assertViewChildrenCount(ViewGroup view, int count) {
116
-        assertThat(view.getChildCount()).isEqualTo(count);
162
+    private LayoutNode createContainerNode() {
163
+        return createContainerNode(VIEW_ID, VIEW_NAME);
164
+    }
117 165
 
118
-        final List<View> children = new ArrayList<>(count);
119
-        for (int i = 0; i < count; i++) {
120
-            children.add(view.getChildAt(i));
121
-        }
122
-        return children;
166
+    private LayoutNode createContainerNode(final String id, final String name) {
167
+        return new LayoutNode(id, "Container", new HashMap<String, Object>() {{ put("name", name); }});
168
+    }
169
+
170
+    private LayoutNode createContainerStackNode(LayoutNode... children) {
171
+        LayoutNode node = new LayoutNode();
172
+        node.type = "ContainerStack";
173
+        node.children = Arrays.asList(children);
174
+        return node;
123 175
     }
124 176
 
125
-    private void assertViewChildren(ViewGroup view, View... children) {
126
-        final List<View> childViews = assertViewChildrenCount(view, children.length);
127
-        assertThat(childViews).isEqualTo(Arrays.asList(children));
177
+    private LayoutNode createTabNode(LayoutNode... children) {
178
+        LayoutNode node = new LayoutNode();
179
+        node.type = "BottomTabs";
180
+        node.children = Arrays.asList(children);
181
+        return node;
128 182
     }
129 183
 }

+ 27
- 0
android/app/src/test/java/com/reactnativenavigation/TestUtils.java Datei anzeigen

@@ -0,0 +1,27 @@
1
+package com.reactnativenavigation;
2
+
3
+import android.view.View;
4
+import android.view.ViewGroup;
5
+
6
+import java.util.ArrayList;
7
+import java.util.Arrays;
8
+import java.util.List;
9
+
10
+import static org.assertj.core.api.Java6Assertions.assertThat;
11
+
12
+public class TestUtils {
13
+    public static List<View> assertViewChildrenCount(ViewGroup view, int count) {
14
+        assertThat(view.getChildCount()).isEqualTo(count);
15
+
16
+        final List<View> children = new ArrayList<>(count);
17
+        for (int i = 0; i < count; i++) {
18
+            children.add(view.getChildAt(i));
19
+        }
20
+        return children;
21
+    }
22
+
23
+    public static void assertViewChildren(ViewGroup view, View... children) {
24
+        final List<View> childViews = assertViewChildrenCount(view, children.length);
25
+        assertThat(childViews).isEqualTo(Arrays.asList(children));
26
+    }
27
+}

+ 4
- 0
playground/android/app/build.gradle Datei anzeigen

@@ -27,9 +27,13 @@ dependencies {
27 27
     androidTestCompile 'com.android.support:support-annotations:25.1.1'
28 28
     androidTestCompile 'com.android.support.test:runner:0.5'
29 29
     androidTestCompile 'com.android.support.test:rules:0.5'
30
+    androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
30 31
     androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2') {
31 32
         exclude module: 'jsr305'
32 33
     }
34
+    androidTestCompile('com.android.support.test.espresso:espresso-intents:2.2.2') {
35
+        exclude module: 'jsr305'
36
+    }
33 37
     androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.2.2') {
34 38
         exclude module: 'support-annotations'
35 39
         exclude module: 'support-v4'

+ 9
- 0
playground/android/app/src/androidTest/AndroidManifest.xml Datei anzeigen

@@ -0,0 +1,9 @@
1
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+    xmlns:tools="http://schemas.android.com/tools"
3
+    package="com.reactnativenavigation.playground">
4
+
5
+    <uses-sdk
6
+        android:minSdkVersion="18"
7
+        tools:overrideLibrary="android.support.test.uiautomator.v18" />
8
+
9
+</manifest>

+ 32
- 5
playground/android/app/src/androidTest/java/com/reactnativenavigation/playground/ApplicationTest.java Datei anzeigen

@@ -1,25 +1,52 @@
1 1
 package com.reactnativenavigation.playground;
2 2
 
3
-import android.support.test.espresso.action.ViewActions;
3
+import android.app.Activity;
4
+import android.app.Instrumentation;
5
+import android.provider.Settings;
6
+import android.support.test.espresso.intent.Intents;
4 7
 import android.support.test.rule.ActivityTestRule;
5 8
 import android.support.test.runner.AndroidJUnit4;
9
+import android.support.test.uiautomator.UiDevice;
10
+import android.support.test.uiautomator.UiObjectNotFoundException;
11
+import android.support.test.uiautomator.UiSelector;
6 12
 
7 13
 import org.junit.Rule;
8 14
 import org.junit.Test;
9 15
 import org.junit.runner.RunWith;
10 16
 
17
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
11 18
 import static android.support.test.espresso.Espresso.onView;
19
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
20
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction;
21
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
12 22
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
13 23
 
14 24
 @RunWith(AndroidJUnit4.class)
15 25
 public class ApplicationTest {
16 26
 
17 27
     @Rule
18
-    public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class);
28
+    public ActivityTestRule<MainActivity> rule = new ActivityTestRule<>(MainActivity.class, false, false);
19 29
 
20 30
     @Test
21
-    public void startsTheApp() {
22
-        
23
-        onView(withText("Playground")).perform(ViewActions.click());
31
+    public void startAppShowsTheSplash() throws InterruptedException {
32
+        Intents.init();
33
+        Intents.intending(hasAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)).respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, null));
34
+
35
+        rule.launchActivity(null);
36
+
37
+        onView(withText("Splash :)")).check(matches(isDisplayed()));
38
+        Intents.release();
39
+    }
40
+
41
+    @Test
42
+    public void startAppLoadsBridgeShowsWelcomeScreen() throws UiObjectNotFoundException {
43
+        rule.launchActivity(null);
44
+
45
+        UiDevice.getInstance(getInstrumentation()).findObject(new UiSelector().text("Playground")).click();
46
+        UiDevice.getInstance(getInstrumentation()).findObject(new UiSelector().text("Permit drawing over other apps")).click();
47
+        UiDevice.getInstance(getInstrumentation()).pressBack();
48
+        UiDevice.getInstance(getInstrumentation()).pressBack();
49
+
50
+        onView(withText("React Native Navigation!")).check(matches(isDisplayed()));
24 51
     }
25 52
 }

+ 2
- 1
playground/android/app/src/main/AndroidManifest.xml Datei anzeigen

@@ -8,7 +8,8 @@
8 8
         android:allowBackup="false"
9 9
         android:icon="@mipmap/ic_launcher"
10 10
         android:label="@string/app_name"
11
-        android:theme="@style/Theme.AppCompat.Light.NoActionBar">
11
+        android:theme="@style/AppTheme"
12
+        >
12 13
         <activity
13 14
             android:name=".MainActivity"
14 15
             android:label="@string/app_name">

+ 4
- 0
playground/android/app/src/main/res/values/styles.xml Datei anzeigen

@@ -0,0 +1,4 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<resources>
3
+    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"/>
4
+</resources>

+ 5
- 1
playground/e2e/app.test.js Datei anzeigen

@@ -38,7 +38,11 @@ describe('app', () => {
38 38
     elementByLabel('Pop').tap();
39 39
     expect(elementByLabel('React Native Navigation!')).toBeVisible();
40 40
   });
41
-  
41
+
42
+  xit('show modal', () => {
43
+    elementByLabel('Show Modal').tap();
44
+    expect(elementByLabel('Modal screen')).toBeVisible();
45
+  });
42 46
 });
43 47
 
44 48
 describe('reload app', () => {

+ 1
- 0
playground/scripts/e2e.android.js Datei anzeigen

@@ -7,6 +7,7 @@ const release = _.includes(process.argv, 'release');
7 7
 function e2e() { //eslint-disable-line
8 8
   try {
9 9
     shellUtils.exec.execSync(`echo 'travis_fold:start:android-espresso'`);
10
+    shellUtils.exec.execSync(`cd android && ./gradlew --stop`);
10 11
     shellUtils.exec.execSync(`cd android && ./gradlew connectedDebugAndroidTest`);
11 12
   } finally {
12 13
     shellUtils.exec.execSync(`echo 'travis_fold:end:android-espresso'`);

+ 1
- 0
playground/scripts/e2e.ios.js Datei anzeigen

@@ -60,6 +60,7 @@ function run() {
60 60
   buildProjForDetox();
61 61
   if (process.env.TRAVIS) {
62 62
     console.warn('skipping e2e temporarily due to fbsimctl is currently broken'); //eslint-disable-line
63
+    return;
63 64
   }
64 65
   e2e();
65 66
 }

+ 9
- 4
playground/src/containers/WelcomeScreen.js Datei anzeigen

@@ -18,7 +18,7 @@ class WelcomeScreen extends Component {
18 18
         <Button title="Switch to app with side menus" onPress={this.onClickSwitchToSideMenus} />
19 19
         <Button title="Switch to lifecycle screen" onPress={this.onClickLifecycleScreen} />
20 20
         <Button title="Push" onPress={this.onClickPush} />
21
-        <Button title="Show Modal" onPress={this.onClickPush} />
21
+        <Button title="Show Modal" onPress={this.onClickShowModal} />
22 22
         <Text style={styles.footer}>{`this.props.id = ${this.props.id}`}</Text>
23 23
       </View>
24 24
     );
@@ -105,9 +105,14 @@ class WelcomeScreen extends Component {
105 105
       }
106 106
     });
107 107
   }
108
-  
109
-  onShowModal() {
110
-    Navigation.on(this.props.id).show
108
+
109
+  onClickShowModal() {
110
+    Navigation.showModal({
111
+      name: 'navigation.playground.SimpleScreen',
112
+      passProps: {
113
+        text: 'Modal screen'
114
+      }
115
+    });
111 116
   }
112 117
 }
113 118
 

+ 4
- 0
src/Navigation.js Datei anzeigen

@@ -34,6 +34,10 @@ class Navigation {
34 34
     return this.appCommands.setRoot(params);
35 35
   }
36 36
 
37
+  showModal(params) {
38
+    return this.appCommands.showModal(params);
39
+  }
40
+
37 41
   events() {
38 42
     return this.publicEventsRegistry;
39 43
   }

+ 9
- 0
src/Navigation.test.js Datei anzeigen

@@ -30,6 +30,15 @@ describe('Navigation', () => {
30 30
     expect(Navigation.appCommands.setRoot).toHaveBeenCalledWith(params);
31 31
   });
32 32
 
33
+  it('showModal delegates to AppCommands', async () => {
34
+    Navigation.appCommands.showModal.mockReturnValue(Promise.resolve('result'));
35
+    const params = {};
36
+    const result = await Navigation.showModal(params);
37
+    expect(result).toEqual('result');
38
+    expect(Navigation.appCommands.showModal).toHaveBeenCalledTimes(1);
39
+    expect(Navigation.appCommands.showModal).toHaveBeenCalledWith(params);
40
+  });
41
+
33 42
   it('events return public events', () => {
34 43
     const cb = jest.fn();
35 44
     Navigation.events().onAppLaunched(cb);

+ 5
- 0
src/adapters/NativeCommandsSender.js Datei anzeigen

@@ -19,5 +19,10 @@ export default class NativeCommandsSender {
19 19
     this.nativeCommandsModule.pop(containerId);
20 20
     return Promise.resolve(containerId);
21 21
   }
22
+
23
+  showModal(layout) {
24
+    this.nativeCommandsModule.showModal(layout);
25
+    return Promise.resolve(layout);
26
+  }
22 27
 }
23 28
 

+ 8
- 1
src/adapters/NativeCommandsSender.test.js Datei anzeigen

@@ -8,7 +8,8 @@ describe('NativeCommandsSender', () => {
8 8
     mockNativeModule = {
9 9
       setRoot: jest.fn(),
10 10
       push: jest.fn(),
11
-      pop: jest.fn()
11
+      pop: jest.fn(),
12
+      showModal: jest.fn()
12 13
     };
13 14
     NativeModules.RNNBridgeModule = mockNativeModule;
14 15
     uut = new NativeCommandsSender();
@@ -36,4 +37,10 @@ describe('NativeCommandsSender', () => {
36 37
     expect(mockNativeModule.pop).toHaveBeenCalledTimes(1);
37 38
     expect(result).toBeDefined();
38 39
   });
40
+
41
+  it('showModal sends to native', async () => {
42
+    const result = await uut.showModal({});
43
+    expect(mockNativeModule.showModal).toHaveBeenCalledTimes(1);
44
+    expect(result).toBeDefined();
45
+  });
39 46
 });

+ 7
- 0
src/commands/AppCommands.js Datei anzeigen

@@ -13,5 +13,12 @@ export default class AppCommands {
13 13
     this.layoutTreeCrawler.crawl(layout);
14 14
     return this.nativeCommandsSender.setRoot(layout);
15 15
   }
16
+
17
+  showModal(simpleApi) {
18
+    const input = _.cloneDeep(simpleApi);
19
+    const layout = this.layoutTreeParser.createContainer(input);
20
+    this.layoutTreeCrawler.crawl(layout);
21
+    return this.nativeCommandsSender.showModal(layout);
22
+  }
16 23
 }
17 24
 

+ 38
- 0
src/commands/AppCommands.test.js Datei anzeigen

@@ -62,4 +62,42 @@ describe('AppCommands', () => {
62 62
       expect(result).toEqual('the resolved layout');
63 63
     });
64 64
   });
65
+
66
+  describe('showModal', () => {
67
+    it('sends command to native after parsing into a correct layout tree', () => {
68
+      uut.showModal({
69
+        name: 'com.example.MyScreen'
70
+      });
71
+      expect(mockCommandsSender.showModal).toHaveBeenCalledTimes(1);
72
+      expect(mockCommandsSender.showModal).toHaveBeenCalledWith({
73
+        type: 'Container',
74
+        id: 'Container+UNIQUE_ID',
75
+        children: [],
76
+        data: {
77
+          name: 'com.example.MyScreen'
78
+        }
79
+      });
80
+    });
81
+
82
+    it('deep clones input to avoid mutation errors', () => {
83
+      const obj = {};
84
+      uut.showModal({ inner: obj });
85
+      expect(mockCommandsSender.showModal.mock.calls[0][0].data.inner).not.toBe(obj);
86
+    });
87
+
88
+    it('passProps into containers', () => {
89
+      expect(store.getPropsForContainerId('Container+UNIQUE_ID')).toEqual({});
90
+      uut.showModal({
91
+        name: 'com.example.MyScreen',
92
+        passProps: SimpleLayouts.passProps
93
+      });
94
+      expect(store.getPropsForContainerId('Container+UNIQUE_ID')).toEqual(SimpleLayouts.passProps);
95
+    });
96
+
97
+    it('returns a promise with the resolved layout', async () => {
98
+      mockCommandsSender.showModal.mockReturnValue(Promise.resolve('the resolved layout'));
99
+      const result = await uut.showModal({ container: { name: 'com.example.MyScreen' } });
100
+      expect(result).toEqual('the resolved layout');
101
+    });
102
+  });
65 103
 });