@@ -293,59 +293,6 @@ class SentryClientTest {
293
293
assertEquals(SentryLevel .DEBUG , sentEvent!! .level)
294
294
}
295
295
296
- @Test
297
- fun `when captureFeedback is called, sentry event contains feedback in contexts and header type` () {
298
- var sentEvent: SentryEvent ? = null
299
- fixture.sentryOptions.setBeforeSend { e, _ -> sentEvent = e; e }
300
- val sut = fixture.getSut()
301
- val scope = createScope()
302
- sut.captureFeedback(Feedback (" message" ), null , scope)
303
-
304
- val sentFeedback = sentEvent!! .contexts.feedback
305
- assertNotNull(sentFeedback)
306
- assertEquals(" message" , sentFeedback.message)
307
- assertNull(sentFeedback.replayId)
308
- assertNull(sentFeedback.url)
309
-
310
- verify(fixture.transport).send(
311
- check {
312
- assertEquals(SentryItemType .Feedback , it.items.first().header.type)
313
- },
314
- anyOrNull()
315
- )
316
- }
317
-
318
- @Test
319
- fun `when captureFeedback, scope replay id is attached to feedback` () {
320
- var sentEvent: SentryEvent ? = null
321
- fixture.sentryOptions.setBeforeSend { e, _ -> sentEvent = e; e }
322
- val replayId = SentryId ()
323
- val sut = fixture.getSut()
324
- val scope = createScope()
325
- scope.replayId = replayId
326
- sut.captureFeedback(Feedback (" message" ), null , scope)
327
-
328
- val sentFeedback = sentEvent!! .contexts.feedback
329
- assertNotNull(sentFeedback)
330
- assertEquals(replayId.toString(), sentFeedback.replayId?.toString())
331
- assertNull(sentFeedback.url)
332
- }
333
-
334
- @Test
335
- fun `when captureFeedback, screen is attached to feedback as url` () {
336
- var sentEvent: SentryEvent ? = null
337
- fixture.sentryOptions.setBeforeSend { e, _ -> sentEvent = e; e }
338
- val sut = fixture.getSut()
339
- val scope = createScope()
340
- scope.screen = " screen"
341
- sut.captureFeedback(Feedback (" message" ), null , scope)
342
-
343
- val sentFeedback = sentEvent!! .contexts.feedback
344
- assertNotNull(sentFeedback)
345
- assertEquals(" screen" , sentFeedback.url)
346
- assertNull(sentFeedback.replayId)
347
- }
348
-
349
296
@Test
350
297
fun `when event has release, value from options not applied` () {
351
298
val event = SentryEvent ()
@@ -2804,6 +2751,8 @@ class SentryClientTest {
2804
2751
verify(fixture.transport).send(anyOrNull(), anyOrNull())
2805
2752
}
2806
2753
2754
+ // region Replay
2755
+
2807
2756
@Test
2808
2757
fun `when captureReplayEvent, envelope is sent` () {
2809
2758
val sut = fixture.getSut()
@@ -3044,6 +2993,194 @@ class SentryClientTest {
3044
2993
)
3045
2994
}
3046
2995
2996
+ // endregion
2997
+
2998
+ // region Feedback
2999
+
3000
+ @Test
3001
+ fun `when captureFeedback is called, sentry event contains feedback in contexts and header type` () {
3002
+ var sentEvent: SentryEvent ? = null
3003
+ fixture.sentryOptions.setBeforeSendFeedback { e, _ -> sentEvent = e; e }
3004
+ val sut = fixture.getSut()
3005
+ val scope = createScope()
3006
+ sut.captureFeedback(Feedback (" message" ), null , scope)
3007
+
3008
+ val sentFeedback = sentEvent!! .contexts.feedback
3009
+ assertNotNull(sentFeedback)
3010
+ assertEquals(" message" , sentFeedback.message)
3011
+ assertNull(sentFeedback.replayId)
3012
+ assertNull(sentFeedback.url)
3013
+
3014
+ verify(fixture.transport).send(
3015
+ check {
3016
+ assertEquals(SentryItemType .Feedback , it.items.first().header.type)
3017
+ },
3018
+ anyOrNull()
3019
+ )
3020
+ }
3021
+
3022
+ @Test
3023
+ fun `when captureFeedback, scope data is attached to feedback` () {
3024
+ var sentEvent: SentryEvent ? = null
3025
+ fixture.sentryOptions.setBeforeSendFeedback { e, _ -> sentEvent = e; e }
3026
+ val sut = fixture.getSut()
3027
+ val scope = createScope()
3028
+ val scopeReplayId = SentryId ()
3029
+ scope.contexts.setTrace(SpanContext (" test" ))
3030
+ scope.setContexts(" context-key" , " context-value" )
3031
+ scope.screen = " screen"
3032
+ scope.replayId = scopeReplayId
3033
+ sut.captureFeedback(Feedback (" message" ), null , scope)
3034
+
3035
+ val sentFeedback = sentEvent!! .contexts.feedback
3036
+ assertNotNull(sentFeedback)
3037
+ // User, tags and contexts are applied to the feedback
3038
+ assertEquals(scope.user, sentEvent!! .user)
3039
+ assertEquals(" tags" , sentEvent!! .tags!! [" tags" ])
3040
+ assertEquals(
3041
+ scope.contexts.trace!! .traceId.toString(),
3042
+ sentEvent!! .contexts.trace!! .traceId.toString()
3043
+ )
3044
+ assertEquals(mapOf (" value" to " context-value" ), sentEvent!! .contexts[" context-key" ])
3045
+ // currently running replay id set in scope is applied to feedback
3046
+ assertEquals(scopeReplayId, sentFeedback.replayId)
3047
+ // screen set to scope is applied as url
3048
+ assertEquals(" screen" , sentFeedback.url)
3049
+ // extras and breadcrumbs are not applied to feedback
3050
+ assertNull(sentEvent!! .extras)
3051
+ assertNull(sentEvent!! .breadcrumbs)
3052
+ }
3053
+
3054
+ @Test
3055
+ fun `when captureFeedback, replay controller is stopped if no replay id is provided` () {
3056
+ var sentEvent: SentryEvent ? = null
3057
+ fixture.sentryOptions.setBeforeSendFeedback { e, _ -> sentEvent = e; e }
3058
+ val replayController = mock<ReplayController >()
3059
+ val replayId = SentryId ()
3060
+ val scope = createScope()
3061
+ whenever(replayController.captureReplay(any())).thenAnswer {
3062
+ run { scope.replayId = replayId }
3063
+ }
3064
+ val sut = fixture.getSut { it.setReplayController(replayController) }
3065
+ // When there is no replay id in the feedback
3066
+ sut.captureFeedback(Feedback (" message" ), null , scope)
3067
+
3068
+ // Then the replay controller captures the replay
3069
+ verify(replayController).captureReplay(eq(false ))
3070
+
3071
+ val sentFeedback = sentEvent!! .contexts.feedback
3072
+ assertNotNull(sentFeedback)
3073
+ // And the replay id is set to the one from the scope (coming from the replay controller)
3074
+ assertEquals(replayId, sentFeedback.replayId)
3075
+ }
3076
+
3077
+ @Test
3078
+ fun `when captureFeedback, replay controller is not stopped if replay id is provided` () {
3079
+ var sentEvent: SentryEvent ? = null
3080
+ fixture.sentryOptions.setBeforeSendFeedback { e, _ -> sentEvent = e; e }
3081
+ val replayController = mock<ReplayController >()
3082
+ val replayId = SentryId ()
3083
+ val scope = createScope()
3084
+ whenever(replayController.captureReplay(any())).thenAnswer {
3085
+ run { scope.replayId = replayId }
3086
+ }
3087
+ val sut = fixture.getSut { it.setReplayController(replayController) }
3088
+ // When there is replay id in the feedback
3089
+ val feedback = Feedback (" message" )
3090
+ feedback.setReplayId(SentryId ())
3091
+ sut.captureFeedback(feedback, null , scope)
3092
+
3093
+ // Then the replay controller doesn't capture the replay
3094
+ verify(replayController, never()).captureReplay(any())
3095
+
3096
+ val sentFeedback = sentEvent!! .contexts.feedback
3097
+ assertNotNull(sentFeedback)
3098
+ // And the replay id is set to the one from the scope (coming from the replay controller)
3099
+ assertNotNull(sentFeedback.replayId)
3100
+ assertNotEquals(replayId, sentFeedback.replayId)
3101
+ }
3102
+
3103
+ @Test
3104
+ fun `when beforeSendFeedback is set, callback is invoked` () {
3105
+ var invoked = false
3106
+ fixture.sentryOptions.setBeforeSendFeedback { event: SentryEvent , _: Hint -> invoked = true ; event }
3107
+ fixture.getSut().captureFeedback(Feedback (" message" ), null , createScope())
3108
+ assertTrue(invoked)
3109
+ }
3110
+
3111
+ @Test
3112
+ fun `when beforeSendFeedback returns null, feedback is dropped` () {
3113
+ fixture.sentryOptions.setBeforeSendFeedback { event: SentryEvent , _: Hint -> null }
3114
+ fixture.getSut().captureFeedback(Feedback (" message" ), null , createScope())
3115
+ verify(fixture.transport, never()).send(any(), anyOrNull())
3116
+
3117
+ assertClientReport(
3118
+ fixture.sentryOptions.clientReportRecorder,
3119
+ listOf (
3120
+ DiscardedEvent (DiscardReason .BEFORE_SEND .reason, DataCategory .Feedback .category, 1 )
3121
+ )
3122
+ )
3123
+ }
3124
+
3125
+ @Test
3126
+ fun `when beforeSendFeedback returns new instance, new instance is sent` () {
3127
+ val expected = SentryEvent ().apply { contexts.setFeedback(Feedback (" expected" )) }
3128
+ fixture.sentryOptions.setBeforeSendFeedback { _, _ -> expected }
3129
+
3130
+ fixture.getSut().captureFeedback(Feedback (" sent" ), null , Scope (fixture.sentryOptions))
3131
+
3132
+ verify(fixture.transport).send(
3133
+ check {
3134
+ val event = getEventFromData(it.items.first().data)
3135
+ assertEquals(" expected" , event.contexts.feedback!! .message)
3136
+ },
3137
+ anyOrNull()
3138
+ )
3139
+ verifyNoMoreInteractions(fixture.transport)
3140
+ }
3141
+
3142
+ @Test
3143
+ fun `when beforeSendFeedback throws an exception, feedback is dropped` () {
3144
+ val exception = Exception (" test" )
3145
+ fixture.sentryOptions.setBeforeSendFeedback { _, _ -> throw exception }
3146
+ val id = fixture.getSut().captureFeedback(Feedback (" message" ), null , Scope (fixture.sentryOptions))
3147
+ assertEquals(SentryId .EMPTY_ID , id)
3148
+
3149
+ assertClientReport(
3150
+ fixture.sentryOptions.clientReportRecorder,
3151
+ listOf (
3152
+ DiscardedEvent (DiscardReason .BEFORE_SEND .reason, DataCategory .Feedback .category, 1 )
3153
+ )
3154
+ )
3155
+ }
3156
+
3157
+ @Test
3158
+ fun `when feedback is dropped, captures client report with datacategory feedback` () {
3159
+ fixture.sentryOptions.addEventProcessor(DropEverythingEventProcessor ())
3160
+ val sut = fixture.getSut()
3161
+ sut.captureFeedback(Feedback (" message" ), null , createScope())
3162
+
3163
+ assertClientReport(
3164
+ fixture.sentryOptions.clientReportRecorder,
3165
+ listOf (DiscardedEvent (DiscardReason .EVENT_PROCESSOR .reason, DataCategory .Feedback .category, 1 ))
3166
+ )
3167
+ }
3168
+
3169
+ @Test
3170
+ fun `captureFeedback does not capture replay when backfilled` () {
3171
+ val replayController = mock<ReplayController >()
3172
+ val sut = fixture.getSut { it.setReplayController(replayController) }
3173
+
3174
+ sut.captureFeedback(
3175
+ Feedback (" message" ),
3176
+ HintUtils .createWithTypeCheckHint(BackfillableHint ()),
3177
+ createScope()
3178
+ )
3179
+ verify(replayController, never()).captureReplay(any())
3180
+ }
3181
+
3182
+ // endregion
3183
+
3047
3184
private fun givenScopeWithStartedSession (errored : Boolean = false, crashed : Boolean = false): IScope {
3048
3185
val scope = createScope(fixture.sentryOptions)
3049
3186
scope.startSession()
0 commit comments