Browse Source

Destroy Overlay when it is dismissed

* Cover OverlayManager with unit tests
* Add e2e to test Overlay unmount
Guy Carmeli 6 years ago
parent
commit
ce806e3b1e

+ 8
- 0
e2e/StaticLifecycleEvents.test.js View File

15
     await expect(elementByLabel('componentDidAppear | navigation.playground.PushedScreen')).toBeVisible();
15
     await expect(elementByLabel('componentDidAppear | navigation.playground.PushedScreen')).toBeVisible();
16
     await expect(elementByLabel('componentDidDisappear | navigation.playground.WelcomeScreen')).toBeVisible();
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 View File

16
         listener.onSuccess(overlay.getId());
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
         } else {
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
         }
33
         }
33
         overlayRegistry.clear();
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 View File

196
     }
196
     }
197
 
197
 
198
     public void dismissOverlay(final String componentId, CommandListener listener) {
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
     @Nullable
202
     @Nullable

+ 63
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/overlay/OverlayManagerTest.java View File

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 View File

1
 const React = require('react');
1
 const React = require('react');
2
 const { Component } = require('react');
2
 const { Component } = require('react');
3
-const { View, Text } = require('react-native');
3
+const { View, Text, TouchableOpacity } = require('react-native');
4
 const { Navigation } = require('react-native-navigation');
4
 const { Navigation } = require('react-native-navigation');
5
+const testIDs = require('../testIDs');
5
 
6
 
6
 class StaticLifecycleOverlay extends Component {
7
 class StaticLifecycleOverlay extends Component {
7
   constructor(props) {
8
   constructor(props) {
10
       text: 'nothing yet',
11
       text: 'nothing yet',
11
       events: []
12
       events: []
12
     };
13
     };
13
-    Navigation.events().registerComponentDidAppearListener((componentId, componentName) => {
14
+    this.listeners = [];
15
+    this.listeners.push(Navigation.events().registerComponentDidAppearListener((componentId, componentName) => {
14
       this.setState({
16
       this.setState({
15
         events: [...this.state.events, { event: 'componentDidAppear', componentId, componentName }]
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
       this.setState({
21
       this.setState({
20
         events: [...this.state.events, { event: 'componentDidDisappear', componentId, componentName }]
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
       // console.log('RNN', `name: ${JSON.stringify(name)}`);
26
       // console.log('RNN', `name: ${JSON.stringify(name)}`);
25
       // console.log('RNN', `params: ${JSON.stringify(params)}`);
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
   render() {
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
           <Text style={styles.h2}>{`${event.event} | ${event.componentName}`}</Text>
41
           <Text style={styles.h2}>{`${event.event} | ${event.componentName}`}</Text>
34
         </View>
42
         </View>
35
       ));
43
       ));
39
         <View style={styles.events}>
47
         <View style={styles.events}>
40
           {events}
48
           {events}
41
         </View>
49
         </View>
50
+        {this.renderDismissButton()}
42
       </View>
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
 module.exports = StaticLifecycleOverlay;
67
 module.exports = StaticLifecycleOverlay;
47
 
68
 
55
     backgroundColor: '#c1d5e0ae',
76
     backgroundColor: '#c1d5e0ae',
56
     flexDirection: 'column'
77
     flexDirection: 'column'
57
   },
78
   },
79
+  dismissBtn: {
80
+    position: 'absolute',
81
+    width: 25,
82
+    height: 25,
83
+    backgroundColor: 'white',
84
+    justifyContent: 'center'
85
+  },
58
   events: {
86
   events: {
59
     flexDirection: 'column',
87
     flexDirection: 'column',
60
     marginHorizontal: 2
88
     marginHorizontal: 2