Skip to content

Commit 4d9f7e3

Browse files
committed
Fixes
1 parent 25475dc commit 4d9f7e3

2 files changed

Lines changed: 75 additions & 30 deletions

File tree

packages/core/src/js/tracing/expoRouterIntegration.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import type { Client, Integration } from '@sentry/core';
2+
23
import { debug } from '@sentry/core';
34

4-
import {
5-
getReactNavigationIntegration,
6-
reactNavigationIntegration,
7-
} from './reactnavigation';
5+
import { getReactNavigationIntegration, reactNavigationIntegration } from './reactnavigation';
86

97
export const INTEGRATION_NAME = 'ExpoRouter';
108

@@ -19,7 +17,7 @@ interface ExpoRouterStore {
1917
navigationRef?: ExpoRouterNavigationRef;
2018
}
2119

22-
type ExpoRouterIntegrationOptions = NonNullable<Parameters<typeof reactNavigationIntegration>[0]>;
20+
type ExpoRouterIntegrationOptions = Parameters<typeof reactNavigationIntegration>[0];
2321

2422
/**
2523
* Integration that connects Expo Router with `reactNavigationIntegration` without
@@ -32,15 +30,22 @@ type ExpoRouterIntegrationOptions = NonNullable<Parameters<typeof reactNavigatio
3230
* });
3331
* ```
3432
*/
35-
export const expoRouterIntegration = (options: Partial<ExpoRouterIntegrationOptions> = {}): Integration => {
33+
export const expoRouterIntegration = (options: ExpoRouterIntegrationOptions = {}): Integration => {
3634
let pollTimer: ReturnType<typeof setTimeout> | undefined;
3735

38-
const setup = (client: Client): void => {
36+
const afterAllSetup = (client: Client): void => {
3937
const store = tryGetExpoRouterStore();
40-
if (!store?.navigationRef) {
38+
if (!store) {
4139
// expo-router not installed
4240
return;
4341
}
42+
if (!store.navigationRef) {
43+
debug.warn(
44+
`${INTEGRATION_NAME} Found expo-router router-store but it does not expose a \`navigationRef\`. ` +
45+
`This likely means the installed expo-router version is incompatible with this integration.`,
46+
);
47+
return;
48+
}
4449

4550
// reuse the user's reactNavigationIntegration if they registered one manually.
4651
// Otherwise, create and add one.
@@ -86,7 +91,7 @@ export const expoRouterIntegration = (options: Partial<ExpoRouterIntegrationOpti
8691

8792
return {
8893
name: INTEGRATION_NAME,
89-
setup,
94+
afterAllSetup,
9095
};
9196
};
9297

packages/core/test/tracing/expoRouterIntegration.test.ts

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -59,25 +59,49 @@ describe('expoRouterIntegration', () => {
5959
const { client, addIntegration } = createMockClient();
6060

6161
const integ = integration();
62-
integ.setup?.(client);
62+
integ.afterAllSetup?.(client);
6363

6464
expect(integ.name).toBe('ExpoRouter');
6565
expect(addIntegration).not.toHaveBeenCalled();
6666
});
6767
});
6868

69+
describe('expo-router router-store found but navigationRef missing', () => {
70+
it('warns and does not add the integration', () => {
71+
jest.doMock(EXPO_ROUTER_STORE_MODULE, () => ({ store: {} }), { virtual: true });
72+
73+
const { debug } = require('@sentry/core');
74+
const warnSpy = jest.spyOn(debug, 'warn').mockImplementation(() => undefined);
75+
76+
const { expoRouterIntegration: integration } = require('../../src/js/tracing/expoRouterIntegration');
77+
const { client, addIntegration } = createMockClient();
78+
79+
const integ = integration();
80+
integ.afterAllSetup?.(client);
81+
82+
expect(addIntegration).not.toHaveBeenCalled();
83+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('navigationRef'));
84+
85+
warnSpy.mockRestore();
86+
});
87+
});
88+
6989
describe('expo-router installed, navigationRef pre-populated', () => {
7090
it('registers the navigation container immediately', () => {
7191
const container = createMockNavigationContainer();
72-
jest.doMock(EXPO_ROUTER_STORE_MODULE, () => ({
73-
store: { navigationRef: { current: container } },
74-
}), { virtual: true });
92+
jest.doMock(
93+
EXPO_ROUTER_STORE_MODULE,
94+
() => ({
95+
store: { navigationRef: { current: container } },
96+
}),
97+
{ virtual: true },
98+
);
7599

76100
const { expoRouterIntegration: integration } = require('../../src/js/tracing/expoRouterIntegration');
77101
const { client, addIntegration } = createMockClient();
78102

79103
const integ = integration();
80-
integ.setup?.(client);
104+
integ.afterAllSetup?.(client);
81105

82106
expect(addIntegration).toHaveBeenCalledTimes(1);
83107
const added = addIntegration.mock.calls[0][0];
@@ -92,15 +116,19 @@ describe('expoRouterIntegration', () => {
92116
it('polls until ref.current is populated, then registers', () => {
93117
const container = createMockNavigationContainer();
94118
const navigationRef: { current: MockNavigationContainer | null } = { current: null };
95-
jest.doMock(EXPO_ROUTER_STORE_MODULE, () => ({
96-
store: { navigationRef },
97-
}), { virtual: true });
119+
jest.doMock(
120+
EXPO_ROUTER_STORE_MODULE,
121+
() => ({
122+
store: { navigationRef },
123+
}),
124+
{ virtual: true },
125+
);
98126

99127
const { expoRouterIntegration: integration } = require('../../src/js/tracing/expoRouterIntegration');
100128
const { client, addIntegration } = createMockClient();
101129

102130
const integ = integration();
103-
integ.setup?.(client);
131+
integ.afterAllSetup?.(client);
104132

105133
// nothing registered yet
106134
expect(container.addListener).not.toHaveBeenCalled();
@@ -120,15 +148,19 @@ describe('expoRouterIntegration', () => {
120148

121149
it('stops polling after the timeout if ref is never populated', () => {
122150
const navigationRef: { current: unknown } = { current: null };
123-
jest.doMock(EXPO_ROUTER_STORE_MODULE, () => ({
124-
store: { navigationRef },
125-
}), { virtual: true });
151+
jest.doMock(
152+
EXPO_ROUTER_STORE_MODULE,
153+
() => ({
154+
store: { navigationRef },
155+
}),
156+
{ virtual: true },
157+
);
126158

127159
const { expoRouterIntegration: integration } = require('../../src/js/tracing/expoRouterIntegration');
128160
const { client, closeHandlers } = createMockClient();
129161

130162
const integ = integration();
131-
integ.setup?.(client);
163+
integ.afterAllSetup?.(client);
132164
const timersAfterSetup = jest.getTimerCount();
133165

134166
jest.advanceTimersByTime(6_000);
@@ -141,9 +173,13 @@ describe('expoRouterIntegration', () => {
141173
describe('user already added reactNavigationIntegration', () => {
142174
it('reuses the existing integration and does not add a duplicate', () => {
143175
const container = createMockNavigationContainer();
144-
jest.doMock(EXPO_ROUTER_STORE_MODULE, () => ({
145-
store: { navigationRef: { current: container } },
146-
}), { virtual: true });
176+
jest.doMock(
177+
EXPO_ROUTER_STORE_MODULE,
178+
() => ({
179+
store: { navigationRef: { current: container } },
180+
}),
181+
{ virtual: true },
182+
);
147183

148184
const { expoRouterIntegration: integration } = require('../../src/js/tracing/expoRouterIntegration');
149185
const { client, addIntegration, getIntegrationByName } = createMockClient();
@@ -156,7 +192,7 @@ describe('expoRouterIntegration', () => {
156192
);
157193

158194
const integ = integration();
159-
integ.setup?.(client);
195+
integ.afterAllSetup?.(client);
160196

161197
expect(addIntegration).not.toHaveBeenCalled();
162198
expect(existingRegister).toHaveBeenCalledWith({ current: container });
@@ -166,16 +202,20 @@ describe('expoRouterIntegration', () => {
166202
describe('cleanup', () => {
167203
it('clears the polling timer when the client closes', () => {
168204
const navigationRef: { current: unknown } = { current: null };
169-
jest.doMock(EXPO_ROUTER_STORE_MODULE, () => ({
170-
store: { navigationRef },
171-
}), { virtual: true });
205+
jest.doMock(
206+
EXPO_ROUTER_STORE_MODULE,
207+
() => ({
208+
store: { navigationRef },
209+
}),
210+
{ virtual: true },
211+
);
172212

173213
const { expoRouterIntegration: integration } = require('../../src/js/tracing/expoRouterIntegration');
174214
const { client, closeHandlers } = createMockClient();
175215

176216
const baselineTimers = jest.getTimerCount();
177217
const integ = integration();
178-
integ.setup?.(client);
218+
integ.afterAllSetup?.(client);
179219
const timersAfterSetup = jest.getTimerCount();
180220

181221
// Setup schedules exactly one polling timer

0 commit comments

Comments
 (0)