소스 검색

Fix ModalStack.findControllerById

* Extract Modal to separate class
* Test ModalStack
Guy Carmeli 6 년 전
부모
커밋
d55795601b

+ 12
- 56
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ModalStack.java 파일 보기

@@ -1,27 +1,26 @@
1 1
 package com.reactnativenavigation.viewcontrollers;
2 2
 
3
-import android.app.Dialog;
4
-import android.content.DialogInterface;
5 3
 import android.support.annotation.Nullable;
6
-import android.view.KeyEvent;
7
-import android.view.View;
8 4
 
9 5
 import com.facebook.react.bridge.Promise;
10
-import com.reactnativenavigation.R;
6
+import com.reactnativenavigation.viewcontrollers.modal.Modal;
7
+import com.reactnativenavigation.viewcontrollers.modal.ModalCreator;
11 8
 
12 9
 import java.util.ArrayList;
13 10
 import java.util.List;
14 11
 
15
-import static android.view.View.MeasureSpec.EXACTLY;
16
-import static android.view.View.MeasureSpec.makeMeasureSpec;
17
-
18 12
 public class ModalStack {
19 13
 
20 14
 	private List<Modal> modals = new ArrayList<>();
15
+    private ModalCreator creator;
16
+
17
+    public ModalStack(ModalCreator creator) {
18
+        this.creator = creator;
19
+    }
21 20
 
22
-	public void showModal(final ViewController viewController, Promise promise) {
23
-		Modal modal = new Modal(viewController);
24
-		modals.add(modal);
21
+    public void showModal(final ViewController viewController, Promise promise) {
22
+        Modal modal = creator.create(viewController);
23
+        modals.add(modal);
25 24
 		modal.show();
26 25
 		if (promise != null) {
27 26
 			promise.resolve(viewController.getId());
@@ -52,7 +51,7 @@ public class ModalStack {
52 51
 	}
53 52
 
54 53
 	@Nullable
55
-	private Modal findModalByComponentId(String componentId) {
54
+	public Modal findModalByComponentId(String componentId) {
56 55
 		for (Modal modal : modals) {
57 56
 			if (modal.containsDeepComponentId(componentId)) {
58 57
 				return modal;
@@ -64,49 +63,6 @@ public class ModalStack {
64 63
 	@Nullable
65 64
     ViewController findControllerById(String id) {
66 65
         Modal modal = findModalByComponentId(id);
67
-        return modal != null ? modal.viewController : null;
68
-    }
69
-
70
-    private static class Modal implements DialogInterface.OnKeyListener {
71
-		public final ViewController viewController;
72
-		private final Dialog dialog;
73
-
74
-		Modal(final ViewController viewController) {
75
-			this.viewController = viewController;
76
-			dialog = new Dialog(viewController.getActivity(), R.style.Modal);
77
-			dialog.setOnKeyListener(this);
78
-		}
79
-
80
-		void show() {
81
-			preMeasureView();
82
-			dialog.setContentView(viewController.getView());
83
-			dialog.show();
84
-		}
85
-
86
-		void dismiss() {
87
-			dialog.dismiss();
88
-		}
89
-
90
-		boolean containsDeepComponentId(String componentId) {
91
-			return viewController.findControllerById(componentId) != null;
92
-		}
93
-
94
-		private void preMeasureView() {
95
-			View decorView = viewController.getActivity().getWindow().getDecorView();
96
-			viewController.getView().measure(makeMeasureSpec(decorView.getMeasuredWidth(), EXACTLY), makeMeasureSpec(decorView.getMeasuredHeight(), EXACTLY));
97
-		}
98
-
99
-        @Override
100
-        public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
101
-            if (keyCode == KeyEvent.KEYCODE_BACK) {
102
-                if (event.getAction() == KeyEvent.ACTION_UP) {
103
-                    if (viewController.handleBack()) {
104
-                        return true;
105
-                    }
106
-                    dialog.dismiss();
107
-                }
108
-            }
109
-            return false;
110
-        }
66
+        return modal != null ? modal.viewController.findControllerById(id) : null;
111 67
     }
112 68
 }

+ 55
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/Modal.java 파일 보기

@@ -0,0 +1,55 @@
1
+package com.reactnativenavigation.viewcontrollers.modal;
2
+
3
+import android.app.Dialog;
4
+import android.content.DialogInterface;
5
+import android.view.KeyEvent;
6
+import android.view.View;
7
+
8
+import com.reactnativenavigation.R;
9
+import com.reactnativenavigation.viewcontrollers.ViewController;
10
+
11
+import static android.view.View.MeasureSpec.EXACTLY;
12
+import static android.view.View.MeasureSpec.makeMeasureSpec;
13
+
14
+public class Modal implements DialogInterface.OnKeyListener {
15
+    public final ViewController viewController;
16
+    private final Dialog dialog;
17
+
18
+    public Modal(final ViewController viewController) {
19
+        this.viewController = viewController;
20
+        dialog = new Dialog(viewController.getActivity(), R.style.Modal);
21
+        dialog.setOnKeyListener(this);
22
+    }
23
+
24
+    public void show() {
25
+        preMeasureView();
26
+        dialog.setContentView(viewController.getView());
27
+        dialog.show();
28
+    }
29
+
30
+    public void dismiss() {
31
+        dialog.dismiss();
32
+    }
33
+
34
+    public boolean containsDeepComponentId(String componentId) {
35
+        return viewController.findControllerById(componentId) != null;
36
+    }
37
+
38
+    private void preMeasureView() {
39
+        View decorView = viewController.getActivity().getWindow().getDecorView();
40
+        viewController.getView().measure(makeMeasureSpec(decorView.getMeasuredWidth(), EXACTLY), makeMeasureSpec(decorView.getMeasuredHeight(), EXACTLY));
41
+    }
42
+
43
+    @Override
44
+    public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
45
+        if (keyCode == KeyEvent.KEYCODE_BACK) {
46
+            if (event.getAction() == KeyEvent.ACTION_UP) {
47
+                if (viewController.handleBack()) {
48
+                    return true;
49
+                }
50
+                dialog.dismiss();
51
+            }
52
+        }
53
+        return false;
54
+    }
55
+}

+ 9
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/ModalCreator.java 파일 보기

@@ -0,0 +1,9 @@
1
+package com.reactnativenavigation.viewcontrollers.modal;
2
+
3
+import com.reactnativenavigation.viewcontrollers.ViewController;
4
+
5
+public class ModalCreator {
6
+    public Modal create(ViewController viewController) {
7
+        return new Modal(viewController);
8
+    }
9
+}

+ 14
- 0
lib/android/app/src/test/java/com/reactnativenavigation/mocks/ModalCreatorMock.java 파일 보기

@@ -0,0 +1,14 @@
1
+package com.reactnativenavigation.mocks;
2
+
3
+import com.reactnativenavigation.viewcontrollers.ViewController;
4
+import com.reactnativenavigation.viewcontrollers.modal.Modal;
5
+import com.reactnativenavigation.viewcontrollers.modal.ModalCreator;
6
+
7
+import static org.mockito.Mockito.spy;
8
+
9
+public class ModalCreatorMock extends ModalCreator {
10
+    @Override
11
+    public Modal create(ViewController viewController) {
12
+        return spy(new Modal(viewController));
13
+    }
14
+}

+ 57
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ModalStackTest.java 파일 보기

@@ -0,0 +1,57 @@
1
+package com.reactnativenavigation.viewcontrollers;
2
+
3
+import com.reactnativenavigation.BaseTest;
4
+import com.reactnativenavigation.mocks.MockPromise;
5
+import com.reactnativenavigation.mocks.ModalCreatorMock;
6
+import com.reactnativenavigation.mocks.SimpleViewController;
7
+import com.reactnativenavigation.parse.Options;
8
+import com.reactnativenavigation.viewcontrollers.modal.Modal;
9
+
10
+import org.junit.Test;
11
+
12
+import javax.annotation.Nullable;
13
+
14
+import static org.assertj.core.api.Assertions.assertThat;
15
+import static org.mockito.Mockito.spy;
16
+import static org.mockito.Mockito.times;
17
+import static org.mockito.Mockito.verify;
18
+
19
+public class ModalStackTest extends BaseTest {
20
+    private static final String CONTROLLER_ID = "simpleController";
21
+    private ModalStack uut;
22
+    private SimpleViewController viewController;
23
+
24
+    @Override
25
+    public void beforeEach() {
26
+        uut = spy(new ModalStack(new ModalCreatorMock()));
27
+        viewController = new SimpleViewController(newActivity(), CONTROLLER_ID, new Options());
28
+    }
29
+
30
+    @Test
31
+    public void modalRefIsSaved() throws Exception {
32
+        uut.showModal(viewController, new MockPromise());
33
+        assertThat(findModal()).isNotNull();
34
+    }
35
+
36
+    @Test
37
+    public void modalIsShown() throws Exception {
38
+        uut.showModal(viewController, new MockPromise() {
39
+            @Override
40
+            public void resolve(@Nullable Object value) {
41
+                verify(findModal(), times(1)).show();
42
+            }
43
+        });
44
+    }
45
+
46
+    @Test
47
+    public void modalIsDismissed() throws Exception {
48
+        uut.showModal(viewController, new MockPromise());
49
+        assertThat(findModal()).isNotNull();
50
+        uut.dismissModal(CONTROLLER_ID, new MockPromise());
51
+        assertThat(findModal()).isNull();
52
+    }
53
+
54
+    private Modal findModal() {
55
+        return uut.findModalByComponentId("simpleController");
56
+    }
57
+}

+ 25
- 2
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/NavigatorTest.java 파일 보기

@@ -29,7 +29,7 @@ import static org.mockito.Mockito.when;
29 29
 public class NavigatorTest extends BaseTest {
30 30
     private Activity activity;
31 31
     private Navigator uut;
32
-    private ParentController parentController;
32
+    private StackController parentController;
33 33
     private SimpleViewController child1;
34 34
     private ViewController child2;
35 35
     private ViewController child3;
@@ -44,7 +44,7 @@ public class NavigatorTest extends BaseTest {
44 44
         imageLoaderMock = ImageLoaderMock.mock();
45 45
         activity = newActivity();
46 46
         uut = new Navigator(activity);
47
-        parentController = new StackController(activity, "stack", new Options());
47
+        parentController = spy(new StackController(activity, "stack", new Options()));
48 48
         parentController.ensureViewIsCreated();
49 49
         child1 = new SimpleViewController(activity, "child1", tabOptions);
50 50
         child2 = new SimpleViewController(activity, "child2", tabOptions);
@@ -312,4 +312,27 @@ public class NavigatorTest extends BaseTest {
312 312
         uut.push(stackController.getId(), child2, new MockPromise());
313 313
         assertIsChildById(stackController.getView(), child2.getView());
314 314
     }
315
+
316
+    @Test
317
+    public void pushedStackCanBePopped() throws Exception {
318
+        StackController parent = new StackController(activity, "someStack", new Options());
319
+        parent.ensureViewIsCreated();
320
+        uut.setRoot(parent, new MockPromise());
321
+        parent.push(parentController, new MockPromise());
322
+
323
+        parentController.push(child1, new MockPromise());
324
+        parentController.push(child2, new MockPromise());
325
+        assertThat(parentController.getChildControllers().size()).isEqualTo(2);
326
+        child1.ensureViewIsCreated();
327
+        child2.ensureViewIsCreated();
328
+
329
+        MockPromise promise = new MockPromise() {
330
+            @Override
331
+            public void resolve(@Nullable Object value) {
332
+                assertThat(parentController.getChildControllers().size()).isEqualTo(1);
333
+            }
334
+        };
335
+        uut.popSpecific("child2", promise);
336
+        verify(parentController, times(1)).popSpecific(child2, promise);
337
+    }
315 338
 }

+ 10
- 7
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackControllerTest.java 파일 보기

@@ -1,19 +1,22 @@
1 1
 package com.reactnativenavigation.viewcontrollers;
2 2
 
3
-import android.app.*;
3
+import android.app.Activity;
4 4
 import android.view.View;
5 5
 
6
-import com.reactnativenavigation.*;
7
-import com.reactnativenavigation.mocks.*;
6
+import com.reactnativenavigation.BaseTest;
7
+import com.reactnativenavigation.mocks.MockPromise;
8
+import com.reactnativenavigation.mocks.SimpleViewController;
8 9
 import com.reactnativenavigation.parse.Options;
9 10
 
10
-import org.assertj.core.api.iterable.*;
11
-import org.junit.*;
11
+import org.assertj.core.api.iterable.Extractor;
12
+import org.junit.Test;
12 13
 
13 14
 import javax.annotation.Nullable;
14 15
 
15
-import static org.assertj.core.api.Java6Assertions.*;
16
-import static org.mockito.Mockito.*;
16
+import static org.assertj.core.api.Java6Assertions.assertThat;
17
+import static org.mockito.Mockito.spy;
18
+import static org.mockito.Mockito.times;
19
+import static org.mockito.Mockito.verify;
17 20
 
18 21
 public class StackControllerTest extends BaseTest {
19 22
 

+ 6
- 0
playground/src/screens/TextScreen.js 파일 보기

@@ -20,6 +20,7 @@ class TextScreen extends Component {
20 20
   constructor(props) {
21 21
     super(props);
22 22
     globalFirstComponentID = (props.text === 'This is tab 1') ? props.componentId : globalFirstComponentID;
23
+    this.onClickPop = this.onClickPop.bind(this);
23 24
   }
24 25
 
25 26
   render() {
@@ -35,10 +36,15 @@ class TextScreen extends Component {
35 36
         <Button title='Show Tab Bar' testID={testIDs.SHOW_BOTTOM_TABS_BUTTON} onPress={() => this.hideTabBar(false)} />
36 37
         <Button title='Show Left Side Menu' testID={testIDs.SHOW_LEFT_SIDE_MENU_BUTTON} onPress={() => this.showSideMenu('left')} />
37 38
         <Button title='Show Right Side Menu' testID={testIDs.SHOW_RIGHT_SIDE_MENU_BUTTON} onPress={() => this.showSideMenu('right')} />
39
+        <Button title='Pop' testID={testIDs.POP_BUTTON} onPress={this.onClickPop} />
38 40
       </View>
39 41
     );
40 42
   }
41 43
 
44
+  async onClickPop() {
45
+    await Navigation.pop(this.props.componentId);
46
+  }
47
+
42 48
   renderTextFromFunctionInProps() {
43 49
     if (!this.props.myFunction) {
44 50
       return undefined;