Skip to content

Commit 5d07797

Browse files
fix tests
1 parent ae79ef2 commit 5d07797

File tree

3 files changed

+50
-53
lines changed

3 files changed

+50
-53
lines changed

src/js/utils/sentryeventemitter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export function createSentryEventEmitter(
8585
addListener,
8686
removeListener,
8787
once(eventType: NewFrameEventName, listener: (event: NewFrameEvent) => void) {
88-
fallbackEventEmitter?.startListenerAsync();
88+
fallbackEventEmitter?.onceNewFrame();
8989

9090
const tmpListener = (event: NewFrameEvent): void => {
9191
listener(event);

src/js/utils/sentryeventemitterfallback.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { logger, timestampInSeconds } from '@sentry/utils';
22

33
import { NATIVE } from '../wrapper';
4-
import type { NewFrameEvent } from './sentryeventemitter';
4+
import type { NewFrameEvent, SentryEventEmitter } from './sentryeventemitter';
55
import { createSentryEventEmitter, NewFrameEventName } from './sentryeventemitter';
66

7+
export const FALLBACK_TIMEOUT_MS = 10_000;
8+
79
export type FallBackNewFrameEvent = { newFrameTimestampInSeconds: number; isFallback?: boolean };
810
export interface SentryEventEmitterFallback {
911
/**
@@ -17,9 +19,10 @@ export interface SentryEventEmitterFallback {
1719
/**
1820
* Creates emitter that allows to listen to UI Frame events when ready.
1921
*/
20-
export function createSentryFallbackEventEmitter(): SentryEventEmitterFallback {
21-
const emitter = createSentryEventEmitter();
22-
22+
export function createSentryFallbackEventEmitter(
23+
emitter: SentryEventEmitter = createSentryEventEmitter(),
24+
fallbackTimeoutMs = FALLBACK_TIMEOUT_MS,
25+
): SentryEventEmitterFallback {
2326
let fallbackTimeout: ReturnType<typeof setTimeout> | undefined;
2427
let animationFrameTimestampSeconds: number | undefined;
2528
let nativeNewFrameTimestampSeconds: number | undefined;
@@ -68,7 +71,7 @@ export function createSentryFallbackEventEmitter(): SentryEventEmitterFallback {
6871
};
6972
fallbackTimeout = setTimeout(() => {
7073
if (nativeNewFrameTimestampSeconds) {
71-
logger.log('Native fallback timestamp received.');
74+
logger.log('Native event emitter did not reply in time');
7275
return listener({
7376
newFrameTimestampInSeconds: nativeNewFrameTimestampSeconds,
7477
isFallback: true,
@@ -83,7 +86,7 @@ export function createSentryFallbackEventEmitter(): SentryEventEmitterFallback {
8386
emitter.removeListener(NewFrameEventName, internalListener);
8487
logger.error('Failed to receive any fallback timestamp.');
8588
}
86-
}, 3_000);
89+
}, fallbackTimeoutMs);
8790

8891
getNativeNewFrameTimestampSeconds();
8992
getAnimationFrameTimestampSeconds();

test/utils/sentryeventemitterfallback.test.ts

Lines changed: 40 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,7 @@
1-
import { DeviceEventEmitter } from 'react-native';
2-
31
import { NewFrameEventName } from '../../src/js/utils/sentryeventemitter';
42
import { createSentryFallbackEventEmitter } from '../../src/js/utils/sentryeventemitterfallback';
53

64
// Mock dependencies
7-
8-
jest.mock('react-native', () => {
9-
return {
10-
DeviceEventEmitter: {
11-
addListener: jest.fn(),
12-
emit: jest.fn(),
13-
},
14-
Platform: {
15-
OS: 'ios',
16-
},
17-
};
18-
});
19-
205
jest.mock('../../src/js/utils/environment', () => ({
216
isTurboModuleEnabled: () => false,
227
}));
@@ -48,12 +33,6 @@ describe('SentryEventEmitterFallback', () => {
4833
NATIVE.getNewScreenTimeToDisplay = jest.fn();
4934
});
5035

51-
it('should initialize and add a listener', () => {
52-
emitter.initAsync();
53-
54-
expect(DeviceEventEmitter.addListener).toHaveBeenCalledWith(NewFrameEventName, expect.any(Function));
55-
});
56-
5736
it('should start listener and use fallback when native call returned undefined/null', async () => {
5837
jest.useFakeTimers();
5938
const spy = jest.spyOn(require('@sentry/utils'), 'timestampInSeconds');
@@ -62,19 +41,20 @@ describe('SentryEventEmitterFallback', () => {
6241

6342
(NATIVE.getNewScreenTimeToDisplay as jest.Mock).mockReturnValue(Promise.resolve());
6443

65-
emitter.startListenerAsync();
44+
const listener = jest.fn();
45+
emitter.onceNewFrame(listener);
6646

6747
// Wait for the next event loop to allow startListenerAsync to call NATIVE.getNewScreenTimeToDisplay
6848
await Promise.resolve();
6949

7050
await expect(NATIVE.getNewScreenTimeToDisplay).toHaveBeenCalled();
71-
expect(logger.error).not.toHaveBeenCalledWith('Failed to recceive Native fallback timestamp.', expect.any(Error));
51+
expect(logger.error).not.toHaveBeenCalledWith('Failed to receive Native fallback timestamp.', expect.any(Error));
7252

7353
// Simulate retries and timer
7454
jest.runAllTimers();
7555

7656
// Ensure fallback event is emitted
77-
expect(DeviceEventEmitter.emit).toHaveBeenCalledWith(NewFrameEventName, {
57+
expect(listener).toHaveBeenCalledWith({
7858
newFrameTimestampInSeconds: fallbackTime,
7959
isFallback: true,
8060
});
@@ -90,23 +70,24 @@ describe('SentryEventEmitterFallback', () => {
9070

9171
(NATIVE.getNewScreenTimeToDisplay as jest.Mock).mockRejectedValue(new Error('Failed'));
9272

93-
emitter.startListenerAsync();
94-
9573
const spy = jest.spyOn(require('@sentry/utils'), 'timestampInSeconds');
9674
const fallbackTime = Date.now() / 1000;
9775
spy.mockReturnValue(fallbackTime);
9876

77+
const listener = jest.fn();
78+
emitter.onceNewFrame(listener);
79+
9980
// Wait for the next event loop to allow startListenerAsync to call NATIVE.getNewScreenTimeToDisplay
10081
await Promise.resolve();
10182

10283
await expect(NATIVE.getNewScreenTimeToDisplay).toHaveBeenCalled();
103-
expect(logger.error).toHaveBeenCalledWith('Failed to recceive Native fallback timestamp.', expect.any(Error));
84+
expect(logger.error).toHaveBeenCalledWith('Failed to receive Native fallback timestamp.', expect.any(Error));
10485

10586
// Simulate retries and timer
10687
jest.runAllTimers();
10788

10889
// Ensure fallback event is emitted
109-
expect(DeviceEventEmitter.emit).toHaveBeenCalledWith(NewFrameEventName, {
90+
expect(listener).toHaveBeenCalledWith({
11091
newFrameTimestampInSeconds: fallbackTime,
11192
isFallback: true,
11293
});
@@ -125,19 +106,20 @@ describe('SentryEventEmitterFallback', () => {
125106

126107
(NATIVE.getNewScreenTimeToDisplay as jest.Mock).mockRejectedValue(new Error('Failed'));
127108

128-
emitter.startListenerAsync();
109+
const listener = jest.fn();
110+
emitter.onceNewFrame(listener);
129111

130112
// Wait for the next event loop to allow startListenerAsync to call NATIVE.getNewScreenTimeToDisplay
131113
await Promise.resolve();
132114

133115
await expect(NATIVE.getNewScreenTimeToDisplay).toHaveBeenCalled();
134-
expect(logger.error).toHaveBeenCalledWith('Failed to recceive Native fallback timestamp.', expect.any(Error));
116+
expect(logger.error).toHaveBeenCalledWith('Failed to receive Native fallback timestamp.', expect.any(Error));
135117

136118
// Simulate retries and timer
137119
jest.runAllTimers();
138120

139121
// Ensure fallback event is emitted
140-
expect(DeviceEventEmitter.emit).toHaveBeenCalledWith(NewFrameEventName, {
122+
expect(listener).toHaveBeenCalledWith({
141123
newFrameTimestampInSeconds: fallbackTime,
142124
isFallback: true,
143125
});
@@ -156,7 +138,8 @@ describe('SentryEventEmitterFallback', () => {
156138

157139
NATIVE.getNewScreenTimeToDisplay = () => Promise.resolve(null);
158140

159-
emitter.startListenerAsync();
141+
const listener = jest.fn();
142+
emitter.onceNewFrame(listener);
160143

161144
// Wait for the next event loop to allow startListenerAsync to call NATIVE.getNewScreenTimeToDisplay
162145
await Promise.resolve();
@@ -165,7 +148,7 @@ describe('SentryEventEmitterFallback', () => {
165148
jest.runAllTimers();
166149

167150
// Ensure fallback event is emitted
168-
expect(DeviceEventEmitter.emit).toHaveBeenCalledWith(NewFrameEventName, {
151+
expect(listener).toHaveBeenCalledWith({
169152
newFrameTimestampInSeconds: fallbackTime,
170153
isFallback: true,
171154
});
@@ -181,21 +164,19 @@ describe('SentryEventEmitterFallback', () => {
181164

182165
(NATIVE.getNewScreenTimeToDisplay as jest.Mock).mockResolvedValueOnce(nativeTimestamp);
183166

184-
emitter.startListenerAsync();
167+
const listener = jest.fn();
168+
emitter.onceNewFrame(listener);
185169

186170
expect(NATIVE.getNewScreenTimeToDisplay).toHaveBeenCalled();
187171
});
188172

189173
it('should not emit if original event emitter was called', async () => {
190174
jest.useFakeTimers();
191175

192-
const mockAddListener = jest.fn();
193-
DeviceEventEmitter.addListener = mockAddListener;
194-
195176
// Capture the callback passed to addListener
196177
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/ban-types
197178
let callback: Function = () => {};
198-
mockAddListener.mockImplementationOnce((eventName, cb) => {
179+
const mockOnce = jest.fn().mockImplementationOnce((eventName, cb) => {
199180
if (eventName === NewFrameEventName) {
200181
callback = cb;
201182
}
@@ -204,24 +185,40 @@ describe('SentryEventEmitterFallback', () => {
204185
};
205186
});
206187

188+
emitter = createSentryFallbackEventEmitter({
189+
addListener: jest.fn(),
190+
initAsync: jest.fn(),
191+
closeAllAsync: jest.fn(),
192+
removeListener: jest.fn(),
193+
once: mockOnce,
194+
});
195+
207196
emitter.initAsync();
208-
emitter.startListenerAsync();
209-
callback();
197+
const listener = jest.fn();
198+
emitter.onceNewFrame(listener);
199+
callback({
200+
newFrameTimestampInSeconds: 67890,
201+
});
210202

211203
// Wait for the next event loop to allow startListenerAsync to call NATIVE.getNewScreenTimeToDisplay
212204
await Promise.resolve();
213205

214206
// Simulate retries and timer
215207
jest.runAllTimers();
216208

217-
expect(DeviceEventEmitter.emit).not.toBeCalled();
209+
// Ensure fallback event is emitted
210+
expect(listener).toHaveBeenCalledWith({
211+
newFrameTimestampInSeconds: 67890,
212+
isFallback: undefined,
213+
});
218214
expect(logger.log).not.toBeCalled();
219215
});
220216

221217
it('should retry up to maxRetries and emit fallback if no response', async () => {
222218
jest.useFakeTimers();
223219

224-
emitter.startListenerAsync();
220+
const listener = jest.fn();
221+
emitter.onceNewFrame(listener);
225222

226223
// Wait for the next event loop to allow startListenerAsync to call NATIVE.getNewScreenTimeToDisplay
227224
await Promise.resolve();
@@ -231,10 +228,7 @@ describe('SentryEventEmitterFallback', () => {
231228
// Simulate retries and timer
232229
jest.runAllTimers();
233230

234-
expect(DeviceEventEmitter.emit).toHaveBeenCalledWith(
235-
NewFrameEventName,
236-
expect.objectContaining({ isFallback: true }),
237-
);
231+
expect(listener).toHaveBeenCalledWith(expect.objectContaining({ isFallback: true }));
238232
expect(logger.log).toHaveBeenCalledWith(expect.stringContaining('Native event emitter did not reply in time'));
239233

240234
jest.useRealTimers();

0 commit comments

Comments
 (0)