Skip to content

Commit

Permalink
Add expo, hermes version, react_native_version to react_native_context (
Browse files Browse the repository at this point in the history
  • Loading branch information
krystofwoldrich authored May 12, 2023
1 parent c882ec1 commit 1652677
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 8 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Features

- Add `expo`, `react_native_version` and `hermes_version` to React Native Context ([#3050](https://github.com/getsentry/sentry-react-native/pull/3050))

## 5.4.2

### Fixes
Expand Down
18 changes: 17 additions & 1 deletion src/js/integrations/reactnativeinfo.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import type { Context, Event, EventHint, EventProcessor, Integration } from '@sentry/types';

import { isFabricEnabled, isHermesEnabled, isTurboModuleEnabled } from '../utils/environment';
import {
getHermesVersion,
getReactNativeVersion,
isExpo,
isFabricEnabled,
isHermesEnabled,
isTurboModuleEnabled,
} from '../utils/environment';
import type { ReactNativeError } from './debugsymbolicator';

export interface ReactNativeContext extends Context {
js_engine?: string;
turbo_module: boolean;
fabric: boolean;
expo: boolean;
hermes_version?: string;
react_native_version: string;
component_stack?: string;
}

Expand All @@ -32,10 +42,16 @@ export class ReactNativeInfo implements Integration {
const reactNativeContext: ReactNativeContext = {
turbo_module: isTurboModuleEnabled(),
fabric: isFabricEnabled(),
react_native_version: getReactNativeVersion(),
expo: isExpo(),
};

if (isHermesEnabled()) {
reactNativeContext.js_engine = 'hermes';
const hermesVersion = getHermesVersion();
if (hermesVersion) {
reactNativeContext.hermes_version = getHermesVersion();
}
} else if (reactNativeError?.jsEngine) {
reactNativeContext.js_engine = reactNativeError.jsEngine;
}
Expand Down
21 changes: 21 additions & 0 deletions src/js/utils/environment.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { version as RNV } from 'react-native/Libraries/Core/ReactNativeVersion';

import { RN_GLOBAL_OBJ } from '../utils/worldwide';

/** Checks if the React Native Hermes engine is running */
Expand All @@ -14,3 +16,22 @@ export function isTurboModuleEnabled(): boolean {
export function isFabricEnabled(): boolean {
return RN_GLOBAL_OBJ.nativeFabricUIManager != null;
}

/** Returns React Native Version as semver string */
export function getReactNativeVersion(): string {
return `${RNV.major}.${RNV.minor}.${RNV.patch}${RNV.prerelease != null ? `-${RNV.prerelease}` : ''}`;
}

/** Checks if Expo is present in the runtime */
export function isExpo(): boolean {
return RN_GLOBAL_OBJ.expo != null;
}

/** Returns Hermes Version if hermes is present in the runtime */
export function getHermesVersion(): string | undefined {
return (
RN_GLOBAL_OBJ.HermesInternal &&
RN_GLOBAL_OBJ.HermesInternal.getRuntimeProperties &&
RN_GLOBAL_OBJ.HermesInternal.getRuntimeProperties()['OSS Release Version']
);
}
5 changes: 4 additions & 1 deletion src/js/utils/worldwide.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import type { ErrorUtils } from 'react-native/types';
export interface ReactNativeInternalGlobal extends InternalGlobal {
__sentry_rn_v4_registered?: boolean;
__sentry_rn_v5_registered?: boolean;
HermesInternal: unknown;
HermesInternal?: {
getRuntimeProperties?: () => Record<string, string | undefined>;
};
Promise: unknown;
__turboModuleProxy: unknown;
nativeFabricUIManager: unknown;
ErrorUtils?: ErrorUtils;
expo: unknown;
}

/** Get's the global object for the current JavaScript runtime */
Expand Down
26 changes: 22 additions & 4 deletions test/integrations/reactnativeinfo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,27 @@ import { ReactNativeInfo } from '../../src/js/integrations/reactnativeinfo';
let mockedIsHermesEnabled: jest.Mock<boolean, []>;
let mockedIsTurboModuleEnabled: jest.Mock<boolean, []>;
let mockedIsFabricEnabled: jest.Mock<boolean, []>;
let mockedGetReactNativeVersion: jest.Mock<string, []>;
let mockedGetHermesVersion: jest.Mock<string | undefined, []>;
let mockedIsExpo: jest.Mock<boolean, []>;

jest.mock('../../src/js/utils/environment', () => ({
isHermesEnabled: () => mockedIsHermesEnabled(),
isTurboModuleEnabled: () => mockedIsTurboModuleEnabled(),
isFabricEnabled: () => mockedIsFabricEnabled(),
getReactNativeVersion: () => mockedGetReactNativeVersion(),
getHermesVersion: () => mockedGetHermesVersion(),
isExpo: () => mockedIsExpo(),
}));

describe('React Native Info', () => {
beforeEach(() => {
mockedIsHermesEnabled = jest.fn().mockReturnValue(false);
mockedIsHermesEnabled = jest.fn().mockReturnValue(true);
mockedIsTurboModuleEnabled = jest.fn().mockReturnValue(false);
mockedIsFabricEnabled = jest.fn().mockReturnValue(false);
mockedGetReactNativeVersion = jest.fn().mockReturnValue('1000.0.0-test');
mockedGetHermesVersion = jest.fn().mockReturnValue(undefined);
mockedIsExpo = jest.fn().mockReturnValue(false);
});

afterEach(() => {
Expand All @@ -39,20 +48,28 @@ describe('React Native Info', () => {
react_native_context: <ReactNativeContext>{
turbo_module: false,
fabric: false,
js_engine: 'hermes',
react_native_version: '1000.0.0-test',
expo: false,
},
},
tags: {
hermes: 'true',
},
});
});

it('adds hermes tag and js_engine to context if hermes enabled', async () => {
mockedIsHermesEnabled = jest.fn().mockReturnValue(true);
mockedGetHermesVersion = jest.fn().mockReturnValue('for RN 999.0.0');
const actualEvent = await executeIntegrationFor({}, {});

expectMocksToBeCalledOnce();
expect(actualEvent?.tags?.hermes).toEqual('true');
expect((actualEvent?.contexts?.react_native_context as ReactNativeContext | undefined)?.js_engine).toEqual(
'hermes',
);
expect(actualEvent?.contexts?.react_native_context).toEqual(expect.objectContaining({
js_engine: 'hermes',
hermes_version: 'for RN 999.0.0',
}));
});

it('does not override existing hermes tag', async () => {
Expand All @@ -69,6 +86,7 @@ describe('React Native Info', () => {
});

it('adds engine from rn error', async () => {
mockedIsHermesEnabled = jest.fn().mockReturnValue(false);
const mockedHint: EventHint = {
originalException: <ReactNativeError>{
jsEngine: 'test_engine',
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"extends": "./node_modules/@sentry/typescript/tsconfig.json",
"include": [
"src/js/*.ts"
"src/js/*.ts",
"typings/*.d.ts"
],
"exclude": [
"node_modules"
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"src/js/*.ts",
"src/js/*.tsx",
"test/**/*.ts",
"test/**/*.tsx"
"test/**/*.tsx",
"typings/*.d.ts"
],
"exclude": [
"dist"
Expand Down
8 changes: 8 additions & 0 deletions typings/react-native.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
declare module 'react-native/Libraries/Core/ReactNativeVersion' {
export const version: {
major: number;
minor: number;
patch: number;
prerelease: string | null;
};
};

0 comments on commit 1652677

Please sign in to comment.