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
73 changes: 73 additions & 0 deletions www/InAppMessagesNamespace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,79 @@ describe('InAppMessages', () => {

expect(window.cordova.exec).not.toHaveBeenCalled();
});

test('should only register native handler once for multiple listeners of same event type', () => {
const handler1 = vi.fn();
const handler2 = vi.fn();
const handler3 = vi.fn();

// Add first listener - should register native handler
inAppMessages.addEventListener('click', handler1);
expect(window.cordova.exec).toHaveBeenCalledTimes(1);

// Add second listener - should NOT register native handler again
inAppMessages.addEventListener('click', handler2);
expect(window.cordova.exec).toHaveBeenCalledTimes(1);

// Add third listener - should still be only one registration
inAppMessages.addEventListener('click', handler3);
expect(window.cordova.exec).toHaveBeenCalledTimes(1);

// Trigger the event with click data
const clickData = {
message: { messageId: 'test' },
result: { closingMessage: true, actionId: 'test' },
};
mockExec.mock.calls[0][0](clickData);

// All handlers should execute exactly once
expect(handler1).toHaveBeenCalledTimes(1);
expect(handler1).toHaveBeenCalledWith(clickData);
expect(handler2).toHaveBeenCalledTimes(1);
expect(handler2).toHaveBeenCalledWith(clickData);
expect(handler3).toHaveBeenCalledTimes(1);
expect(handler3).toHaveBeenCalledWith(clickData);
});

test('should only register native handler once for willDisplay event', () => {
const handler1 = vi.fn();
const handler2 = vi.fn();

inAppMessages.addEventListener('willDisplay', handler1);
inAppMessages.addEventListener('willDisplay', handler2);

expect(window.cordova.exec).toHaveBeenCalledTimes(1);
});

test('should only register native handler once for didDisplay event', () => {
const handler1 = vi.fn();
const handler2 = vi.fn();

inAppMessages.addEventListener('didDisplay', handler1);
inAppMessages.addEventListener('didDisplay', handler2);

expect(window.cordova.exec).toHaveBeenCalledTimes(1);
});

test('should only register native handler once for willDismiss event', () => {
const handler1 = vi.fn();
const handler2 = vi.fn();

inAppMessages.addEventListener('willDismiss', handler1);
inAppMessages.addEventListener('willDismiss', handler2);

expect(window.cordova.exec).toHaveBeenCalledTimes(1);
});

test('should only register native handler once for didDismiss event', () => {
const handler1 = vi.fn();
const handler2 = vi.fn();

inAppMessages.addEventListener('didDismiss', handler1);
inAppMessages.addEventListener('didDismiss', handler2);

expect(window.cordova.exec).toHaveBeenCalledTimes(1);
});
});

describe('removeEventListener', () => {
Expand Down
162 changes: 100 additions & 62 deletions www/InAppMessagesNamespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ export default class InAppMessages {
event: InAppMessageDidDismissEvent,
) => void)[] = [];

// Track whether native handlers have been registered to avoid duplicate registrations
private _clickHandlerRegistered = false;
private _willDisplayHandlerRegistered = false;
private _didDisplayHandlerRegistered = false;
private _willDismissHandlerRegistered = false;
private _didDismissHandlerRegistered = false;

private _processFunctionList<T>(
array: ((event: T) => void)[],
param: T,
Expand All @@ -49,86 +56,117 @@ export default class InAppMessages {
this._inAppMessageClickListeners.push(
listener as (event: InAppMessageClickEvent) => void,
);
const inAppMessageClickListener = (json: InAppMessageClickEvent) => {
this._processFunctionList(this._inAppMessageClickListeners, json);
};
window.cordova.exec(
inAppMessageClickListener,
noop,
'OneSignalPush',
'setInAppMessageClickHandler',
[],
);

// Only register the native handler once
if (!this._clickHandlerRegistered) {
this._clickHandlerRegistered = true;
const inAppMessageClickListener = (json: InAppMessageClickEvent) => {
this._processFunctionList(this._inAppMessageClickListeners, json);
};
window.cordova.exec(
inAppMessageClickListener,
noop,
'OneSignalPush',
'setInAppMessageClickHandler',
[],
);
}
} else if (event === 'willDisplay') {
this._willDisplayInAppMessageListeners.push(
listener as (event: InAppMessageWillDisplayEvent) => void,
);
const willDisplayCallBackProcessor = (
event: InAppMessageWillDisplayEvent,
) => {
this._processFunctionList(
this._willDisplayInAppMessageListeners,
event,

// Only register the native handler once
if (!this._willDisplayHandlerRegistered) {
this._willDisplayHandlerRegistered = true;
const willDisplayCallBackProcessor = (
event: InAppMessageWillDisplayEvent,
) => {
this._processFunctionList(
this._willDisplayInAppMessageListeners,
event,
);
};
window.cordova.exec(
willDisplayCallBackProcessor,
noop,
'OneSignalPush',
'setOnWillDisplayInAppMessageHandler',
[],
);
};
window.cordova.exec(
willDisplayCallBackProcessor,
noop,
'OneSignalPush',
'setOnWillDisplayInAppMessageHandler',
[],
);
}
} else if (event === 'didDisplay') {
this._didDisplayInAppMessageListeners.push(
listener as (event: InAppMessageDidDisplayEvent) => void,
);
const didDisplayCallBackProcessor = (
event: InAppMessageDidDisplayEvent,
) => {
this._processFunctionList(this._didDisplayInAppMessageListeners, event);
};
window.cordova.exec(
didDisplayCallBackProcessor,
noop,
'OneSignalPush',
'setOnDidDisplayInAppMessageHandler',
[],
);

// Only register the native handler once
if (!this._didDisplayHandlerRegistered) {
this._didDisplayHandlerRegistered = true;
const didDisplayCallBackProcessor = (
event: InAppMessageDidDisplayEvent,
) => {
this._processFunctionList(
this._didDisplayInAppMessageListeners,
event,
);
};
window.cordova.exec(
didDisplayCallBackProcessor,
noop,
'OneSignalPush',
'setOnDidDisplayInAppMessageHandler',
[],
);
}
} else if (event === 'willDismiss') {
this._willDismissInAppMessageListeners.push(
listener as (event: InAppMessageWillDismissEvent) => void,
);
const willDismissInAppMessageProcessor = (
event: InAppMessageWillDismissEvent,
) => {
this._processFunctionList(
this._willDismissInAppMessageListeners,
event,

// Only register the native handler once
if (!this._willDismissHandlerRegistered) {
this._willDismissHandlerRegistered = true;
const willDismissInAppMessageProcessor = (
event: InAppMessageWillDismissEvent,
) => {
this._processFunctionList(
this._willDismissInAppMessageListeners,
event,
);
};
window.cordova.exec(
willDismissInAppMessageProcessor,
noop,
'OneSignalPush',
'setOnWillDismissInAppMessageHandler',
[],
);
};
window.cordova.exec(
willDismissInAppMessageProcessor,
noop,
'OneSignalPush',
'setOnWillDismissInAppMessageHandler',
[],
);
}
} else if (event === 'didDismiss') {
this._didDismissInAppMessageListeners.push(
listener as (event: InAppMessageDidDismissEvent) => void,
);
const didDismissInAppMessageCallBackProcessor = (
event: InAppMessageDidDismissEvent,
) => {
this._processFunctionList(this._didDismissInAppMessageListeners, event);
};
window.cordova.exec(
didDismissInAppMessageCallBackProcessor,
noop,
'OneSignalPush',
'setOnDidDismissInAppMessageHandler',
[],
);

// Only register the native handler once
if (!this._didDismissHandlerRegistered) {
this._didDismissHandlerRegistered = true;
const didDismissInAppMessageCallBackProcessor = (
event: InAppMessageDidDismissEvent,
) => {
this._processFunctionList(
this._didDismissInAppMessageListeners,
event,
);
};
window.cordova.exec(
didDismissInAppMessageCallBackProcessor,
noop,
'OneSignalPush',
'setOnDidDismissInAppMessageHandler',
[],
);
}
}
}

Expand Down
56 changes: 54 additions & 2 deletions www/NotificationsNamespace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,13 +243,15 @@ describe('Notifications', () => {
[],
);

mockExec.mockClear();
// Capture the callback before adding second listener
const foregroundCallback = mockExec.mock.calls[0][0];

const mockListener2 = vi.fn();
notifications.addEventListener('foregroundWillDisplay', mockListener2);

// can call display event listeners
const notificationData = mockNotification();
mockExec.mock.calls[0][0](notificationData);
foregroundCallback(notificationData);

const displayEvent = new NotificationWillDisplayEvent(notificationData);
expect(mockListener).toHaveBeenCalledWith(displayEvent);
Expand Down Expand Up @@ -292,6 +294,56 @@ describe('Notifications', () => {

expect(window.cordova.exec).not.toHaveBeenCalled();
});

test('should only register native handler once for multiple click listeners', () => {
const handler1 = vi.fn();
const handler2 = vi.fn();
const handler3 = vi.fn();

// Add first listener - should register native handler
notifications.addEventListener('click', handler1);
expect(window.cordova.exec).toHaveBeenCalledTimes(1);

// Add second listener - should NOT register native handler again
notifications.addEventListener('click', handler2);
expect(window.cordova.exec).toHaveBeenCalledTimes(1);

// Add third listener - should still be only one registration
notifications.addEventListener('click', handler3);
expect(window.cordova.exec).toHaveBeenCalledTimes(1);

// Trigger the event
const clickData = mockNotificationClickEvent();
mockExec.mock.calls[0][0](clickData);

// All handlers should execute exactly once
expect(handler1).toHaveBeenCalledTimes(1);
expect(handler1).toHaveBeenCalledWith(clickData);
expect(handler2).toHaveBeenCalledTimes(1);
expect(handler2).toHaveBeenCalledWith(clickData);
expect(handler3).toHaveBeenCalledTimes(1);
expect(handler3).toHaveBeenCalledWith(clickData);
});

test('should only register native handler once for multiple foregroundWillDisplay listeners', () => {
const handler1 = vi.fn();
const handler2 = vi.fn();

notifications.addEventListener('foregroundWillDisplay', handler1);
notifications.addEventListener('foregroundWillDisplay', handler2);

expect(window.cordova.exec).toHaveBeenCalledTimes(1);
});

test('should only register native handler once for multiple permissionChange listeners', () => {
const handler1 = vi.fn();
const handler2 = vi.fn();

notifications.addEventListener('permissionChange', handler1);
notifications.addEventListener('permissionChange', handler2);

expect(window.cordova.exec).toHaveBeenCalledTimes(1);
});
});

describe('removeEventListener', () => {
Expand Down
Loading
Loading