@truewatchtech/react-native-mobile 0.4.1 → 0.4.3-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/FTMobileReactNativeSDK.podspec +7 -5
  2. package/README.md +34 -0
  3. package/android/build.gradle +9 -2
  4. package/android/src/main/java/com/ft/sdk/reactnative/FTMobileImpl.java +45 -14
  5. package/android/src/main/java/com/ft/sdk/reactnative/FTRUMImpl.java +85 -2
  6. package/android/src/main/java/com/ft/sdk/reactnative/FTTraceImpl.java +5 -1
  7. package/android/src/main/java/com/ft/sdk/reactnative/JsLongTaskMonitor.java +281 -0
  8. package/android/src/main/java/com/ft/sdk/reactnative/utils/ReactNativeUtils.java +5 -12
  9. package/android/src/newarch/java/com/ft/sdk/reactnative/FTMobileModule.java +9 -5
  10. package/android/src/newarch/java/com/ft/sdk/reactnative/FTRUMModule.java +26 -1
  11. package/android/src/newarch/java/com/ft/sdk/reactnative/FTTraceModule.java +14 -0
  12. package/android/src/oldarch/java/com/ft/sdk/reactnative/FTMobileModule.java +2 -2
  13. package/android/src/oldarch/java/com/ft/sdk/reactnative/FTRUMModule.java +29 -1
  14. package/android/src/oldarch/java/com/ft/sdk/reactnative/FTTraceModule.java +12 -0
  15. package/android/src/rnpost70/java/com/ft/sdk/reactnative/ReactJsThreadExecutor.java +21 -0
  16. package/android/src/rnpre70/java/com/ft/sdk/reactnative/ReactJsThreadExecutor.java +37 -0
  17. package/android/src/test/java/com/ft/sdk/reactnative/FTRUMModuleTest.java +117 -0
  18. package/android/src/test/java/com/ft/sdk/reactnative/JsLongTaskMonitorTest.java +446 -0
  19. package/android/src/test/java/com/ft/sdk/reactnative/utils/ReactNativeUtilsTest.java +33 -0
  20. package/ios/FTJSLongTaskMonitor.h +51 -0
  21. package/ios/FTJSLongTaskMonitor.m +250 -0
  22. package/ios/FTMobileReactNative.mm +23 -16
  23. package/ios/FTReactNativeLog.mm +3 -4
  24. package/ios/FTReactNativeRUM.mm +103 -28
  25. package/ios/FTReactNativeResource.h +4 -0
  26. package/ios/FTReactNativeResource.m +58 -0
  27. package/ios/FTReactNativeTrace.mm +56 -13
  28. package/ios/FTReactNativeUtils.m +10 -15
  29. package/ios/FTReactNativeWebSocket.h +10 -0
  30. package/ios/FTReactNativeWebSocket.mm +201 -0
  31. package/ios/FTWebSocketMetadataStore.h +25 -0
  32. package/ios/FTWebSocketMetadataStore.m +622 -0
  33. package/ios/FTWebSocketResourceData.h +16 -0
  34. package/ios/FTWebSocketResourceData.m +75 -0
  35. package/lib/commonjs/ft_mobile_agent.js +36 -3
  36. package/lib/commonjs/ft_mobile_agent.js.map +1 -1
  37. package/lib/commonjs/ft_rum.js +46 -2
  38. package/lib/commonjs/ft_rum.js.map +1 -1
  39. package/lib/commonjs/ft_tracing.js +12 -3
  40. package/lib/commonjs/ft_tracing.js.map +1 -1
  41. package/lib/commonjs/index.js +14 -0
  42. package/lib/commonjs/index.js.map +1 -1
  43. package/lib/commonjs/rum/FTBabelInteractionTracking.js +72 -0
  44. package/lib/commonjs/rum/FTBabelInteractionTracking.js.map +1 -0
  45. package/lib/commonjs/rum/FTBabelUtils.js +87 -0
  46. package/lib/commonjs/rum/FTBabelUtils.js.map +1 -0
  47. package/lib/commonjs/rum/FTRumActionTracking.js +10 -39
  48. package/lib/commonjs/rum/FTRumActionTracking.js.map +1 -1
  49. package/lib/commonjs/rum/FTRumActionTrackingContext.js +50 -0
  50. package/lib/commonjs/rum/FTRumActionTrackingContext.js.map +1 -0
  51. package/lib/commonjs/rum/FTRumWebSocketNative.js +73 -0
  52. package/lib/commonjs/rum/FTRumWebSocketNative.js.map +1 -0
  53. package/lib/commonjs/rum/FTRumWebSocketTiming.js +55 -0
  54. package/lib/commonjs/rum/FTRumWebSocketTiming.js.map +1 -0
  55. package/lib/commonjs/rum/FTRumWebSocketTracking.js +510 -0
  56. package/lib/commonjs/rum/FTRumWebSocketTracking.js.map +1 -0
  57. package/lib/commonjs/rum/longTasksUtils.js +25 -0
  58. package/lib/commonjs/rum/longTasksUtils.js.map +1 -0
  59. package/lib/commonjs/specs/NativeFTMobileReactNative.js.map +1 -1
  60. package/lib/commonjs/specs/NativeFTReactNativeRUM.js.map +1 -1
  61. package/lib/commonjs/specs/NativeFTReactNativeTrace.js.map +1 -1
  62. package/lib/commonjs/specs/NativeFTReactNativeWebSocket.js +11 -0
  63. package/lib/commonjs/specs/NativeFTReactNativeWebSocket.js.map +1 -0
  64. package/lib/commonjs/version.js +1 -1
  65. package/lib/commonjs/version.js.map +1 -1
  66. package/lib/module/ft_mobile_agent.js +36 -3
  67. package/lib/module/ft_mobile_agent.js.map +1 -1
  68. package/lib/module/ft_rum.js +46 -2
  69. package/lib/module/ft_rum.js.map +1 -1
  70. package/lib/module/ft_tracing.js +12 -1
  71. package/lib/module/ft_tracing.js.map +1 -1
  72. package/lib/module/index.js +3 -1
  73. package/lib/module/index.js.map +1 -1
  74. package/lib/module/rum/FTBabelInteractionTracking.js +66 -0
  75. package/lib/module/rum/FTBabelInteractionTracking.js.map +1 -0
  76. package/lib/module/rum/FTBabelUtils.js +80 -0
  77. package/lib/module/rum/FTBabelUtils.js.map +1 -0
  78. package/lib/module/rum/FTRumActionTracking.js +10 -39
  79. package/lib/module/rum/FTRumActionTracking.js.map +1 -1
  80. package/lib/module/rum/FTRumActionTrackingContext.js +43 -0
  81. package/lib/module/rum/FTRumActionTrackingContext.js.map +1 -0
  82. package/lib/module/rum/FTRumWebSocketNative.js +62 -0
  83. package/lib/module/rum/FTRumWebSocketNative.js.map +1 -0
  84. package/lib/module/rum/FTRumWebSocketTiming.js +49 -0
  85. package/lib/module/rum/FTRumWebSocketTiming.js.map +1 -0
  86. package/lib/module/rum/FTRumWebSocketTracking.js +503 -0
  87. package/lib/module/rum/FTRumWebSocketTracking.js.map +1 -0
  88. package/lib/module/rum/longTasksUtils.js +18 -0
  89. package/lib/module/rum/longTasksUtils.js.map +1 -0
  90. package/lib/module/specs/NativeFTMobileReactNative.js.map +1 -1
  91. package/lib/module/specs/NativeFTReactNativeRUM.js.map +1 -1
  92. package/lib/module/specs/NativeFTReactNativeTrace.js.map +1 -1
  93. package/lib/module/specs/NativeFTReactNativeWebSocket.js +8 -0
  94. package/lib/module/specs/NativeFTReactNativeWebSocket.js.map +1 -0
  95. package/lib/module/version.js +1 -1
  96. package/lib/module/version.js.map +1 -1
  97. package/lib/typescript/ft_mobile_agent.d.ts +7 -0
  98. package/lib/typescript/ft_rum.d.ts +9 -0
  99. package/lib/typescript/index.d.ts +3 -1
  100. package/lib/typescript/rum/FTBabelInteractionTracking.d.ts +30 -0
  101. package/lib/typescript/rum/FTBabelUtils.d.ts +1 -0
  102. package/lib/typescript/rum/FTRumActionTracking.d.ts +0 -2
  103. package/lib/typescript/rum/FTRumActionTrackingContext.d.ts +11 -0
  104. package/lib/typescript/rum/FTRumWebSocketNative.d.ts +23 -0
  105. package/lib/typescript/rum/FTRumWebSocketTiming.d.ts +2 -0
  106. package/lib/typescript/rum/FTRumWebSocketTracking.d.ts +56 -0
  107. package/lib/typescript/rum/longTasksUtils.d.ts +8 -0
  108. package/lib/typescript/specs/NativeFTMobileReactNative.d.ts +14 -0
  109. package/lib/typescript/specs/NativeFTReactNativeRUM.d.ts +4 -0
  110. package/lib/typescript/specs/NativeFTReactNativeTrace.d.ts +12 -0
  111. package/lib/typescript/specs/NativeFTReactNativeWebSocket.d.ts +13 -0
  112. package/lib/typescript/version.d.ts +1 -1
  113. package/package.json +7 -6
  114. package/src/ft_mobile_agent.tsx +49 -6
  115. package/src/ft_rum.tsx +61 -2
  116. package/src/ft_tracing.tsx +15 -1
  117. package/src/index.tsx +4 -0
  118. package/src/rum/FTBabelInteractionTracking.ts +124 -0
  119. package/src/rum/FTBabelUtils.tsx +99 -0
  120. package/src/rum/FTRumActionTracking.tsx +14 -43
  121. package/src/rum/FTRumActionTrackingContext.ts +68 -0
  122. package/src/rum/FTRumWebSocketNative.ts +106 -0
  123. package/src/rum/FTRumWebSocketTiming.ts +50 -0
  124. package/src/rum/FTRumWebSocketTracking.ts +727 -0
  125. package/src/rum/longTasksUtils.ts +17 -0
  126. package/src/specs/NativeFTMobileReactNative.ts +17 -0
  127. package/src/specs/NativeFTReactNativeRUM.ts +4 -0
  128. package/src/specs/NativeFTReactNativeTrace.ts +12 -0
  129. package/src/specs/NativeFTReactNativeWebSocket.ts +33 -0
  130. package/src/version.ts +1 -1
@@ -81,23 +81,27 @@ public class FTMobileModule extends NativeFTMobileReactNativeSpec {
81
81
  }
82
82
 
83
83
  @ReactMethod
84
+ @Override
84
85
  public void updateRemoteConfig(Promise promise) {
85
- impl.updateRemoteConfig(promise);
86
+ impl.updateRemoteConfig(promise);
86
87
  }
87
88
 
88
89
  @ReactMethod
89
- public void updateRemoteConfigWithMiniUpdateInterval(int interval, @Nullable ReadableArray rules, Promise promise) {
90
- impl.updateRemoteConfigWithMiniUpdateInterval(interval, rules, promise);
90
+ @Override
91
+ public void updateRemoteConfigWithMiniUpdateInterval(double interval, @Nullable ReadableArray rules, Promise promise) {
92
+ impl.updateRemoteConfigWithMiniUpdateInterval((int) interval, rules, promise);
91
93
  }
92
94
 
93
95
  @ReactMethod
96
+ @Override
94
97
  public void addListener(String eventName) {
95
- impl.addListener(eventName);
98
+ // Required by NativeEventEmitter; Android does not need listener bookkeeping.
96
99
  }
97
100
 
98
101
  @ReactMethod
102
+ @Override
99
103
  public void removeListeners(double count) {
100
- impl.removeListeners(count);
104
+ // Required by NativeEventEmitter; Android does not need listener bookkeeping.
101
105
  }
102
106
 
103
107
  }
@@ -9,10 +9,11 @@ import com.facebook.react.bridge.ReadableMap;
9
9
 
10
10
  public class FTRUMModule extends NativeFTReactNativeRUMSpec {
11
11
 
12
- private final FTRUMImpl impl = new FTRUMImpl();
12
+ private final FTRUMImpl impl;
13
13
 
14
14
  public FTRUMModule(ReactApplicationContext reactContext) {
15
15
  super(reactContext);
16
+ impl = new FTRUMImpl(reactContext);
16
17
  }
17
18
 
18
19
  @Override
@@ -21,6 +22,30 @@ public class FTRUMModule extends NativeFTReactNativeRUMSpec {
21
22
  impl.setConfig(context, promise);
22
23
  }
23
24
 
25
+ @Override
26
+ @ReactMethod
27
+ public void stopLongTaskTracking(Promise promise) {
28
+ impl.stopLongTaskTracking(promise);
29
+ }
30
+
31
+ @Override
32
+ @ReactMethod(isBlockingSynchronousMethod = true)
33
+ public boolean setLongTaskContext(ReadableMap context) {
34
+ return impl.setLongTaskContext(context);
35
+ }
36
+
37
+ @Override
38
+ public void invalidate() {
39
+ impl.destroy();
40
+ super.invalidate();
41
+ }
42
+
43
+ @Override
44
+ public void onCatalystInstanceDestroy() {
45
+ impl.destroy();
46
+ super.onCatalystInstanceDestroy();
47
+ }
48
+
24
49
  @Override
25
50
  @ReactMethod
26
51
  public void startAction(String actionName, String actionType, ReadableMap map, Promise promise) {
@@ -4,6 +4,7 @@ import com.facebook.react.bridge.Promise;
4
4
  import com.facebook.react.bridge.ReactApplicationContext;
5
5
  import com.facebook.react.bridge.ReactMethod;
6
6
  import com.facebook.react.bridge.ReadableMap;
7
+ import com.facebook.react.bridge.WritableMap;
7
8
 
8
9
  public class FTTraceModule extends NativeFTReactNativeTraceSpec {
9
10
  private final FTTraceImpl impl = new FTTraceImpl();
@@ -29,4 +30,17 @@ public class FTTraceModule extends NativeFTReactNativeTraceSpec {
29
30
  public void getTraceHeaderFields(String url, String key, Promise promise) {
30
31
  impl.getTraceHeaderFields(url, key, promise);
31
32
  }
33
+
34
+ @Override
35
+ @ReactMethod(isBlockingSynchronousMethod = true)
36
+ public WritableMap getTraceHeaderFieldsSync(String url, String key) {
37
+ return impl.getTraceHeaderFieldsSync(url, key);
38
+ }
39
+
40
+ @Override
41
+ @ReactMethod
42
+ public void cancelWebSocketTrace(String key, Promise promise) {
43
+ // JS WebSocket observation is iOS-only; Android owns its native lifecycle.
44
+ promise.resolve(null);
45
+ }
32
46
  }
@@ -85,12 +85,12 @@ public class FTMobileModule extends ReactContextBaseJavaModule {
85
85
 
86
86
  @ReactMethod
87
87
  public void addListener(String eventName) {
88
- impl.addListener(eventName);
88
+ // Required by NativeEventEmitter; Android does not need listener bookkeeping.
89
89
  }
90
90
 
91
91
  @ReactMethod
92
92
  public void removeListeners(double count) {
93
- impl.removeListeners(count);
93
+ // Required by NativeEventEmitter; Android does not need listener bookkeeping.
94
94
  }
95
95
 
96
96
  @NonNull
@@ -7,10 +7,11 @@ import com.facebook.react.bridge.ReactMethod;
7
7
  import com.facebook.react.bridge.ReadableMap;
8
8
 
9
9
  public class FTRUMModule extends ReactContextBaseJavaModule {
10
- private final FTRUMImpl impl = new FTRUMImpl();
10
+ private final FTRUMImpl impl;
11
11
 
12
12
  public FTRUMModule(ReactApplicationContext reactContext) {
13
13
  super(reactContext);
14
+ impl = new FTRUMImpl(reactContext);
14
15
  }
15
16
 
16
17
  @Override
@@ -23,6 +24,28 @@ public class FTRUMModule extends ReactContextBaseJavaModule {
23
24
  impl.setConfig(context, promise);
24
25
  }
25
26
 
27
+ @ReactMethod
28
+ public void stopLongTaskTracking(Promise promise) {
29
+ impl.stopLongTaskTracking(promise);
30
+ }
31
+
32
+ @ReactMethod(isBlockingSynchronousMethod = true)
33
+ public boolean setLongTaskContext(ReadableMap context) {
34
+ return impl.setLongTaskContext(context);
35
+ }
36
+
37
+ @Override
38
+ public void invalidate() {
39
+ impl.destroy();
40
+ super.invalidate();
41
+ }
42
+
43
+ @Override
44
+ public void onCatalystInstanceDestroy() {
45
+ impl.destroy();
46
+ super.onCatalystInstanceDestroy();
47
+ }
48
+
26
49
  @ReactMethod
27
50
  public void startAction(String actionName, String actionType, ReadableMap map, Promise promise) {
28
51
  impl.startAction(actionName, actionType, map, promise);
@@ -67,4 +90,9 @@ public class FTRUMModule extends ReactContextBaseJavaModule {
67
90
  public void stopResource(String key, ReadableMap map, Promise promise) {
68
91
  impl.stopResource(key, map, promise);
69
92
  }
93
+
94
+ @ReactMethod
95
+ public void addResource(String key, ReadableMap resource, ReadableMap metrics, Promise promise) {
96
+ impl.addResource(key, resource, metrics, promise);
97
+ }
70
98
  }
@@ -5,6 +5,7 @@ import com.facebook.react.bridge.ReactApplicationContext;
5
5
  import com.facebook.react.bridge.ReactContextBaseJavaModule;
6
6
  import com.facebook.react.bridge.ReactMethod;
7
7
  import com.facebook.react.bridge.ReadableMap;
8
+ import com.facebook.react.bridge.WritableMap;
8
9
 
9
10
  public class FTTraceModule extends ReactContextBaseJavaModule {
10
11
  private final FTTraceImpl impl = new FTTraceImpl();
@@ -27,4 +28,15 @@ public class FTTraceModule extends ReactContextBaseJavaModule {
27
28
  public void getTraceHeaderFields(String url, String key, Promise promise) {
28
29
  impl.getTraceHeaderFields(url, key, promise);
29
30
  }
31
+
32
+ @ReactMethod(isBlockingSynchronousMethod = true)
33
+ public WritableMap getTraceHeaderFieldsSync(String url, String key) {
34
+ return impl.getTraceHeaderFieldsSync(url, key);
35
+ }
36
+
37
+ @ReactMethod
38
+ public void cancelWebSocketTrace(String key, Promise promise) {
39
+ // JS WebSocket observation is iOS-only; Android owns its native lifecycle.
40
+ promise.resolve(null);
41
+ }
30
42
  }
@@ -0,0 +1,21 @@
1
+ package com.ft.sdk.reactnative;
2
+
3
+ import com.facebook.react.bridge.ReactApplicationContext;
4
+
5
+ final class ReactJsThreadExecutor implements JsLongTaskMonitor.Executor {
6
+ private final ReactApplicationContext reactContext;
7
+
8
+ ReactJsThreadExecutor(ReactApplicationContext reactContext) {
9
+ this.reactContext = reactContext;
10
+ }
11
+
12
+ @Override
13
+ public boolean runOnJsThread(Runnable runnable) {
14
+ try {
15
+ return reactContext.runOnJSQueueThread(runnable);
16
+ } catch (AssertionError ignored) {
17
+ // ReactContext asserts if its JS queue has not been initialized yet.
18
+ return false;
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,37 @@
1
+ package com.ft.sdk.reactnative;
2
+
3
+ import android.os.Handler;
4
+ import android.os.Looper;
5
+
6
+ import com.facebook.react.bridge.ReactApplicationContext;
7
+ import com.facebook.react.bridge.queue.MessageQueueThread;
8
+ import com.facebook.react.bridge.queue.MessageQueueThreadImpl;
9
+ import com.facebook.react.bridge.queue.ReactQueueConfiguration;
10
+
11
+ final class ReactJsThreadExecutor implements JsLongTaskMonitor.Executor {
12
+ private final ReactApplicationContext reactContext;
13
+
14
+ ReactJsThreadExecutor(ReactApplicationContext reactContext) {
15
+ this.reactContext = reactContext;
16
+ }
17
+
18
+ @Override
19
+ public boolean runOnJsThread(Runnable runnable) {
20
+ if (!reactContext.hasActiveCatalystInstance()) {
21
+ return false;
22
+ }
23
+ ReactQueueConfiguration configuration =
24
+ reactContext.getCatalystInstance().getReactQueueConfiguration();
25
+ if (configuration == null) {
26
+ return false;
27
+ }
28
+ MessageQueueThread queue = configuration.getJSQueueThread();
29
+ if (!(queue instanceof MessageQueueThreadImpl)) {
30
+ return false;
31
+ }
32
+ Looper looper = ((MessageQueueThreadImpl) queue).getLooper();
33
+ // Older RN queue APIs return void. Post SDK work to the same JS looper so
34
+ // a stopped queue can explicitly reject it, without waiting for a callback.
35
+ return looper != null && new Handler(looper).post(runnable);
36
+ }
37
+ }
@@ -0,0 +1,117 @@
1
+ package com.ft.sdk.reactnative;
2
+
3
+ import com.facebook.react.bridge.Promise;
4
+ import com.facebook.react.bridge.ReactApplicationContext;
5
+
6
+ import org.junit.Test;
7
+
8
+ import java.lang.reflect.Method;
9
+
10
+ import static org.junit.Assume.assumeTrue;
11
+ import static org.mockito.ArgumentMatchers.any;
12
+ import static org.mockito.Mockito.mock;
13
+ import static org.mockito.Mockito.times;
14
+ import static org.mockito.Mockito.verify;
15
+ import static org.mockito.Mockito.verifyNoMoreInteractions;
16
+ import static org.mockito.Mockito.when;
17
+ import static org.junit.Assert.assertFalse;
18
+ import static org.junit.Assert.assertTrue;
19
+
20
+ public class FTRUMModuleTest {
21
+ @Test
22
+ public void bridgeContextUpdatesDoNotStartOrReviveMonitor() {
23
+ ReactApplicationContext context = mock(ReactApplicationContext.class);
24
+ // Exercise the real module forwarding separately: only a snapshot is installed.
25
+ FTRUMModule module = new FTRUMModule(context);
26
+ assertTrue(module.setLongTaskContext(null));
27
+ module.invalidate();
28
+ assertFalse(module.setLongTaskContext(null));
29
+ }
30
+ @Test
31
+ public void shutdownResolvesWhenJSQueueIsUnavailable() {
32
+ ReactApplicationContext context = mock(ReactApplicationContext.class);
33
+ FTRUMModule module = new FTRUMModule(context);
34
+ Promise promise = mock(Promise.class);
35
+
36
+ module.stopLongTaskTracking(promise);
37
+
38
+ verify(promise, times(1)).resolve(null);
39
+ verifyNoMoreInteractions(promise);
40
+ }
41
+
42
+ @Test
43
+ public void shutdownResolvesWhenRNRejectsDispatch() throws Exception {
44
+ Method dispatch = ReactApplicationContext.class.getMethod("runOnJSQueueThread", Runnable.class);
45
+ // RN 0.63 has a void API. Reflection keeps this test compilable on both APIs.
46
+ assumeTrue(dispatch.getReturnType() == boolean.class);
47
+ ReactApplicationContext context = mock(ReactApplicationContext.class);
48
+ when(dispatch.invoke(context, any(Runnable.class))).thenReturn(false);
49
+ FTRUMModule module = new FTRUMModule(context);
50
+ Promise promise = mock(Promise.class);
51
+
52
+ module.stopLongTaskTracking(promise);
53
+
54
+ dispatch.invoke(verify(context, times(1)), any(Runnable.class));
55
+ verify(promise, times(1)).resolve(null);
56
+ verifyNoMoreInteractions(promise);
57
+ }
58
+
59
+ @Test
60
+ public void shutdownResolvesWhenRNQueueIsNotInitialized() throws Exception {
61
+ Method dispatch = ReactApplicationContext.class.getMethod("runOnJSQueueThread", Runnable.class);
62
+ assumeTrue(dispatch.getReturnType() == boolean.class);
63
+ ReactApplicationContext context = mock(ReactApplicationContext.class);
64
+ when(dispatch.invoke(context, any(Runnable.class))).thenThrow(new AssertionError("JS queue is null"));
65
+ FTRUMModule module = new FTRUMModule(context);
66
+ Promise promise = mock(Promise.class);
67
+
68
+ module.stopLongTaskTracking(promise);
69
+
70
+ verify(promise, times(1)).resolve(null);
71
+ verifyNoMoreInteractions(promise);
72
+ }
73
+
74
+ @Test
75
+ public void invalidateAndLegacyDestroyShareOneCleanup() {
76
+ ReactApplicationContext context = mock(ReactApplicationContext.class);
77
+ FTRUMModule module = new FTRUMModule(context);
78
+
79
+ module.invalidate();
80
+ module.invalidate();
81
+ module.onCatalystInstanceDestroy();
82
+
83
+ verify(context, times(1)).removeLifecycleEventListener(any(FTRUMImpl.class));
84
+ }
85
+
86
+ @Test
87
+ public void legacyDestroyThenInvalidateAlsoCleansUpOnce() {
88
+ ReactApplicationContext context = mock(ReactApplicationContext.class);
89
+ FTRUMModule module = new FTRUMModule(context);
90
+
91
+ module.onCatalystInstanceDestroy();
92
+ module.invalidate();
93
+
94
+ verify(context, times(1)).removeLifecycleEventListener(any(FTRUMImpl.class));
95
+ }
96
+
97
+ @Test
98
+ public void destroyedRUMDoesNotResumeMonitoring() {
99
+ ReactApplicationContext context = mock(ReactApplicationContext.class);
100
+ JsLongTaskMonitor.Executor executor = mock(JsLongTaskMonitor.Executor.class);
101
+ JsLongTaskMonitor monitor = new JsLongTaskMonitor(
102
+ executor,
103
+ mock(JsLongTaskMonitor.FrameSchedulerFactory.class),
104
+ mock(JsLongTaskMonitor.Reporter.class)
105
+ );
106
+ FTRUMImpl rum = new FTRUMImpl(context, monitor);
107
+
108
+ rum.destroy();
109
+ rum.destroy();
110
+ monitor.setThresholdMilliseconds(100);
111
+ rum.onHostResume();
112
+
113
+ verify(context, times(1)).removeLifecycleEventListener(rum);
114
+ verify(executor, times(1)).runOnJsThread(any(Runnable.class));
115
+ verifyNoMoreInteractions(executor);
116
+ }
117
+ }