Skip to content

Commit 9fdf699

Browse files
committed
improve typings for event manager
1 parent 1eb33be commit 9fdf699

File tree

6 files changed

+154
-164
lines changed

6 files changed

+154
-164
lines changed

src/events/EventManager.test.ts

Lines changed: 61 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,22 @@ import {
1212
SUBSCRIPTION_CHANGED,
1313
USER_STATE_CHANGED,
1414
} from '../constants/events';
15+
import OSNotification from '../OSNotification';
16+
import type { NotificationClickEvent } from '../types/notificationEvents';
1517
import type { PushSubscriptionChangedState } from '../types/subscription';
1618
import type { UserChangedState } from '../types/user';
17-
import EventManager, { type EventListenerMap } from './EventManager';
19+
import EventManager, { type RawEventListenerMap } from './EventManager';
1820
import NotificationWillDisplayEvent from './NotificationWillDisplayEvent';
1921

2022
describe('EventManager', () => {
2123
let eventManager: EventManager;
2224
let mockNativeModule: any;
2325
let eventCallbacks: Map<string, (payload: any) => void>;
2426

25-
const getCallback = <K extends keyof EventListenerMap>(
27+
const getCallback = <K extends keyof RawEventListenerMap>(
2628
eventName: K,
27-
): ((payload: Parameters<EventListenerMap[K]>[0]) => void) | undefined => {
28-
return eventCallbacks.get(eventName) as any;
29+
): RawEventListenerMap[K] | undefined => {
30+
return eventCallbacks.get(eventName) as RawEventListenerMap[K] | undefined;
2931
};
3032

3133
beforeEach(() => {
@@ -210,14 +212,7 @@ describe('EventManager', () => {
210212
eventManager.addEventListener(NOTIFICATION_WILL_DISPLAY, handler);
211213

212214
const callback = getCallback(NOTIFICATION_WILL_DISPLAY);
213-
214-
const notificationPayload = {
215-
notificationId: 'test-id',
216-
body: 'test-body',
217-
rawPayload: {},
218-
};
219-
220-
callback!(notificationPayload);
215+
callback!(rawWillDisplayPayload);
221216

222217
expect(handler).toHaveBeenCalled();
223218
const receivedEvent = handler.mock.calls[0][0];
@@ -228,33 +223,22 @@ describe('EventManager', () => {
228223
const handler = vi.fn();
229224
eventManager.addEventListener(PERMISSION_CHANGED, handler);
230225

226+
const payload = getRawPermissionChangedPayload(true);
231227
const callback = getCallback(PERMISSION_CHANGED);
232-
callback!({ permission: true });
228+
callback!(payload);
233229

234230
expect(handler).toHaveBeenCalledWith(true);
235231
});
236232

237233
test('should handle generic events with object payload', () => {
238234
const handler = vi.fn();
239-
const payload = {
240-
previous: {
241-
id: 'previous-id',
242-
token: 'previous-token',
243-
optedIn: false,
244-
},
245-
current: {
246-
id: 'current-id',
247-
token: 'current-token',
248-
optedIn: true,
249-
},
250-
} satisfies PushSubscriptionChangedState;
251235

252236
eventManager.addEventListener(SUBSCRIPTION_CHANGED, handler);
253237

254238
const callback = getCallback(SUBSCRIPTION_CHANGED);
255-
callback!(payload);
239+
callback!(pushChangedPayload);
256240

257-
expect(handler).toHaveBeenCalledWith(payload);
241+
expect(handler).toHaveBeenCalledWith(pushChangedPayload);
258242
});
259243

260244
test('should call all handlers for an event', () => {
@@ -350,7 +334,14 @@ describe('EventManager', () => {
350334

351335
test('should handle NOTIFICATION_CLICKED events', () => {
352336
const handler = vi.fn();
353-
const payload = { notificationId: 'notif-123', action: 'clicked' };
337+
const payload = {
338+
result: { actionId: 'action-1' },
339+
notification: new OSNotification({
340+
notificationId: 'test-id',
341+
body: 'test-body',
342+
rawPayload: {},
343+
}),
344+
} satisfies NotificationClickEvent;
354345

355346
eventManager.addEventListener(NOTIFICATION_CLICKED, handler);
356347

@@ -372,7 +363,8 @@ describe('EventManager', () => {
372363

373364
// Trigger event
374365
const callback = getCallback(PERMISSION_CHANGED);
375-
callback!({ permission: true });
366+
const payload = getRawPermissionChangedPayload(true);
367+
callback!(payload);
376368

377369
expect(handler1).toHaveBeenCalledWith(true);
378370
expect(handler2).toHaveBeenCalledWith(true);
@@ -385,7 +377,7 @@ describe('EventManager', () => {
385377
handler2.mockClear();
386378

387379
// Trigger event again
388-
callback!({ permission: false });
380+
callback!(getRawPermissionChangedPayload(false));
389381

390382
expect(handler1).not.toHaveBeenCalled();
391383
expect(handler2).toHaveBeenCalledWith(false);
@@ -409,17 +401,16 @@ describe('EventManager', () => {
409401
const notificationCallback = getCallback(NOTIFICATION_WILL_DISPLAY);
410402

411403
// Trigger different event types
412-
permissionCallback!({ permission: true });
413-
subscriptionCallback!({ id: 'sub-123' });
414-
notificationCallback!({
415-
notificationId: 'notif-123',
416-
body: 'test',
417-
rawPayload: {},
418-
});
404+
const permissionPayload = getRawPermissionChangedPayload(true);
405+
permissionCallback!(permissionPayload);
406+
subscriptionCallback!(pushChangedPayload);
407+
notificationCallback!(rawWillDisplayPayload);
419408

420409
expect(permissionHandler).toHaveBeenCalledWith(true);
421-
expect(subscriptionHandler).toHaveBeenCalledWith({ id: 'sub-123' });
422-
expect(notificationWillDisplayHandler).toHaveBeenCalled();
410+
expect(subscriptionHandler).toHaveBeenCalledWith(pushChangedPayload);
411+
expect(notificationWillDisplayHandler).toHaveBeenCalledWith(
412+
new NotificationWillDisplayEvent(rawWillDisplayPayload),
413+
);
423414
});
424415

425416
test('should maintain separate handler arrays for different events', () => {
@@ -455,20 +446,44 @@ describe('EventManager', () => {
455446

456447
const callback = getCallback(SUBSCRIPTION_CHANGED);
457448

458-
callback!({ id: '1' });
459-
expect(handler1).toHaveBeenCalledWith({ id: '1' });
460-
expect(handler2).toHaveBeenCalledWith({ id: '1' });
461-
expect(handler3).toHaveBeenCalledWith({ id: '1' });
449+
callback!(pushChangedPayload);
450+
expect(handler1).toHaveBeenCalledWith(pushChangedPayload);
451+
expect(handler2).toHaveBeenCalledWith(pushChangedPayload);
452+
expect(handler3).toHaveBeenCalledWith(pushChangedPayload);
462453

463454
eventManager.removeEventListener(SUBSCRIPTION_CHANGED, handler2);
464455
handler1.mockClear();
465456
handler2.mockClear();
466457
handler3.mockClear();
467458

468-
callback!({ id: '2' });
469-
expect(handler1).toHaveBeenCalledWith({ id: '2' });
459+
callback!(pushChangedPayload);
460+
expect(handler1).toHaveBeenCalledWith(pushChangedPayload);
470461
expect(handler2).not.toHaveBeenCalled();
471-
expect(handler3).toHaveBeenCalledWith({ id: '2' });
462+
expect(handler3).toHaveBeenCalledWith(pushChangedPayload);
472463
});
473464
});
474465
});
466+
467+
// helper payloads
468+
const getRawPermissionChangedPayload = (permission: boolean) => ({
469+
permission: permission,
470+
});
471+
472+
const rawWillDisplayPayload = new OSNotification({
473+
notificationId: 'test-id',
474+
body: 'test-body',
475+
rawPayload: {},
476+
});
477+
478+
const pushChangedPayload = {
479+
previous: {
480+
id: 'previous-id',
481+
token: 'previous-token',
482+
optedIn: false,
483+
},
484+
current: {
485+
id: 'current-id',
486+
token: 'current-token',
487+
optedIn: true,
488+
},
489+
} satisfies PushSubscriptionChangedState;

src/events/EventManager.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import type { UserChangedState } from '../types/user';
2929
import NotificationWillDisplayEvent from './NotificationWillDisplayEvent';
3030

3131
export interface EventListenerMap {
32-
[PERMISSION_CHANGED]: (event: { permission: boolean }) => void;
32+
[PERMISSION_CHANGED]: (event: boolean) => void;
3333
[SUBSCRIPTION_CHANGED]: (event: PushSubscriptionChangedState) => void;
3434
[USER_STATE_CHANGED]: (event: UserChangedState) => void;
3535
[NOTIFICATION_WILL_DISPLAY]: (event: NotificationWillDisplayEvent) => void;
@@ -41,6 +41,16 @@ export interface EventListenerMap {
4141
[IN_APP_MESSAGE_DID_DISPLAY]: (event: InAppMessageDidDisplayEvent) => void;
4242
}
4343

44+
type RawNotificationOverrides = {
45+
[PERMISSION_CHANGED]: (payload: { permission: boolean }) => void;
46+
[NOTIFICATION_WILL_DISPLAY]: (payload: OSNotification) => void;
47+
};
48+
export type RawEventListenerMap = Omit<
49+
EventListenerMap,
50+
keyof RawNotificationOverrides
51+
> &
52+
RawNotificationOverrides;
53+
4454
const eventList = [
4555
PERMISSION_CHANGED,
4656
SUBSCRIPTION_CHANGED,
@@ -120,10 +130,10 @@ export default class EventManager {
120130
}
121131

122132
// returns an event listener with the js to native mapping
123-
generateEventListener<K extends keyof EventListenerMap>(
133+
generateEventListener<K extends keyof RawEventListenerMap>(
124134
eventName: K,
125135
): EmitterSubscription {
126-
const addListenerCallback = (payload: unknown) => {
136+
const addListenerCallback: RawEventListenerMap[K] = (payload: unknown) => {
127137
let handlerArray = this.eventListenerArrayMap.get(eventName);
128138
if (handlerArray) {
129139
if (eventName === NOTIFICATION_WILL_DISPLAY) {

src/index.test.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
SUBSCRIPTION_CHANGED,
1313
USER_STATE_CHANGED,
1414
} from './constants/events';
15-
import EventManager from './events/EventManager';
15+
import EventManager, { type EventListenerMap } from './events/EventManager';
1616
import * as helpers from './helpers';
1717
import { LogLevel, OneSignal, OSNotificationPermission } from './index';
1818

@@ -38,10 +38,12 @@ const removeEventManagerListenerSpy = vi.spyOn(
3838
'removeEventListener',
3939
);
4040

41-
const filterEventListener = (eventName: string) => {
41+
const filterEventListener = <K extends keyof EventListenerMap>(
42+
eventName: K,
43+
): EventListenerMap[K] => {
4244
return addEventManagerListenerSpy.mock.calls.filter(
4345
(call) => call[0] === eventName,
44-
)[0][1];
46+
)[0][1] as EventListenerMap[K];
4547
};
4648

4749
describe('OneSignal', () => {
@@ -78,20 +80,29 @@ describe('OneSignal', () => {
7880
expect(permission).toBe(true);
7981

8082
// test push subscription change listener
81-
const subscriptionChangeFn = filterEventListener(SUBSCRIPTION_CHANGED);
82-
subscriptionChangeFn({
83+
const pushData = {
84+
previous: {
85+
id: '',
86+
token: '',
87+
optedIn: false,
88+
},
8389
current: {
8490
id: 'subscription-id',
8591
token: 'push-token',
8692
optedIn: true,
8793
},
88-
});
94+
};
95+
const subscriptionChangeFn = filterEventListener(SUBSCRIPTION_CHANGED);
96+
subscriptionChangeFn(pushData);
8997
const pushSubscription =
9098
OneSignal.User.pushSubscription.getPushSubscriptionId();
9199
expect(pushSubscription).toBe('subscription-id');
92100

93101
// reset push subscription
94-
subscriptionChangeFn({ current: {} });
102+
subscriptionChangeFn({
103+
...pushData,
104+
current: { id: '', token: '', optedIn: false },
105+
});
95106
});
96107

97108
test('should not initialize if native module is not loaded', () => {

0 commit comments

Comments
 (0)