Skip to content

Commit dd4be93

Browse files
committed
Merge branch 'antonis/3859-newCaptureFeedbackAPI' into antonis/3859-newCaptureFeedbackAPI-hint
2 parents bd027d8 + 2bb104b commit dd4be93

File tree

4 files changed

+71
-3
lines changed

4 files changed

+71
-3
lines changed

CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
- Adds new `captureFeedback` and deprecates the `captureUserFeedback` API ([#4320](https://github.com/getsentry/sentry-react-native/pull/4320))
1414

15-
1615
```jsx
1716
import * as Sentry from "@sentry/react-native";
1817

packages/core/src/js/client.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
Outcome,
1010
SeverityLevel,
1111
TransportMakeRequestResponse,
12+
UserFeedback,
1213
} from '@sentry/types';
1314
import { dateTimestampInSeconds, logger, SentryError } from '@sentry/utils';
1415
import { Alert } from 'react-native';
@@ -19,7 +20,7 @@ import { getDefaultSidecarUrl } from './integrations/spotlight';
1920
import type { ReactNativeClientOptions } from './options';
2021
import type { mobileReplayIntegration } from './replay/mobilereplay';
2122
import { MOBILE_REPLAY_INTEGRATION_NAME } from './replay/mobilereplay';
22-
import { items } from './utils/envelope';
23+
import { createUserFeedbackEnvelope, items } from './utils/envelope';
2324
import { ignoreRequireCycleLogs } from './utils/ignorerequirecyclelogs';
2425
import { mergeOutcomes } from './utils/outcome';
2526
import { ReactNativeLibraries } from './utils/rnlibraries';
@@ -82,6 +83,20 @@ export class ReactNativeClient extends BaseClient<ReactNativeClientOptions> {
8283
});
8384
}
8485

86+
/**
87+
* Sends user feedback to Sentry.
88+
* @deprecated Use `Sentry.captureFeedback` instead.
89+
*/
90+
public captureUserFeedback(feedback: UserFeedback): void {
91+
const envelope = createUserFeedbackEnvelope(feedback, {
92+
metadata: this._options._metadata,
93+
dsn: this.getDsn(),
94+
tunnel: undefined,
95+
});
96+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
97+
this.sendEnvelope(envelope);
98+
}
99+
85100
/**
86101
* @inheritdoc
87102
*/

packages/core/test/client.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
envelopeItems,
2020
firstArg,
2121
getMockSession,
22+
getMockUserFeedback,
2223
getSyncPromiseRejectOnFirstCall,
2324
} from './testutils';
2425

@@ -186,6 +187,15 @@ describe('Tests ReactNativeClient', () => {
186187
expect(mockTransport.send).not.toBeCalled();
187188
});
188189

190+
test('captureUserFeedback does not call transport when enabled false', () => {
191+
const mockTransport = createMockTransport();
192+
const client = createDisabledClientWith(mockTransport);
193+
194+
client.captureUserFeedback(getMockUserFeedback());
195+
196+
expect(mockTransport.send).not.toBeCalled();
197+
});
198+
189199
function createDisabledClientWith(transport: Transport) {
190200
return new ReactNativeClient({
191201
...DEFAULT_OPTIONS,
@@ -279,6 +289,38 @@ describe('Tests ReactNativeClient', () => {
279289
});
280290
});
281291

292+
describe('UserFeedback', () => {
293+
test('sends UserFeedback to native Layer', () => {
294+
const mockTransportSend: jest.Mock = jest.fn(() => Promise.resolve());
295+
const client = new ReactNativeClient({
296+
...DEFAULT_OPTIONS,
297+
dsn: EXAMPLE_DSN,
298+
transport: () => ({
299+
send: mockTransportSend,
300+
flush: jest.fn(),
301+
}),
302+
});
303+
304+
client.captureUserFeedback({
305+
comments: 'Test Comments',
306+
307+
name: 'Test User',
308+
event_id: 'testEvent123',
309+
});
310+
311+
expect(mockTransportSend.mock.calls[0][firstArg][envelopeHeader].event_id).toEqual('testEvent123');
312+
expect(mockTransportSend.mock.calls[0][firstArg][envelopeItems][0][envelopeItemHeader].type).toEqual(
313+
'user_report',
314+
);
315+
expect(mockTransportSend.mock.calls[0][firstArg][envelopeItems][0][envelopeItemPayload]).toEqual({
316+
comments: 'Test Comments',
317+
318+
name: 'Test User',
319+
event_id: 'testEvent123',
320+
});
321+
});
322+
});
323+
282324
describe('attachStacktrace', () => {
283325
let mockTransportSend: jest.Mock;
284326
let client: ReactNativeClient;
@@ -375,6 +417,11 @@ describe('Tests ReactNativeClient', () => {
375417
client.captureSession(getMockSession());
376418
expect(getSdkInfoFrom(mockTransportSend)).toStrictEqual(expectedSdkInfo);
377419
});
420+
421+
test('send SdkInfo in the user feedback envelope header', () => {
422+
client.captureUserFeedback(getMockUserFeedback());
423+
expect(getSdkInfoFrom(mockTransportSend)).toStrictEqual(expectedSdkInfo);
424+
});
378425
});
379426

380427
describe('event data enhancement', () => {

packages/core/test/testutils.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Session, Transport } from '@sentry/types';
1+
import type { Session, Transport, UserFeedback } from '@sentry/types';
22
import { rejectedSyncPromise } from '@sentry/utils';
33

44
export type MockInterface<T> = {
@@ -36,6 +36,13 @@ export const getMockSession = (): Session => ({
3636
}),
3737
});
3838

39+
export const getMockUserFeedback = (): UserFeedback => ({
40+
comments: 'comments_test_value',
41+
email: 'email_test_value',
42+
name: 'name_test_value',
43+
event_id: 'event_id_test_value',
44+
});
45+
3946
export const getSyncPromiseRejectOnFirstCall = <Y extends any[]>(reason: unknown): jest.Mock => {
4047
let shouldSyncReject = true;
4148
return jest.fn((..._args: Y) => {

0 commit comments

Comments
 (0)