浏览代码

Destroy Overlay when it is dismissed

* Cover OverlayManager with unit tests
* Add e2e to test Overlay unmount
Guy Carmeli 6 年前
父节点
当前提交
ce806e3b1e

+ 8
- 0
e2e/StaticLifecycleEvents.test.js 查看文件

@@ -15,4 +15,12 @@ describe('static lifecycle events', () => {
15 15
     await expect(elementByLabel('componentDidAppear | navigation.playground.PushedScreen')).toBeVisible();
16 16
     await expect(elementByLabel('componentDidDisappear | navigation.playground.WelcomeScreen')).toBeVisible();
17 17
   });
18
+
19
+  it(':android: unmounts when dismissed', async () => {
20
+    await elementById(testIDs.PUSH_STATIC_LIFECYCLE_BUTTON).tap();
21
+    await expect(elementByLabel('Static Lifecycle Events Overlay')).toBeVisible();
22
+    await elementById(testIDs.DISMISS_BUTTON).tap();
23
+    await expect(elementByLabel('Overlay Unmounted')).toBeVisible();
24
+    await elementById(testIDs.OK_BUTTON).tap();
25
+  });
18 26
 });

+ 11
- 6
lib/android/app/src/main/java/com/reactnativenavigation/presentation/OverlayManager.java 查看文件

@@ -16,13 +16,14 @@ public class OverlayManager {
16 16
         listener.onSuccess(overlay.getId());
17 17
     }
18 18
 
19
-    public void dismiss(ViewGroup root, String componentId, CommandListener listener) {
20
-        ViewGroup overlay = overlayRegistry.get(componentId).getView();
21
-        if (overlay.getParent() == root) {
22
-            root.removeView(overlay);
23
-            listener.onSuccess(componentId);
19
+    public void dismiss(String componentId, CommandListener listener) {
20
+        ViewController overlay = overlayRegistry.get(componentId);
21
+        if (overlay == null) {
22
+            listener.onError("Could not dismiss Overlay. Overlay with id " + componentId + " was not found.");
24 23
         } else {
25
-            listener.onError("Overlay is not attached");
24
+            overlay.destroy();
25
+            overlayRegistry.remove(componentId);
26
+            listener.onSuccess(componentId);
26 27
         }
27 28
     }
28 29
 
@@ -32,4 +33,8 @@ public class OverlayManager {
32 33
         }
33 34
         overlayRegistry.clear();
34 35
     }
36
+
37
+    public int size() {
38
+        return overlayRegistry.size();
39
+    }
35 40
 }

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Navigator.java 查看文件

@@ -196,7 +196,7 @@ public class Navigator extends ParentController implements JsDevReloadHandler.Re
196 196
     }
197 197
 
198 198
     public void dismissOverlay(final String componentId, CommandListener listener) {
199
-        overlayManager.dismiss(getView(), componentId, listener);
199
+        overlayManager.dismiss(componentId, listener);
200 200
     }
201 201
 
202 202
     @Nullable

+ 63
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/overlay/OverlayManagerTest.java 查看文件

@@ -0,0 +1,63 @@
1
+package com.reactnativenavigation.viewcontrollers.overlay;
2
+
3
+import android.app.Activity;
4
+import android.widget.FrameLayout;
5
+
6
+import com.reactnativenavigation.BaseTest;
7
+import com.reactnativenavigation.mocks.SimpleViewController;
8
+import com.reactnativenavigation.parse.Options;
9
+import com.reactnativenavigation.presentation.OverlayManager;
10
+import com.reactnativenavigation.utils.CommandListener;
11
+import com.reactnativenavigation.utils.CommandListenerAdapter;
12
+
13
+import org.junit.Test;
14
+
15
+import static org.assertj.core.api.Java6Assertions.assertThat;
16
+import static org.mockito.ArgumentMatchers.any;
17
+import static org.mockito.Mockito.spy;
18
+import static org.mockito.Mockito.times;
19
+import static org.mockito.Mockito.verify;
20
+
21
+public class OverlayManagerTest extends BaseTest {
22
+    private static final String OVERLAY_ID_1 = "OVERLAY_1";
23
+    private OverlayManager uut;
24
+    private SimpleViewController overlay1;
25
+    private FrameLayout root;
26
+
27
+    @Override
28
+    public void beforeEach() {
29
+        Activity activity = newActivity();
30
+        root = new FrameLayout(activity);
31
+        root.layout(0, 0, 1000, 1000);
32
+        activity.setContentView(root);
33
+
34
+        overlay1 = spy(new SimpleViewController(activity, OVERLAY_ID_1, new Options()));
35
+        uut = new OverlayManager();
36
+    }
37
+
38
+    @Test
39
+    public void show() {
40
+        CommandListenerAdapter listener = spy(new CommandListenerAdapter());
41
+        uut.show(root, overlay1, listener);
42
+        verify(listener, times(1)).onSuccess(OVERLAY_ID_1);
43
+        assertThat(overlay1.getView().getParent()).isEqualTo(root);
44
+    }
45
+
46
+    @Test
47
+    public void dismiss() {
48
+        uut.show(root, overlay1, new CommandListenerAdapter());
49
+        assertThat(uut.size()).isOne();
50
+        CommandListener listener = spy(new CommandListenerAdapter());
51
+        uut.dismiss(overlay1.getId(), listener);
52
+        assertThat(uut.size()).isZero();
53
+        verify(listener, times(1)).onSuccess(OVERLAY_ID_1);
54
+        verify(overlay1, times(1)).destroy();
55
+    }
56
+
57
+    @Test
58
+    public void dismiss_rejectIfOverlayNotFound() {
59
+        CommandListener listener = spy(new CommandListenerAdapter());
60
+        uut.dismiss(overlay1.getId(), listener);
61
+        verify(listener, times(1)).onError(any());
62
+    }
63
+}

+ 37
- 9
playground/src/screens/StaticLifecycleOverlay.js 查看文件

@@ -1,7 +1,8 @@
1 1
 const React = require('react');
2 2
 const { Component } = require('react');
3
-const { View, Text } = require('react-native');
3
+const { View, Text, TouchableOpacity } = require('react-native');
4 4
 const { Navigation } = require('react-native-navigation');
5
+const testIDs = require('../testIDs');
5 6
 
6 7
 class StaticLifecycleOverlay extends Component {
7 8
   constructor(props) {
@@ -10,26 +11,33 @@ class StaticLifecycleOverlay extends Component {
10 11
       text: 'nothing yet',
11 12
       events: []
12 13
     };
13
-    Navigation.events().registerComponentDidAppearListener((componentId, componentName) => {
14
+    this.listeners = [];
15
+    this.listeners.push(Navigation.events().registerComponentDidAppearListener((componentId, componentName) => {
14 16
       this.setState({
15 17
         events: [...this.state.events, { event: 'componentDidAppear', componentId, componentName }]
16 18
       });
17
-    });
18
-    Navigation.events().registerComponentDidDisappearListener((componentId, componentName) => {
19
+    }));
20
+    this.listeners.push(Navigation.events().registerComponentDidDisappearListener((componentId, componentName) => {
19 21
       this.setState({
20 22
         events: [...this.state.events, { event: 'componentDidDisappear', componentId, componentName }]
21 23
       });
22
-    });
23
-    Navigation.events().registerCommandListener((name, params) => {
24
+    }));
25
+    this.listeners.push(Navigation.events().registerCommandListener((name, params) => {
24 26
       // console.log('RNN', `name: ${JSON.stringify(name)}`);
25 27
       // console.log('RNN', `params: ${JSON.stringify(params)}`);
26
-    });
28
+    }));
29
+  }
30
+
31
+  componentWillUnmount() {
32
+    this.listeners.forEach(listener => listener.remove());
33
+    this.listeners = [];
34
+    alert('Overlay Unmounted');
27 35
   }
28 36
 
29 37
   render() {
30
-    const events = this.state.events.map((event) =>
38
+    const events = this.state.events.map((event, idx) =>
31 39
       (
32
-        <View key={event.componentId}>
40
+        <View key={`${event.componentId}${idx}`}>
33 41
           <Text style={styles.h2}>{`${event.event} | ${event.componentName}`}</Text>
34 42
         </View>
35 43
       ));
@@ -39,9 +47,22 @@ class StaticLifecycleOverlay extends Component {
39 47
         <View style={styles.events}>
40 48
           {events}
41 49
         </View>
50
+        {this.renderDismissButton()}
42 51
       </View>
43 52
     );
44 53
   }
54
+
55
+  renderDismissButton = () => {
56
+  return (
57
+    <TouchableOpacity
58
+      style={styles.dismissBtn}
59
+      testID={testIDs.DISMISS_BUTTON}
60
+      onPress={() => Navigation.dismissOverlay(this.props.componentId)}
61
+    >
62
+      <Text style={{ color: 'red', alignSelf: 'center' }}>X</Text>
63
+    </TouchableOpacity>
64
+  )
65
+}
45 66
 }
46 67
 module.exports = StaticLifecycleOverlay;
47 68
 
@@ -55,6 +76,13 @@ const styles = {
55 76
     backgroundColor: '#c1d5e0ae',
56 77
     flexDirection: 'column'
57 78
   },
79
+  dismissBtn: {
80
+    position: 'absolute',
81
+    width: 25,
82
+    height: 25,
83
+    backgroundColor: 'white',
84
+    justifyContent: 'center'
85
+  },
58 86
   events: {
59 87
     flexDirection: 'column',
60 88
     marginHorizontal: 2