@@ -293,59 +293,6 @@ class SentryClientTest {
293293 assertEquals(SentryLevel .DEBUG , sentEvent!! .level)
294294 }
295295
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-
349296 @Test
350297 fun `when event has release, value from options not applied` () {
351298 val event = SentryEvent ()
@@ -2804,6 +2751,8 @@ class SentryClientTest {
28042751 verify(fixture.transport).send(anyOrNull(), anyOrNull())
28052752 }
28062753
2754+ // region Replay
2755+
28072756 @Test
28082757 fun `when captureReplayEvent, envelope is sent` () {
28092758 val sut = fixture.getSut()
@@ -3044,6 +2993,194 @@ class SentryClientTest {
30442993 )
30452994 }
30462995
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+
30473184 private fun givenScopeWithStartedSession (errored : Boolean = false, crashed : Boolean = false): IScope {
30483185 val scope = createScope(fixture.sentryOptions)
30493186 scope.startSession()
0 commit comments