@datadog/mobile-react-native-session-replay 2.0.2-alpha.0

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 (77) hide show
  1. package/DatadogSDKReactNativeSessionReplay.podspec +41 -0
  2. package/README.md +3 -0
  3. package/android/build.gradle +239 -0
  4. package/android/detekt.yml +572 -0
  5. package/android/gradle.properties +5 -0
  6. package/android/src/main/AndroidManifest.xml +11 -0
  7. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/DatadogSDKReactNativeSessionReplayPackage.kt +46 -0
  8. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementation.kt +57 -0
  9. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/NoopTextPropertiesResolver.kt +22 -0
  10. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ReactNativeSessionReplayExtensionSupport.kt +77 -0
  11. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ReactTextPropertiesResolver.kt +196 -0
  12. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/SessionReplaySDKWrapper.kt +24 -0
  13. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/SessionReplayWrapper.kt +22 -0
  14. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ShadowNodeWrapper.kt +70 -0
  15. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/TextPropertiesResolver.kt +20 -0
  16. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/extensions/LongExt.kt +15 -0
  17. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactMaskInputTextMapper.kt +54 -0
  18. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactMaskTextMapper.kt +55 -0
  19. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactTextMapper.kt +54 -0
  20. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactViewGroupMapper.kt +58 -0
  21. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/utils/ColorUtils.kt +22 -0
  22. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/utils/DrawableUtils.kt +35 -0
  23. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +66 -0
  24. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/utils/ReflectionUtils.kt +30 -0
  25. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/utils/TextViewUtils.kt +40 -0
  26. package/android/src/newarch/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplay.kt +33 -0
  27. package/android/src/oldarch/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplay.kt +34 -0
  28. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementationTest.kt +105 -0
  29. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/ReactNativeSessionReplayExtensionSupportTest.kt +127 -0
  30. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/ReactTextPropertiesResolverTest.kt +271 -0
  31. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactViewGroupMapperTest.kt +131 -0
  32. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/utils/ColorUtilsTest.kt +42 -0
  33. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/utils/DrawableUtilsTest.kt +101 -0
  34. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/utils/TextViewUtilsTest.kt +109 -0
  35. package/android/src/test/kotlin/com/datadog/reactnative/tools/unit/GenericAssert.kt +69 -0
  36. package/android/src/test/kotlin/com/datadog/reactnative/tools/unit/MapExt.kt +29 -0
  37. package/android/src/test/kotlin/com/datadog/reactnative/tools/unit/ReflectUtils.kt +266 -0
  38. package/android/src/test/kotlin/com/datadog/reactnative/tools/unit/forge/BaseConfigurator.kt +24 -0
  39. package/android/src/test/kotlin/com/datadog/reactnative/tools/unit/forge/ForgeConfigurator.kt +24 -0
  40. package/android/src/test/kotlin/com/datadog/reactnative/tools/unit/forge/TextWireframeForgeryFactory.kt +64 -0
  41. package/android/src/test/kotlin/com/datadog/reactnative/tools/unit/forge/Throwable.kt +31 -0
  42. package/android/src/test/kotlin/com/datadog/reactnative/tools/unit/forge/ThrowableForgeryFactory.kt +21 -0
  43. package/android/src/test/kotlin/com/datadog/reactnative/tools/unit/forge/WireframeClipForgeryFactory.kt +25 -0
  44. package/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker +1 -0
  45. package/ios/DatadogSDKReactNativeSessionReplay.xcodeproj/project.pbxproj +272 -0
  46. package/ios/DatadogSDKReactNativeSessionReplay.xcodeproj/project.xcworkspace/contents.xcworkspacedata +4 -0
  47. package/ios/Sources/DatadogSDKReactNativeSessionReplay.h +8 -0
  48. package/ios/Sources/DdSessionReplay.h +24 -0
  49. package/ios/Sources/DdSessionReplay.mm +53 -0
  50. package/ios/Sources/DdSessionReplayImplementation.swift +70 -0
  51. package/ios/Sources/RCTTextViewRecorder.swift +157 -0
  52. package/lib/commonjs/SessionReplay.js +66 -0
  53. package/lib/commonjs/SessionReplay.js.map +1 -0
  54. package/lib/commonjs/index.js +26 -0
  55. package/lib/commonjs/index.js.map +1 -0
  56. package/lib/commonjs/nativeModulesTypes.js +2 -0
  57. package/lib/commonjs/nativeModulesTypes.js.map +1 -0
  58. package/lib/commonjs/specs/NativeDdSessionReplay.js +20 -0
  59. package/lib/commonjs/specs/NativeDdSessionReplay.js.map +1 -0
  60. package/lib/module/SessionReplay.js +53 -0
  61. package/lib/module/SessionReplay.js.map +1 -0
  62. package/lib/module/index.js +8 -0
  63. package/lib/module/index.js.map +1 -0
  64. package/lib/module/nativeModulesTypes.js +2 -0
  65. package/lib/module/nativeModulesTypes.js.map +1 -0
  66. package/lib/module/specs/NativeDdSessionReplay.js +14 -0
  67. package/lib/module/specs/NativeDdSessionReplay.js.map +1 -0
  68. package/lib/typescript/SessionReplay.d.ts +34 -0
  69. package/lib/typescript/index.d.ts +2 -0
  70. package/lib/typescript/nativeModulesTypes.d.ts +18 -0
  71. package/lib/typescript/specs/NativeDdSessionReplay.d.ts +15 -0
  72. package/package.json +90 -0
  73. package/src/SessionReplay.ts +84 -0
  74. package/src/__tests__/SessionReplay.test.ts +49 -0
  75. package/src/index.ts +13 -0
  76. package/src/nativeModulesTypes.ts +29 -0
  77. package/src/specs/NativeDdSessionReplay.ts +28 -0
@@ -0,0 +1,271 @@
1
+ /*
2
+ * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3
+ * This product includes software developed at Datadog (https://www.datadoghq.com/).
4
+ * Copyright 2016-Present Datadog, Inc.
5
+ */
6
+
7
+ package com.datadog.reactnative.sessionreplay
8
+
9
+ import android.widget.TextView
10
+ import com.datadog.android.sessionreplay.model.MobileSegment
11
+ import com.datadog.reactnative.sessionreplay.ReactTextPropertiesResolver.Companion.COLOR_FIELD_NAME
12
+ import com.datadog.reactnative.sessionreplay.ReactTextPropertiesResolver.Companion.FONT_FAMILY_FIELD_NAME
13
+ import com.datadog.reactnative.sessionreplay.ReactTextPropertiesResolver.Companion.MONOSPACE_FAMILY_NAME
14
+ import com.datadog.reactnative.sessionreplay.ReactTextPropertiesResolver.Companion.TEXT_ATTRIBUTES_FIELD_NAME
15
+ import com.datadog.reactnative.sessionreplay.ShadowNodeWrapper.Companion.UI_IMPLEMENTATION_FIELD_NAME
16
+ import com.datadog.reactnative.sessionreplay.utils.DrawableUtils
17
+ import com.datadog.reactnative.sessionreplay.utils.ReactViewBackgroundDrawableUtils
18
+ import com.datadog.reactnative.sessionreplay.utils.ReflectionUtils
19
+ import com.datadog.reactnative.sessionreplay.utils.formatAsRgba
20
+ import com.datadog.reactnative.tools.unit.forge.ForgeConfigurator
21
+ import com.facebook.react.bridge.ReactContext
22
+ import com.facebook.react.uimanager.ReactShadowNode
23
+ import com.facebook.react.uimanager.UIImplementation
24
+ import com.facebook.react.uimanager.UIManagerModule
25
+ import com.facebook.react.views.text.TextAttributes
26
+ import com.facebook.react.views.view.ReactViewBackgroundDrawable
27
+ import fr.xgouchet.elmyr.Forge
28
+ import fr.xgouchet.elmyr.annotation.Forgery
29
+ import fr.xgouchet.elmyr.annotation.IntForgery
30
+ import fr.xgouchet.elmyr.junit5.ForgeConfiguration
31
+ import fr.xgouchet.elmyr.junit5.ForgeExtension
32
+ import org.assertj.core.api.Assertions.assertThat
33
+ import org.junit.jupiter.api.BeforeEach
34
+ import org.junit.jupiter.api.Test
35
+ import org.junit.jupiter.api.extension.ExtendWith
36
+ import org.junit.jupiter.api.extension.Extensions
37
+ import org.mockito.Mock
38
+ import org.mockito.junit.jupiter.MockitoExtension
39
+ import org.mockito.junit.jupiter.MockitoSettings
40
+ import org.mockito.kotlin.any
41
+ import org.mockito.kotlin.eq
42
+ import org.mockito.kotlin.whenever
43
+ import org.mockito.quality.Strictness
44
+
45
+ @Extensions(
46
+ ExtendWith(MockitoExtension::class),
47
+ ExtendWith(ForgeExtension::class)
48
+ )
49
+ @MockitoSettings(strictness = Strictness.LENIENT)
50
+ @ForgeConfiguration(ForgeConfigurator::class)
51
+ internal class ReactTextPropertiesResolverTest {
52
+ private lateinit var testedResolver: ReactTextPropertiesResolver
53
+
54
+ @Mock
55
+ lateinit var mockReactContext: ReactContext
56
+
57
+ @Mock
58
+ lateinit var mockUiManagerModule: UIManagerModule
59
+
60
+ @Mock
61
+ lateinit var mockWireframe: MobileSegment.Wireframe.TextWireframe
62
+
63
+ @Mock
64
+ lateinit var mockTextView: TextView
65
+
66
+ @Mock
67
+ lateinit var mockDrawableUtils: DrawableUtils
68
+
69
+ @Mock
70
+ lateinit var mockReactViewBackgroundDrawable: ReactViewBackgroundDrawable
71
+
72
+ @Mock
73
+ lateinit var mockReactViewBackgroundDrawableUtils: ReactViewBackgroundDrawableUtils
74
+
75
+ @Mock
76
+ lateinit var mockShadowNodeWrapper: ShadowNodeWrapper
77
+
78
+ @Mock
79
+ lateinit var mockReflectionUtils: ReflectionUtils
80
+
81
+ @Forgery
82
+ private lateinit var fakeWireframe: MobileSegment.Wireframe.TextWireframe
83
+
84
+ @Mock
85
+ private lateinit var mockUiImplementation: UIImplementation
86
+
87
+ @Mock
88
+ private lateinit var mockShadowNode: ReactShadowNode<out ReactShadowNode<*>>
89
+
90
+ @BeforeEach
91
+ fun `set up`(forge: Forge) {
92
+ whenever(
93
+ mockReflectionUtils.getDeclaredField(mockUiManagerModule, UI_IMPLEMENTATION_FIELD_NAME)
94
+ ).thenReturn(mockUiImplementation)
95
+
96
+ whenever(
97
+ mockUiImplementation.resolveShadowNode(
98
+ eq(mockTextView.id)
99
+ )
100
+ ).thenReturn(mockShadowNode)
101
+
102
+ whenever(mockReactContext.runOnNativeModulesQueueThread(any())).thenAnswer {
103
+ (it.arguments[0] as Runnable).run()
104
+ }
105
+
106
+ testedResolver = ReactTextPropertiesResolver(
107
+ reactContext = mockReactContext,
108
+ uiManagerModule = mockUiManagerModule,
109
+ reactViewBackgroundDrawableUtils = mockReactViewBackgroundDrawableUtils,
110
+ drawableUtils = mockDrawableUtils,
111
+ reflectionUtils = mockReflectionUtils
112
+ )
113
+ }
114
+
115
+ // region addReactNativeProperties
116
+
117
+ @Test
118
+ fun `M get original wireframe W addReactNativeProperties() { no react properties }`() {
119
+ // Given
120
+ whenever(mockTextView.background).thenReturn(null)
121
+ whenever(mockUiImplementation.resolveShadowNode(any())).thenReturn(null)
122
+
123
+ // When
124
+ val result = testedResolver.addReactNativeProperties(mockWireframe, mockTextView, 0f)
125
+
126
+ // Then
127
+ assertThat(result).isEqualTo(mockWireframe)
128
+ }
129
+
130
+ @Test
131
+ fun `M add drawable properties W addReactNativeProperties() { has reactBackgroundDrawable }`(
132
+ forge: Forge
133
+ ) {
134
+ // Given
135
+ val pixelDensity = 0f
136
+ val fakeBorderRadius = forge.aPositiveFloat()
137
+ val fakeBorderWidth = forge.aPositiveFloat()
138
+ val fakeBorderColor = forge.aPositiveInt()
139
+ whenever(mockTextView.background).thenReturn(mockReactViewBackgroundDrawable)
140
+ whenever(
141
+ mockDrawableUtils.getReactBackgroundFromDrawable(
142
+ eq(mockTextView.background)
143
+ )
144
+ ).thenReturn(mockReactViewBackgroundDrawable)
145
+ whenever(
146
+ mockReactViewBackgroundDrawableUtils.resolveShapeAndBorder(
147
+ drawable = eq(mockReactViewBackgroundDrawable),
148
+ opacity = eq(0f),
149
+ pixelDensity = eq(0f)
150
+ )
151
+ ).thenReturn(
152
+ MobileSegment.ShapeStyle(
153
+ backgroundColor = formatAsRgba(fakeBorderColor),
154
+ opacity = 0f,
155
+ cornerRadius = fakeBorderRadius.toLong()
156
+ ) to MobileSegment.ShapeBorder(
157
+ color = formatAsRgba(fakeBorderColor),
158
+ width = fakeBorderWidth.toLong()
159
+ )
160
+ )
161
+
162
+ // When
163
+ val result = testedResolver.addReactNativeProperties(
164
+ fakeWireframe,
165
+ mockTextView,
166
+ pixelDensity
167
+ )
168
+
169
+ // Then
170
+ assertThat(result.shapeStyle?.cornerRadius).isEqualTo(fakeBorderRadius.toLong())
171
+ assertThat(result.border?.width).isEqualTo(fakeBorderWidth.toLong())
172
+ assertThat(result.border?.color).isEqualTo(formatAsRgba(fakeBorderColor))
173
+ }
174
+
175
+ @Test
176
+ fun `M resolve font family W addReactNativeProperties()`() {
177
+ // Given
178
+ whenever(mockReflectionUtils.getDeclaredField(mockShadowNode, FONT_FAMILY_FIELD_NAME))
179
+ .thenReturn(MONOSPACE_FAMILY_NAME)
180
+
181
+ whenever(mockTextView.background).thenReturn(null)
182
+
183
+ // When
184
+ val result = testedResolver
185
+ .addReactNativeProperties(fakeWireframe, mockTextView, 0f)
186
+
187
+ // Then
188
+ assertThat(result.textStyle.family)
189
+ .isEqualTo(MONOSPACE_FAMILY_NAME)
190
+ }
191
+
192
+ @Test
193
+ fun `M fallback W addReactNativeProperties() { cannot resolve fontFamily }`() {
194
+ // Given
195
+ whenever(mockTextView.background).thenReturn(null)
196
+ whenever(mockShadowNodeWrapper.getDeclaredShadowNodeField(FONT_FAMILY_FIELD_NAME))
197
+ .thenReturn(null)
198
+
199
+ // When
200
+ val result = testedResolver.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
201
+
202
+ // Then
203
+ assertThat(result.textStyle.family).isEqualTo(fakeWireframe.textStyle.family)
204
+ }
205
+
206
+ @Test
207
+ fun `M resolve font size W addReactNativeProperties()`(
208
+ @Mock mockTextAttributes: TextAttributes,
209
+ @IntForgery fakeTextSize: Int
210
+ ) {
211
+ // Given
212
+ whenever(mockReflectionUtils.getDeclaredField(mockShadowNode, TEXT_ATTRIBUTES_FIELD_NAME))
213
+ .thenReturn(mockTextAttributes)
214
+ whenever(mockTextView.background).thenReturn(null)
215
+ whenever(mockTextAttributes.effectiveFontSize).thenReturn(fakeTextSize)
216
+
217
+ // When
218
+ val result = testedResolver.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
219
+
220
+ // Then
221
+ assertThat(result.textStyle.size).isEqualTo(fakeTextSize.toLong())
222
+ }
223
+
224
+ @Test
225
+ fun `M fallback W addReactNativeProperties() { cannot resolve fontSize }`(
226
+ @Mock mockTextAttributes: TextAttributes
227
+ ) {
228
+ // Given
229
+ whenever(mockTextView.background).thenReturn(null)
230
+ whenever(mockShadowNodeWrapper.getDeclaredShadowNodeField(TEXT_ATTRIBUTES_FIELD_NAME))
231
+ .thenReturn(null)
232
+
233
+ // When
234
+ val result = testedResolver.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
235
+
236
+ // Then
237
+ assertThat(result.textStyle.size).isEqualTo(fakeWireframe.textStyle.size)
238
+ }
239
+
240
+ @Test
241
+ fun `M resolve font color W addReactNativeProperties()`(
242
+ @IntForgery fakeTextColor: Int
243
+ ) {
244
+ // Given
245
+ whenever(mockTextView.background).thenReturn(null)
246
+ whenever(mockReflectionUtils.getDeclaredField(mockShadowNode, COLOR_FIELD_NAME))
247
+ .thenReturn(fakeTextColor)
248
+
249
+ // When
250
+ val result = testedResolver.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
251
+
252
+ // Then
253
+ assertThat(result.textStyle.color).isEqualTo(formatAsRgba(fakeTextColor))
254
+ }
255
+
256
+ @Test
257
+ fun `M fallback W addReactNativeProperties() { cannot resolve fontColor }`() {
258
+ // Given
259
+ whenever(mockTextView.background).thenReturn(null)
260
+ whenever(mockShadowNodeWrapper.getDeclaredShadowNodeField(COLOR_FIELD_NAME))
261
+ .thenReturn(null)
262
+
263
+ // When
264
+ val result = testedResolver.addReactNativeProperties(fakeWireframe, mockTextView, 0f)
265
+
266
+ // Then
267
+ assertThat(result.textStyle.color).isEqualTo(fakeWireframe.textStyle.color)
268
+ }
269
+
270
+ // endregion
271
+ }
@@ -0,0 +1,131 @@
1
+ /*
2
+ *
3
+ * * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
4
+ * * This product includes software developed at Datadog (https://www.datadoghq.com/).
5
+ * * Copyright 2016-Present Datadog, Inc.
6
+ *
7
+ */
8
+
9
+ package com.datadog.reactnative.sessionreplay.mappers
10
+
11
+ import android.graphics.drawable.ColorDrawable
12
+ import com.datadog.android.sessionreplay.internal.AsyncJobStatusCallback
13
+ import com.datadog.android.sessionreplay.internal.recorder.MappingContext
14
+ import com.datadog.android.sessionreplay.internal.recorder.SystemInformation
15
+ import com.datadog.android.sessionreplay.model.MobileSegment
16
+ import com.datadog.reactnative.sessionreplay.utils.DrawableUtils
17
+ import com.datadog.reactnative.sessionreplay.utils.ReactViewBackgroundDrawableUtils
18
+ import com.facebook.react.views.view.ReactViewBackgroundDrawable
19
+ import com.facebook.react.views.view.ReactViewGroup
20
+ import fr.xgouchet.elmyr.junit5.ForgeExtension
21
+ import org.assertj.core.api.Assertions.assertThat
22
+ import org.junit.jupiter.api.BeforeEach
23
+ import org.junit.jupiter.api.Test
24
+ import org.junit.jupiter.api.extension.ExtendWith
25
+ import org.junit.jupiter.api.extension.Extensions
26
+ import org.mockito.Mock
27
+ import org.mockito.junit.jupiter.MockitoExtension
28
+ import org.mockito.junit.jupiter.MockitoSettings
29
+ import org.mockito.kotlin.eq
30
+ import org.mockito.kotlin.whenever
31
+ import org.mockito.quality.Strictness
32
+
33
+ @Extensions(
34
+ ExtendWith(MockitoExtension::class),
35
+ ExtendWith(ForgeExtension::class)
36
+ )
37
+ @MockitoSettings(strictness = Strictness.LENIENT)
38
+ internal class ReactViewGroupMapperTest {
39
+ private lateinit var testedMapper: ReactViewGroupMapper
40
+
41
+ @Mock
42
+ private lateinit var mockReactViewBackgroundDrawableUtils: ReactViewBackgroundDrawableUtils
43
+
44
+ @Mock
45
+ private lateinit var mockReactViewGroup: ReactViewGroup
46
+
47
+ @Mock
48
+ private lateinit var mockMappingContext: MappingContext
49
+
50
+ @Mock
51
+ private lateinit var mockAsyncJobStatusCallback: AsyncJobStatusCallback
52
+
53
+ @Mock
54
+ private lateinit var mockSystemInformation: SystemInformation
55
+
56
+ @Mock
57
+ private lateinit var mockDrawableUtils: DrawableUtils
58
+
59
+ @Mock
60
+ private lateinit var mockReactViewBackgroundDrawable: ReactViewBackgroundDrawable
61
+
62
+ @Mock
63
+ private lateinit var mockShapeStyle: MobileSegment.ShapeStyle
64
+
65
+ @Mock
66
+ private lateinit var mockShapeBorder: MobileSegment.ShapeBorder
67
+
68
+ @BeforeEach
69
+ fun `set up`() {
70
+ whenever(mockMappingContext.systemInformation).thenReturn(mockSystemInformation)
71
+ whenever(mockSystemInformation.screenDensity).thenReturn(0f)
72
+
73
+ testedMapper = ReactViewGroupMapper(
74
+ reactViewBackgroundDrawableUtils = mockReactViewBackgroundDrawableUtils,
75
+ drawableUtils = mockDrawableUtils
76
+ )
77
+ }
78
+
79
+ @Test
80
+ fun `M return null shapestyle + border W map() { other drawable }`(
81
+ @Mock mockColorDrawable: ColorDrawable
82
+ ) {
83
+ // Given
84
+ whenever(mockReactViewGroup.background).thenReturn(mockColorDrawable)
85
+ whenever(
86
+ mockDrawableUtils.getReactBackgroundFromDrawable(
87
+ drawable = eq(mockColorDrawable)
88
+ )
89
+ ).thenReturn(null)
90
+
91
+ // When
92
+ val result = testedMapper.map(
93
+ view = mockReactViewGroup,
94
+ mappingContext = mockMappingContext,
95
+ asyncJobStatusCallback = mockAsyncJobStatusCallback
96
+ )[0] as MobileSegment.Wireframe.ShapeWireframe
97
+
98
+ // Then
99
+ assertThat(result.shapeStyle).isNull()
100
+ assertThat(result.border).isNull()
101
+ }
102
+
103
+ @Test
104
+ fun `M return shapestyle + border W map() { ReactViewBackgroundDrawable }`() {
105
+ // Given
106
+ whenever(mockReactViewGroup.background).thenReturn(mockReactViewBackgroundDrawable)
107
+ whenever(
108
+ mockDrawableUtils.getReactBackgroundFromDrawable(
109
+ drawable = eq(mockReactViewBackgroundDrawable)
110
+ )
111
+ ).thenReturn(mockReactViewBackgroundDrawable)
112
+ whenever(
113
+ mockReactViewBackgroundDrawableUtils.resolveShapeAndBorder(
114
+ drawable = eq(mockReactViewBackgroundDrawable),
115
+ pixelDensity = eq(0f),
116
+ opacity = eq(0f)
117
+ )
118
+ ).thenReturn(mockShapeStyle to mockShapeBorder)
119
+
120
+ // When
121
+ val result = testedMapper.map(
122
+ view = mockReactViewGroup,
123
+ mappingContext = mockMappingContext,
124
+ asyncJobStatusCallback = mockAsyncJobStatusCallback
125
+ )[0] as MobileSegment.Wireframe.ShapeWireframe
126
+
127
+ // Then
128
+ assertThat(result.shapeStyle).isEqualTo(mockShapeStyle)
129
+ assertThat(result.border).isEqualTo(mockShapeBorder)
130
+ }
131
+ }
@@ -0,0 +1,42 @@
1
+ /*
2
+ * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3
+ * This product includes software developed at Datadog (https://www.datadoghq.com/).
4
+ * Copyright 2016-Present Datadog, Inc.
5
+ */
6
+
7
+ package com.datadog.reactnative.sessionreplay.utils
8
+
9
+ import fr.xgouchet.elmyr.junit5.ForgeExtension
10
+ import org.assertj.core.api.Assertions.assertThat
11
+ import org.junit.jupiter.api.Test
12
+ import org.junit.jupiter.api.extension.ExtendWith
13
+ import org.junit.jupiter.api.extension.Extensions
14
+ import org.mockito.junit.jupiter.MockitoExtension
15
+ import org.mockito.junit.jupiter.MockitoSettings
16
+ import org.mockito.quality.Strictness
17
+
18
+ @Extensions(
19
+ ExtendWith(MockitoExtension::class),
20
+ ExtendWith(ForgeExtension::class)
21
+ )
22
+ @MockitoSettings(strictness = Strictness.LENIENT)
23
+ internal class ColorUtilsTest {
24
+
25
+ @Test
26
+ fun `M return without alpha W formatAsRgba { color without alpha }`() {
27
+ // When
28
+ val hexColor = formatAsRgba(16711680)
29
+
30
+ // Then
31
+ assertThat(hexColor).isEqualTo("#ff0000")
32
+ }
33
+
34
+ @Test
35
+ fun `M resolve with alpha W formatAsRgba { color with alpha }`() {
36
+ // When
37
+ val hexColor = formatAsRgba(1717960806)
38
+
39
+ // Then
40
+ assertThat(hexColor).isEqualTo("#66006666")
41
+ }
42
+ }
@@ -0,0 +1,101 @@
1
+ /*
2
+ * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3
+ * This product includes software developed at Datadog (https://www.datadoghq.com/).
4
+ * Copyright 2016-Present Datadog, Inc.
5
+ */
6
+
7
+ package com.datadog.reactnative.sessionreplay.utils
8
+
9
+ import android.graphics.drawable.ColorDrawable
10
+ import android.graphics.drawable.InsetDrawable
11
+ import android.graphics.drawable.LayerDrawable
12
+ import com.facebook.react.views.view.ReactViewBackgroundDrawable
13
+ import fr.xgouchet.elmyr.junit5.ForgeExtension
14
+ import org.assertj.core.api.Assertions.assertThat
15
+ import org.junit.jupiter.api.BeforeEach
16
+ import org.junit.jupiter.api.Test
17
+ import org.junit.jupiter.api.extension.ExtendWith
18
+ import org.junit.jupiter.api.extension.Extensions
19
+ import org.mockito.Mock
20
+ import org.mockito.junit.jupiter.MockitoExtension
21
+ import org.mockito.junit.jupiter.MockitoSettings
22
+ import org.mockito.kotlin.whenever
23
+ import org.mockito.quality.Strictness
24
+
25
+ @Extensions(
26
+ ExtendWith(MockitoExtension::class),
27
+ ExtendWith(ForgeExtension::class)
28
+ )
29
+ @MockitoSettings(strictness = Strictness.LENIENT)
30
+ internal class DrawableUtilsTest {
31
+ private lateinit var testedDrawableUtils: DrawableUtils
32
+
33
+ @Mock
34
+ private lateinit var mockReactViewBackgroundDrawable: ReactViewBackgroundDrawable
35
+
36
+ @Mock
37
+ private lateinit var mockInsetDrawable: InsetDrawable
38
+
39
+ @Mock
40
+ private lateinit var mockLayerDrawable: LayerDrawable
41
+
42
+ @Mock
43
+ private lateinit var mockColorDrawable: ColorDrawable
44
+
45
+ @BeforeEach
46
+ fun `set up`() {
47
+ whenever(mockLayerDrawable.numberOfLayers).thenReturn(3)
48
+ whenever(mockLayerDrawable.getDrawable(0)).thenReturn(mockReactViewBackgroundDrawable)
49
+
50
+ testedDrawableUtils = DrawableUtils()
51
+ }
52
+
53
+ @Test
54
+ fun `M return drawable W getReactBackgroundFromDrawable { ReactViewBackgroundDrawable }`() {
55
+ // When
56
+ val result = testedDrawableUtils
57
+ .getReactBackgroundFromDrawable(mockReactViewBackgroundDrawable)
58
+
59
+ // Then
60
+ assertThat(result).isEqualTo(mockReactViewBackgroundDrawable)
61
+ }
62
+
63
+ @Test
64
+ fun `M return drawable W getReactBackgroundFromDrawable { InsetDrawable }`() {
65
+ // Given
66
+ whenever(mockInsetDrawable.drawable).thenReturn(mockReactViewBackgroundDrawable)
67
+
68
+ // When
69
+ val result = testedDrawableUtils.getReactBackgroundFromDrawable(mockInsetDrawable)
70
+
71
+ // Then
72
+ assertThat(result).isEqualTo(mockReactViewBackgroundDrawable)
73
+ }
74
+
75
+ @Test
76
+ fun `M return drawable W getReactBackgroundFromDrawable { LayerDrawable }`() {
77
+ // When
78
+ val result = testedDrawableUtils.getReactBackgroundFromDrawable(mockLayerDrawable)
79
+
80
+ // Then
81
+ assertThat(result).isEqualTo(mockReactViewBackgroundDrawable)
82
+ }
83
+
84
+ @Test
85
+ fun `M return null W getReactBackgroundFromDrawable { drawable is not supported }`() {
86
+ // When
87
+ val result = testedDrawableUtils.getReactBackgroundFromDrawable(mockColorDrawable)
88
+
89
+ // Then
90
+ assertThat(result).isNull()
91
+ }
92
+
93
+ @Test
94
+ fun `M return null W getReactBackgroundFromDrawable { null drawable }`() {
95
+ // When
96
+ val result = testedDrawableUtils.getReactBackgroundFromDrawable(null)
97
+
98
+ // Then
99
+ assertThat(result).isNull()
100
+ }
101
+ }
@@ -0,0 +1,109 @@
1
+ /*
2
+ * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3
+ * This product includes software developed at Datadog (https://www.datadoghq.com/).
4
+ * Copyright 2016-Present Datadog, Inc.
5
+ */
6
+
7
+ package com.datadog.reactnative.sessionreplay.utils
8
+
9
+ import android.content.res.Resources
10
+ import android.graphics.Typeface
11
+ import android.util.DisplayMetrics
12
+ import android.widget.TextView
13
+ import com.datadog.android.sessionreplay.internal.recorder.MappingContext
14
+ import com.datadog.android.sessionreplay.internal.recorder.SystemInformation
15
+ import com.datadog.android.sessionreplay.model.MobileSegment
16
+ import com.datadog.reactnative.sessionreplay.ReactTextPropertiesResolver
17
+ import fr.xgouchet.elmyr.Forge
18
+ import fr.xgouchet.elmyr.junit5.ForgeExtension
19
+ import org.assertj.core.api.Assertions.assertThat
20
+ import org.junit.jupiter.api.BeforeEach
21
+ import org.junit.jupiter.api.Test
22
+ import org.junit.jupiter.api.extension.ExtendWith
23
+ import org.junit.jupiter.api.extension.Extensions
24
+ import org.mockito.Mock
25
+ import org.mockito.junit.jupiter.MockitoExtension
26
+ import org.mockito.junit.jupiter.MockitoSettings
27
+ import org.mockito.kotlin.eq
28
+ import org.mockito.kotlin.whenever
29
+ import org.mockito.quality.Strictness
30
+
31
+ @Extensions(
32
+ ExtendWith(MockitoExtension::class),
33
+ ExtendWith(ForgeExtension::class)
34
+ )
35
+ @MockitoSettings(strictness = Strictness.LENIENT)
36
+ internal class TextViewUtilsTest {
37
+ private lateinit var testedUtils: TextViewUtils
38
+
39
+ @Mock
40
+ private lateinit var mockReactTextPropertiesResolver: ReactTextPropertiesResolver
41
+
42
+ @Mock
43
+ private lateinit var mockMappingContext: MappingContext
44
+
45
+ @Mock
46
+ private lateinit var mockTextView: TextView
47
+
48
+ @Mock
49
+ private lateinit var mockSystemInformation: SystemInformation
50
+
51
+ @Mock
52
+ private lateinit var mockResources: Resources
53
+
54
+ @Mock
55
+ private lateinit var mockDisplayMetrics: DisplayMetrics
56
+
57
+ @BeforeEach
58
+ fun `set up`(forge: Forge) {
59
+ whenever(mockResources.displayMetrics).thenReturn(mockDisplayMetrics)
60
+ whenever(mockTextView.resources).thenReturn(mockResources)
61
+ whenever(mockSystemInformation.screenDensity).thenReturn(0f)
62
+ whenever(mockMappingContext.systemInformation).thenReturn(mockSystemInformation)
63
+ whenever(mockTextView.text).thenReturn(forge.aString())
64
+ whenever(mockTextView.typeface).thenReturn(Typeface.SANS_SERIF)
65
+
66
+ testedUtils = TextViewUtils()
67
+ }
68
+
69
+ @Test
70
+ fun `M return wireframe W map() { even if not TextWireframeType }`(
71
+ @Mock mockImageWireframe: MobileSegment.Wireframe.ImageWireframe
72
+ ) {
73
+ // When
74
+ val result = testedUtils.mapTextViewToWireframes(
75
+ wireframes = listOf(mockImageWireframe),
76
+ view = mockTextView,
77
+ mappingContext = mockMappingContext,
78
+ reactTextPropertiesResolver = mockReactTextPropertiesResolver
79
+ )
80
+
81
+ // Then
82
+ assertThat(result).contains(mockImageWireframe)
83
+ }
84
+
85
+ @Test
86
+ fun `M return textWireframe W map()`(
87
+ @Mock mockTextWireframe: MobileSegment.Wireframe.TextWireframe
88
+ ) {
89
+ // Given
90
+ whenever(
91
+ mockReactTextPropertiesResolver.addReactNativeProperties(
92
+ originalWireframe = eq(mockTextWireframe),
93
+ view = eq(mockTextView),
94
+ pixelDensity = eq(0f)
95
+ )
96
+ ).thenReturn(mockTextWireframe)
97
+
98
+ // When
99
+ val result = testedUtils.mapTextViewToWireframes(
100
+ wireframes = listOf(mockTextWireframe),
101
+ view = mockTextView,
102
+ mappingContext = mockMappingContext,
103
+ reactTextPropertiesResolver = mockReactTextPropertiesResolver
104
+ )[0] as MobileSegment.Wireframe.TextWireframe
105
+
106
+ // Then
107
+ assertThat(result).isEqualTo(mockTextWireframe)
108
+ }
109
+ }