Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
Sentry.wrapExpoAsset(Asset);
```
- Adds tags with Expo Updates context variables to make them searchable and filterable ([#5788](https://github.com/getsentry/sentry-react-native/pull/5788))
- Automatically capture a warning event when Expo Updates performs an emergency launch ([#5794](https://github.com/getsentry/sentry-react-native/pull/5794))
- Adds environment configuration in the Expo config plugin. This can be set with the `SENTRY_ENVIRONMENT` env variable or in `sentry.options.json` ([#5796](https://github.com/getsentry/sentry-react-native/pull/5796))
```json
["@sentry/react-native/expo", {
Expand Down
24 changes: 24 additions & 0 deletions packages/core/src/js/integrations/expocontext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const expoContextIntegration = (): Integration => {
}

setExpoUpdatesNativeContext();
captureEmergencyLaunchEvent(client);
});
}

Expand All @@ -37,6 +38,29 @@ export const expoContextIntegration = (): Integration => {
}
}

function captureEmergencyLaunchEvent(client: ReactNativeClient): void {
if (!isExpo() || isExpoGo()) {
return;
}

const updatesContext = getExpoUpdatesContextCached();
if (!updatesContext.is_emergency_launch) {
return;
}

const message = updatesContext.emergency_launch_reason
? `Expo Updates emergency launch: ${updatesContext.emergency_launch_reason}`
: 'Expo Updates emergency launch';

client.captureEvent({
level: 'warning',
message,
tags: {
'expo.updates.emergency_launch': 'true',
},
});
}

function processEvent(event: Event): Event {
if (!isExpo()) {
return event;
Expand Down
107 changes: 106 additions & 1 deletion packages/core/test/integrations/expocontext.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as environment from '../../src/js/utils/environment';
import type { ExpoUpdates } from '../../src/js/utils/expoglobalobject';
import { getExpoDevice } from '../../src/js/utils/expomodules';
import * as expoModules from '../../src/js/utils/expomodules';
import { setupTestClient } from '../mocks/client';
import { setupTestClient, TestClient } from '../mocks/client';
import { NATIVE } from '../mockWrapper';

jest.mock('../../src/js/wrapper', () => jest.requireActual('../mockWrapper'));
Expand Down Expand Up @@ -80,6 +80,111 @@ describe('Expo Context Integration', () => {
});
});

describe('Emergency Launch Event', () => {
it('captures a warning event on emergency launch', () => {
jest.spyOn(environment, 'isExpo').mockReturnValue(true);
jest.spyOn(environment, 'isExpoGo').mockReturnValue(false);
jest.spyOn(expoModules, 'getExpoUpdates').mockReturnValue({
isEmergencyLaunch: true,
emergencyLaunchReason: 'The previous launch failed with a fatal error.',
});

setupTestClient({ enableNative: true, integrations: [expoContextIntegration()] });

const capturedEvent = TestClient.instance?.eventQueue.find(
e => e.tags?.['expo.updates.emergency_launch'] === 'true',
);

expect(capturedEvent).toBeDefined();
expect(capturedEvent?.level).toBe('warning');
expect(capturedEvent?.message).toBe(
'Expo Updates emergency launch: The previous launch failed with a fatal error.',
);
});

it('captures a warning event without reason when reason is missing', () => {
jest.spyOn(environment, 'isExpo').mockReturnValue(true);
jest.spyOn(environment, 'isExpoGo').mockReturnValue(false);
jest.spyOn(expoModules, 'getExpoUpdates').mockReturnValue({
isEmergencyLaunch: true,
});

setupTestClient({ enableNative: true, integrations: [expoContextIntegration()] });

const capturedEvent = TestClient.instance?.eventQueue.find(
e => e.tags?.['expo.updates.emergency_launch'] === 'true',
);

expect(capturedEvent).toBeDefined();
expect(capturedEvent?.level).toBe('warning');
expect(capturedEvent?.message).toBe('Expo Updates emergency launch');
});

it('does not capture event when not emergency launch', () => {
jest.spyOn(environment, 'isExpo').mockReturnValue(true);
jest.spyOn(environment, 'isExpoGo').mockReturnValue(false);
jest.spyOn(expoModules, 'getExpoUpdates').mockReturnValue({
isEmergencyLaunch: false,
});

setupTestClient({ enableNative: true, integrations: [expoContextIntegration()] });

const capturedEvent = TestClient.instance?.eventQueue.find(
e => e.tags?.['expo.updates.emergency_launch'] === 'true',
);

expect(capturedEvent).toBeUndefined();
});

it('does not capture event when not expo', () => {
jest.spyOn(environment, 'isExpo').mockReturnValue(false);
jest.spyOn(environment, 'isExpoGo').mockReturnValue(false);
jest.spyOn(expoModules, 'getExpoUpdates').mockReturnValue({
isEmergencyLaunch: true,
});

setupTestClient({ enableNative: true, integrations: [expoContextIntegration()] });

const capturedEvent = TestClient.instance?.eventQueue.find(
e => e.tags?.['expo.updates.emergency_launch'] === 'true',
);

expect(capturedEvent).toBeUndefined();
});

it('does not capture event when in Expo Go', () => {
jest.spyOn(environment, 'isExpo').mockReturnValue(true);
jest.spyOn(environment, 'isExpoGo').mockReturnValue(true);
jest.spyOn(expoModules, 'getExpoUpdates').mockReturnValue({
isEmergencyLaunch: true,
});

setupTestClient({ enableNative: true, integrations: [expoContextIntegration()] });

const capturedEvent = TestClient.instance?.eventQueue.find(
e => e.tags?.['expo.updates.emergency_launch'] === 'true',
);

expect(capturedEvent).toBeUndefined();
});

it('does not capture event when native is disabled', () => {
jest.spyOn(environment, 'isExpo').mockReturnValue(true);
jest.spyOn(environment, 'isExpoGo').mockReturnValue(false);
jest.spyOn(expoModules, 'getExpoUpdates').mockReturnValue({
isEmergencyLaunch: true,
});

setupTestClient({ enableNative: false, integrations: [expoContextIntegration()] });

const capturedEvent = TestClient.instance?.eventQueue.find(
e => e.tags?.['expo.updates.emergency_launch'] === 'true',
);

expect(capturedEvent).toBeUndefined();
});
});

describe('Non Expo App', () => {
beforeEach(() => {
jest.spyOn(environment, 'isExpo').mockReturnValue(false);
Expand Down
Loading