Browse Source

Detox android (#2811)

* Make detox android test suit pass

Disabled emulator animations due to limitation of espresso/detox which can't wait until
a push/pop animation ends

* Fix orientation parsing and enable detox orientation tests

* remove only

* Fix BottomTabs testId

* Support user orientation

* Fix rightButtons testId

* Add Modals tests

* e2e fix ios
Guy Carmeli 6 years ago
parent
commit
cb4ced860f
No account linked to committer's email address

+ 3
- 3
AndroidE2E/app/src/androidTest/java/com/reactnativenavigation/e2e/androide2e/OverlayTest.java View File

12
 		elementByText("PUSH OPTIONS SCREEN").click();
12
 		elementByText("PUSH OPTIONS SCREEN").click();
13
         elementByText("SHOW OVERLAY").click();
13
         elementByText("SHOW OVERLAY").click();
14
 		assertExists(By.text("Test view"));
14
 		assertExists(By.text("Test view"));
15
-        assetDismissed();
15
+        assertDismissed();
16
 	}
16
 	}
17
 
17
 
18
     @Test
18
     @Test
22
 		assertExists(By.text("Test view"));
22
 		assertExists(By.text("Test view"));
23
         elementByText("DYNAMIC OPTIONS").click();
23
         elementByText("DYNAMIC OPTIONS").click();
24
         assertExists(By.text("Dynamic Title"));
24
         assertExists(By.text("Dynamic Title"));
25
-        assetDismissed();
25
+        assertDismissed();
26
 	}
26
 	}
27
 
27
 
28
-    private void assetDismissed() throws UiObjectNotFoundException {
28
+    private void assertDismissed() throws UiObjectNotFoundException {
29
         elementByText("OK").click();
29
         elementByText("OK").click();
30
         assertExists(By.text("Overlay disappeared"));
30
         assertExists(By.text("Overlay disappeared"));
31
         elementByText("OK").click();
31
         elementByText("OK").click();

+ 15
- 1
e2e/Modals.test.js View File

1
 const Utils = require('./Utils');
1
 const Utils = require('./Utils');
2
 const testIDs = require('../playground/src/testIDs');
2
 const testIDs = require('../playground/src/testIDs');
3
 
3
 
4
-const { elementByLabel, elementById } = Utils;
4
+const { elementByLabel, elementById, tapDeviceBackAndroid } = Utils;
5
 
5
 
6
 describe('modal', () => {
6
 describe('modal', () => {
7
   beforeEach(async () => {
7
   beforeEach(async () => {
92
     await elementById(testIDs.DISMISS_ALL_MODALS_BUTTON).tap();
92
     await elementById(testIDs.DISMISS_ALL_MODALS_BUTTON).tap();
93
     await expect(elementById(testIDs.WELCOME_SCREEN_HEADER)).toBeVisible();
93
     await expect(elementById(testIDs.WELCOME_SCREEN_HEADER)).toBeVisible();
94
   });
94
   });
95
+
96
+  it('push into modal', async () => {
97
+    await elementById(testIDs.SHOW_MODAL_BUTTON).tap();
98
+    await elementById(testIDs.PUSH_BUTTON).tap();
99
+    await expect(elementByLabel('Pushed Screen')).toBeVisible();
100
+  });
101
+
102
+  it(':android: push into modal', async () => {
103
+    await elementById(testIDs.SHOW_MODAL_BUTTON).tap();
104
+    await elementById(testIDs.PUSH_BUTTON).tap();
105
+    await elementById(testIDs.PUSH_BUTTON).tap();
106
+    tapDeviceBackAndroid();
107
+    await expect(elementByLabel('Pushed Screen')).toBeVisible();
108
+  });
95
 });
109
 });

+ 4
- 3
e2e/Orientations.test.js View File

4
 
4
 
5
 const { elementById } = Utils;
5
 const { elementById } = Utils;
6
 
6
 
7
-describe(':ios: orientation', () => {
7
+describe('orientation', () => {
8
+
8
   beforeEach(async () => {
9
   beforeEach(async () => {
9
     await device.relaunchApp();
10
     await device.relaunchApp();
10
   });
11
   });
31
     await elementById(testIDs.DISMISS_BUTTON).tap();
32
     await elementById(testIDs.DISMISS_BUTTON).tap();
32
   });
33
   });
33
 
34
 
34
-  it('portrait only', async () => {
35
+  it(':ios: portrait only', async () => {
35
     await elementById(testIDs.ORIENTATION_BUTTON).tap();
36
     await elementById(testIDs.ORIENTATION_BUTTON).tap();
36
     await elementById(testIDs.PORTRAIT_ORIENTATION_BUTTON).tap();
37
     await elementById(testIDs.PORTRAIT_ORIENTATION_BUTTON).tap();
37
     await expect(elementById(testIDs.PORTRAIT_ELEMENT)).toBeVisible();
38
     await expect(elementById(testIDs.PORTRAIT_ELEMENT)).toBeVisible();
42
     await elementById(testIDs.DISMISS_BUTTON).tap();
43
     await elementById(testIDs.DISMISS_BUTTON).tap();
43
   });
44
   });
44
 
45
 
45
-  it('landscape only', async () => {
46
+  it(':ios: landscape only', async () => {
46
     await elementById(testIDs.ORIENTATION_BUTTON).tap();
47
     await elementById(testIDs.ORIENTATION_BUTTON).tap();
47
     await elementById(testIDs.LANDSCAPE_ORIENTATION_BUTTON).tap();
48
     await elementById(testIDs.LANDSCAPE_ORIENTATION_BUTTON).tap();
48
     await device.setOrientation('landscape');
49
     await device.setOrientation('landscape');

+ 5
- 8
e2e/ScreenStyle.test.js View File

21
   });
21
   });
22
 
22
 
23
   it('set dynamic options with valid options will do something and not crash', async () => {
23
   it('set dynamic options with valid options will do something and not crash', async () => {
24
-    // we have no way of testing individual styles for the screen
25
     await elementById(testIDs.PUSH_OPTIONS_BUTTON).tap();
24
     await elementById(testIDs.PUSH_OPTIONS_BUTTON).tap();
26
     await elementById(testIDs.DYNAMIC_OPTIONS_BUTTON).tap();
25
     await elementById(testIDs.DYNAMIC_OPTIONS_BUTTON).tap();
27
     await expect(elementById(testIDs.OPTIONS_SCREEN_HEADER)).toBeVisible();
26
     await expect(elementById(testIDs.OPTIONS_SCREEN_HEADER)).toBeVisible();
35
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
34
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
36
   });
35
   });
37
 
36
 
38
-  it(':ios: hides topBar onScroll down and shows it on scroll up', async () => {
37
+  it('hides topBar onScroll down and shows it on scroll up', async () => {
39
     await elementById(testIDs.PUSH_OPTIONS_BUTTON).tap();
38
     await elementById(testIDs.PUSH_OPTIONS_BUTTON).tap();
40
     await elementById(testIDs.SCROLLVIEW_SCREEN_BUTTON).tap();
39
     await elementById(testIDs.SCROLLVIEW_SCREEN_BUTTON).tap();
41
     await elementById(testIDs.TOGGLE_TOP_BAR_HIDE_ON_SCROLL).tap();
40
     await elementById(testIDs.TOGGLE_TOP_BAR_HIDE_ON_SCROLL).tap();
42
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
41
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
43
     await element(by.id(testIDs.SCROLLVIEW_ELEMENT)).swipe('up', 'slow');
42
     await element(by.id(testIDs.SCROLLVIEW_ELEMENT)).swipe('up', 'slow');
44
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeNotVisible();
43
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeNotVisible();
45
-    await element(by.id(testIDs.SCROLLVIEW_ELEMENT)).swipe('down', 'slow');
44
+    await element(by.id(testIDs.SCROLLVIEW_ELEMENT)).swipe('down', 'fast');
46
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
45
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
47
   });
46
   });
48
 
47
 
52
     await expect(element(by.text('TeSt'))).toBeVisible();
51
     await expect(element(by.text('TeSt'))).toBeVisible();
53
   });
52
   });
54
 
53
 
55
-  it(':ios: hide Tab Bar', async () => {
54
+  it('hide Tab Bar', async () => {
56
     await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
55
     await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
57
     await expect(elementById(testIDs.BOTTOM_TABS_ELEMENT)).toBeVisible();
56
     await expect(elementById(testIDs.BOTTOM_TABS_ELEMENT)).toBeVisible();
58
     await elementById(testIDs.HIDE_BOTTOM_TABS_BUTTON).tap();
57
     await elementById(testIDs.HIDE_BOTTOM_TABS_BUTTON).tap();
59
     await expect(elementById(testIDs.BOTTOM_TABS_ELEMENT)).toBeNotVisible();
58
     await expect(elementById(testIDs.BOTTOM_TABS_ELEMENT)).toBeNotVisible();
60
   });
59
   });
61
 
60
 
62
-  it(':ios: show Tab Bar', async () => {
61
+  it('show Tab Bar', async () => {
63
     await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
62
     await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
64
     await elementById(testIDs.HIDE_BOTTOM_TABS_BUTTON).tap();
63
     await elementById(testIDs.HIDE_BOTTOM_TABS_BUTTON).tap();
65
     await expect(elementById(testIDs.BOTTOM_TABS_ELEMENT)).toBeNotVisible();
64
     await expect(elementById(testIDs.BOTTOM_TABS_ELEMENT)).toBeNotVisible();
83
     await expect(elementById(testIDs.CENTERED_TEXT_HEADER)).toBeVisible();
82
     await expect(elementById(testIDs.CENTERED_TEXT_HEADER)).toBeVisible();
84
   });
83
   });
85
 
84
 
86
-  it(':ios: set right buttons', async () => {
85
+  it('set right buttons', async () => {
87
     await elementById(testIDs.PUSH_OPTIONS_BUTTON).tap();
86
     await elementById(testIDs.PUSH_OPTIONS_BUTTON).tap();
88
     await expect(elementById('buttonOne')).toBeVisible();
87
     await expect(elementById('buttonOne')).toBeVisible();
89
     await elementById('buttonOne').tap();
88
     await elementById('buttonOne').tap();
126
 
125
 
127
   it('stack options should not override component options', async () => {
126
   it('stack options should not override component options', async () => {
128
     await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
127
     await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
129
-    await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeNotVisible();
130
-    await elementById(testIDs.SECOND_TAB_BAR_BUTTON).tap();
131
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
128
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
132
   });
129
   });
133
 });
130
 });

+ 5
- 0
e2e/Utils.js View File

1
+const exec = require('shell-utils').exec;
2
+
1
 module.exports = {
3
 module.exports = {
2
   elementByLabel: (label) => {
4
   elementByLabel: (label) => {
3
     return element(by.text(label));
5
     return element(by.text(label));
11
     } catch (err) {
13
     } catch (err) {
12
       return element(by.type('_UIModernBarButton').and(by.label('Back'))).tap();
14
       return element(by.type('_UIModernBarButton').and(by.label('Back'))).tap();
13
     }
15
     }
16
+  },
17
+  tapDeviceBackAndroid: () => {
18
+    exec.execSync('adb shell input keyevent 4');
14
   }
19
   }
15
 };
20
 };

+ 9
- 0
e2e/init.js View File

1
 const detox = require('detox');
1
 const detox = require('detox');
2
 const config = require('../package.json').detox;
2
 const config = require('../package.json').detox;
3
+const exec = require('shell-utils').exec;
3
 
4
 
4
 before(async () => {
5
 before(async () => {
5
   await detox.init(config, { launchApp: false });
6
   await detox.init(config, { launchApp: false });
7
+  disableAndroidEmulatorAnimations();
6
 });
8
 });
7
 
9
 
8
 after(async () => {
10
 after(async () => {
9
   await detox.cleanup();
11
   await detox.cleanup();
10
 });
12
 });
13
+
14
+// Temporary solution, #2809
15
+function disableAndroidEmulatorAnimations() {
16
+  exec.execAsync(`adb shell settings put global window_animation_scale 0.0`);
17
+  exec.execAsync(`adb shell settings put global transition_animation_scale 0.0`);
18
+  exec.execAsync(`adb shell settings put global animator_duration_scale 0.0`);
19
+}

+ 3
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabsOptions.java View File

64
         if (other.backgroundColor.hasValue()) {
64
         if (other.backgroundColor.hasValue()) {
65
 		    backgroundColor = other.backgroundColor;
65
 		    backgroundColor = other.backgroundColor;
66
         }
66
         }
67
+        if (other.testId.hasValue()) {
68
+            testId = other.testId;
69
+        }
67
     }
70
     }
68
 
71
 
69
     void mergeWithDefault(final BottomTabsOptions defaultOptions) {
72
     void mergeWithDefault(final BottomTabsOptions defaultOptions) {

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/Options.java View File

19
         Options result = new Options();
19
         Options result = new Options();
20
         if (json == null) return result;
20
         if (json == null) return result;
21
 
21
 
22
-        result.orientationOptions = OrientationOptions.parse(json.optJSONArray("orientation"));
22
+        result.orientationOptions = OrientationOptions.parse(json);
23
         result.topBarOptions = TopBarOptions.parse(typefaceManager, json.optJSONObject("topBar"));
23
         result.topBarOptions = TopBarOptions.parse(typefaceManager, json.optJSONObject("topBar"));
24
         result.topTabsOptions = TopTabsOptions.parse(json.optJSONObject("topTabs"));
24
         result.topTabsOptions = TopTabsOptions.parse(json.optJSONObject("topTabs"));
25
         result.topTabOptions = TopTabOptions.parse(typefaceManager, json.optJSONObject("topTab"));
25
         result.topTabOptions = TopTabOptions.parse(typefaceManager, json.optJSONObject("topTab"));

+ 26
- 15
lib/android/app/src/main/java/com/reactnativenavigation/parse/OrientationOptions.java View File

3
 import com.reactnativenavigation.parse.params.Orientation;
3
 import com.reactnativenavigation.parse.params.Orientation;
4
 
4
 
5
 import org.json.JSONArray;
5
 import org.json.JSONArray;
6
+import org.json.JSONObject;
6
 
7
 
7
 import java.util.ArrayList;
8
 import java.util.ArrayList;
8
 import java.util.Arrays;
9
 import java.util.Arrays;
9
 import java.util.List;
10
 import java.util.List;
10
 
11
 
11
 public class OrientationOptions {
12
 public class OrientationOptions {
12
-    Orientation[] orientations = new Orientation[0];
13
+    List<Orientation> orientations = new ArrayList<>();
13
 
14
 
14
-    public static OrientationOptions parse(JSONArray orientations) {
15
+    public static OrientationOptions parse(JSONObject json) {
15
         OrientationOptions options = new OrientationOptions();
16
         OrientationOptions options = new OrientationOptions();
16
-        if (orientations == null) return options;
17
+        if (json == null) return options;
17
 
18
 
18
-        List<Orientation> parsed = new ArrayList<>();
19
-        for (int i = 0; i < orientations.length(); i++) {
20
-            Orientation o = Orientation.fromString(orientations.optString(i, "default"));
21
-            if (o != null) {
22
-                parsed.add(o);
19
+        JSONArray orientations = json.optJSONArray("orientation");
20
+        if (orientations == null) {
21
+            String orientation = json.optString("orientation", Orientation.Default.name);
22
+            options.orientations.add(Orientation.fromString(orientation));
23
+        } else {
24
+            List<Orientation> parsed = new ArrayList<>();
25
+            for (int i = 0; i < orientations.length(); i++) {
26
+                Orientation o = Orientation.fromString(orientations.optString(i, "default"));
27
+                if (o != null) {
28
+                    parsed.add(o);
29
+                }
23
             }
30
             }
31
+            options.orientations = parsed;
24
         }
32
         }
25
-        options.orientations = parsed.toArray(new Orientation[0]);
26
 
33
 
27
         return options;
34
         return options;
28
     }
35
     }
30
     public int getValue() {
37
     public int getValue() {
31
         if (!hasValue()) return Orientation.Default.orientationCode;
38
         if (!hasValue()) return Orientation.Default.orientationCode;
32
 
39
 
33
-        int result = 0;
34
-        for (Orientation orientation : orientations) {
35
-            result |= orientation.orientationCode;
40
+        switch (orientations.get(0)) {
41
+            case Landscape:
42
+                return orientations.contains(Orientation.Portrait) ? Orientation.PortraitLandscape.orientationCode : Orientation.Landscape.orientationCode;
43
+            case Portrait:
44
+                return orientations.contains(Orientation.Landscape) ? Orientation.PortraitLandscape.orientationCode : Orientation.Portrait.orientationCode;
45
+            default:
46
+            case Default:
47
+                return Orientation.Default.orientationCode;
36
         }
48
         }
37
-        return result;
38
     }
49
     }
39
 
50
 
40
     public void mergeWith(OrientationOptions other) {
51
     public void mergeWith(OrientationOptions other) {
42
     }
53
     }
43
 
54
 
44
     private boolean hasValue() {
55
     private boolean hasValue() {
45
-        return orientations.length > 0;
56
+        return !orientations.isEmpty();
46
     }
57
     }
47
 
58
 
48
     public void mergeWithDefault(OrientationOptions defaultOptions) {
59
     public void mergeWithDefault(OrientationOptions defaultOptions) {
51
 
62
 
52
     @Override
63
     @Override
53
     public String toString() {
64
     public String toString() {
54
-        return hasValue() ? Arrays.toString(orientations) : Orientation.Default.toString();
65
+        return hasValue() ? Arrays.toString(orientations.toArray(new Orientation[0])) : Orientation.Default.toString();
55
     }
66
     }
56
 }
67
 }

+ 2
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Orientation.java View File

7
 public enum Orientation {
7
 public enum Orientation {
8
     Portrait("portrait", Configuration.ORIENTATION_PORTRAIT, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT),
8
     Portrait("portrait", Configuration.ORIENTATION_PORTRAIT, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT),
9
     Landscape("landscape", Configuration.ORIENTATION_LANDSCAPE, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE),
9
     Landscape("landscape", Configuration.ORIENTATION_LANDSCAPE, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE),
10
-    Default("default", Configuration.ORIENTATION_UNDEFINED, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
10
+    Default("default", Configuration.ORIENTATION_UNDEFINED, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED),
11
+    PortraitLandscape("sensor", Configuration.ORIENTATION_UNDEFINED, ActivityInfo.SCREEN_ORIENTATION_USER);
11
 
12
 
12
     public String name;
13
     public String name;
13
     public int configurationCode;
14
     public int configurationCode;

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/views/TitleBarButton.java View File

158
             ActionMenuView buttonsLayout = ViewUtils.findChildByClass(toolbar, ActionMenuView.class);
158
             ActionMenuView buttonsLayout = ViewUtils.findChildByClass(toolbar, ActionMenuView.class);
159
             List<TextView> buttons = ViewUtils.findChildrenByClass(buttonsLayout, TextView.class);
159
             List<TextView> buttons = ViewUtils.findChildrenByClass(buttonsLayout, TextView.class);
160
             for (TextView view : buttons) {
160
             for (TextView view : buttons) {
161
-                if (button.title.hasValue() && button.title.get().equals(view.getText())) {
161
+                if (button.title.hasValue() && button.title.get().equals(view.getText().toString())) {
162
                     view.setTag(testId.get());
162
                     view.setTag(testId.get());
163
                 } else if (button.icon.hasValue() && ArrayUtils.contains(view.getCompoundDrawables(), icon)) {
163
                 } else if (button.icon.hasValue() && ArrayUtils.contains(view.getCompoundDrawables(), icon)) {
164
                     view.setTag(testId.get());
164
                     view.setTag(testId.get());

+ 32
- 6
lib/android/app/src/test/java/com/reactnativenavigation/parse/OrientationOptionsTest.java View File

4
 import com.reactnativenavigation.parse.params.Orientation;
4
 import com.reactnativenavigation.parse.params.Orientation;
5
 
5
 
6
 import org.json.JSONArray;
6
 import org.json.JSONArray;
7
+import org.json.JSONException;
8
+import org.json.JSONObject;
7
 import org.junit.Test;
9
 import org.junit.Test;
8
 
10
 
9
 import java.util.Arrays;
11
 import java.util.Arrays;
26
     @Test
28
     @Test
27
     public void parseOrientations() throws Exception {
29
     public void parseOrientations() throws Exception {
28
         OrientationOptions options = OrientationOptions.parse(create("default", "landscape", "portrait"));
30
         OrientationOptions options = OrientationOptions.parse(create("default", "landscape", "portrait"));
29
-        assertThat(options.orientations[0]).isEqualTo(Orientation.Default);
30
-        assertThat(options.orientations[1]).isEqualTo(Orientation.Landscape);
31
-        assertThat(options.orientations[2]).isEqualTo(Orientation.Portrait);
31
+        assertThat(options.orientations.get(0)).isEqualTo(Orientation.Default);
32
+        assertThat(options.orientations.get(1)).isEqualTo(Orientation.Landscape);
33
+        assertThat(options.orientations.get(2)).isEqualTo(Orientation.Portrait);
34
+    }
35
+
36
+    @Test
37
+    public void parseSingleOrientation() throws Exception {
38
+        OrientationOptions options = OrientationOptions.parse(create("landscape"));
39
+        assertThat(options.orientations.get(0)).isEqualTo(Orientation.Landscape);
40
+    }
41
+
42
+    @Test
43
+    public void landscapePortrait_regardedAsUserOrientation() throws Exception {
44
+        OrientationOptions options = OrientationOptions.parse(create("landscape", "portrait"));
45
+        assertThat(options.getValue()).isEqualTo(Orientation.PortraitLandscape.orientationCode);
46
+    }
47
+
48
+    @Test
49
+    public void portraitLandscape_regardedAsUserOrientation() throws Exception {
50
+        OrientationOptions options = OrientationOptions.parse(create("portrait", "landscape"));
51
+        assertThat(options.getValue()).isEqualTo(Orientation.PortraitLandscape.orientationCode);
32
     }
52
     }
33
 
53
 
34
     @Test
54
     @Test
35
     public void unsupportedOrientationsAreIgnored() throws Exception {
55
     public void unsupportedOrientationsAreIgnored() throws Exception {
36
         OrientationOptions options = OrientationOptions.parse(create("default", "autoRotate"));
56
         OrientationOptions options = OrientationOptions.parse(create("default", "autoRotate"));
37
         assertThat(options.orientations).hasSize(1);
57
         assertThat(options.orientations).hasSize(1);
38
-        assertThat(options.orientations[0]).isEqualTo(Orientation.Default);
58
+        assertThat(options.orientations.get(0)).isEqualTo(Orientation.Default);
39
     }
59
     }
40
 
60
 
41
     @Test
61
     @Test
44
         assertThat(options.getValue()).isEqualTo(Orientation.Default.orientationCode);
64
         assertThat(options.getValue()).isEqualTo(Orientation.Default.orientationCode);
45
     }
65
     }
46
 
66
 
47
-    private JSONArray create(String... orientations) {
48
-        return new JSONArray(Arrays.asList(orientations));
67
+    private JSONObject create(String... orientations) {
68
+        JSONObject orientation = new JSONObject();
69
+        try {
70
+            orientation.putOpt("orientation", orientations.length > 1 ? new JSONArray(Arrays.asList(orientations)) : orientations[0]);
71
+        } catch (JSONException e) {
72
+            throw new RuntimeException("Unable to create orientation object");
73
+        }
74
+        return orientation;
49
     }
75
     }
50
 }
76
 }

+ 18
- 5
lib/ios/RNNNavigationOptions.m View File

41
 	for (id key in otherOptions) {
41
 	for (id key in otherOptions) {
42
 		if ([self hasProperty:key]) {
42
 		if ([self hasProperty:key]) {
43
 			if ([[self valueForKey:key] isKindOfClass:[RNNOptions class]]) {
43
 			if ([[self valueForKey:key] isKindOfClass:[RNNOptions class]]) {
44
-			RNNOptions* options = [self valueForKey:key];
45
-			[options mergeWith:[otherOptions objectForKey:key]];
46
-		} else {
47
-			[self setValue:[otherOptions objectForKey:key] forKey:key];
48
-		} 		
44
+				RNNOptions* options = [self valueForKey:key];
45
+				[options mergeWith:[otherOptions objectForKey:key]];
46
+			} else {
47
+				[self setValue:[otherOptions objectForKey:key] forKey:key];
48
+			}
49
+		}
50
+	}
51
+}
52
+
53
+-(void)mergeIfEmptyWith:(NSDictionary *)otherOptions {
54
+	for (id key in otherOptions) {
55
+		if ([self hasProperty:key]) {
56
+			if ([[self valueForKey:key] isKindOfClass:[RNNOptions class]]) {
57
+				RNNOptions* options = [self valueForKey:key];
58
+				[options mergeIfEmptyWith:[otherOptions objectForKey:key]];
59
+			} else if (![self valueForKey:key]) {
60
+				[self setValue:[otherOptions objectForKey:key] forKey:key];
61
+			}
49
 		}
62
 		}
50
 	}
63
 	}
51
 }
64
 }

+ 1
- 0
lib/ios/RNNOptions.h View File

16
 
16
 
17
 - (instancetype)initWithDict:(NSDictionary*)dict;
17
 - (instancetype)initWithDict:(NSDictionary*)dict;
18
 - (void)mergeWith:(NSDictionary*)otherOptions;
18
 - (void)mergeWith:(NSDictionary*)otherOptions;
19
+- (void)mergeIfEmptyWith:(NSDictionary*)otherOptions;
19
 - (void)applyOn:(UIViewController *)viewController defaultOptions:(RNNOptions*)defaultOptions;
20
 - (void)applyOn:(UIViewController *)viewController defaultOptions:(RNNOptions*)defaultOptions;
20
 - (BOOL)hasProperty:(NSString*)propName;
21
 - (BOOL)hasProperty:(NSString*)propName;
21
 
22
 

+ 8
- 0
lib/ios/RNNOptions.m View File

22
 	}
22
 	}
23
 }
23
 }
24
 
24
 
25
+-(void)mergeIfEmptyWith:(NSDictionary *)otherOptions {
26
+	for (id key in otherOptions) {
27
+		if ([self hasProperty:key] && ![self valueForKey:key]) {
28
+			[self setValue:[otherOptions objectForKey:key] forKey:key];
29
+		}
30
+	}
31
+}
32
+
25
 - (BOOL)hasProperty:(NSString*)propName {
33
 - (BOOL)hasProperty:(NSString*)propName {
26
 	return [self respondsToSelector:NSSelectorFromString(propName)];
34
 	return [self respondsToSelector:NSSelectorFromString(propName)];
27
 }
35
 }

+ 1
- 1
lib/ios/RNNRootViewController.m View File

69
 }
69
 }
70
 
70
 
71
 - (void)mergeOptions:(NSDictionary *)options {
71
 - (void)mergeOptions:(NSDictionary *)options {
72
-	[self.options mergeWith:options];
72
+	[self.options mergeIfEmptyWith:options];
73
 }
73
 }
74
 
74
 
75
 - (void)setCustomNavigationTitleView {
75
 - (void)setCustomNavigationTitleView {

+ 4
- 1
playground/src/screens/WelcomeScreen.js View File

69
                     },
69
                     },
70
                     options: {
70
                     options: {
71
                       topBar: {
71
                       topBar: {
72
-                        visible: (Platform.OS === 'android') ? true : false,
72
+                        visible: true,
73
                         title: 'React Native Navigation!'
73
                         title: 'React Native Navigation!'
74
                       }
74
                       }
75
                     }
75
                     }
81
                   title: 'Tab 1',
81
                   title: 'Tab 1',
82
                   icon: require('../images/one.png'),
82
                   icon: require('../images/one.png'),
83
                   testID: testIDs.FIRST_TAB_BAR_BUTTON
83
                   testID: testIDs.FIRST_TAB_BAR_BUTTON
84
+                },
85
+                topBar: {
86
+                  visible: false
84
                 }
87
                 }
85
               }
88
               }
86
             }
89
             }