Skip to content

Commit 1e5a15d

Browse files
committed
add tests for custom events
1 parent d9e0021 commit 1e5a15d

5 files changed

Lines changed: 108 additions & 18 deletions

File tree

__mocks__/react-native.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ const mockRNOneSignal = {
8686
addOutcomeWithValue: vi.fn(),
8787
displayNotification: vi.fn(),
8888
preventDefault: vi.fn(),
89+
trackEvent: vi.fn(),
8990
};
9091

9192
const mockPlatform = {

src/helpers.test.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import type { NativeModule } from 'react-native';
22
import type { MockInstance } from 'vitest';
3-
import { isNativeModuleLoaded, isValidCallback } from './helpers';
3+
import {
4+
isNativeModuleLoaded,
5+
isObjectSerializable,
6+
isValidCallback,
7+
} from './helpers';
48

59
describe('helpers', () => {
610
let errorSpy: MockInstance;
@@ -58,4 +62,42 @@ describe('helpers', () => {
5862
expect(result).toBe(true);
5963
});
6064
});
65+
66+
describe('isObjectSerializable', () => {
67+
test.each([
68+
{ description: 'an empty object', value: {} },
69+
{ description: 'an object with string values', value: { key: 'value' } },
70+
{ description: 'an object with number values', value: { count: 42 } },
71+
{ description: 'an object with boolean values', value: { active: true } },
72+
{ description: 'an object with null values', value: { data: null } },
73+
{
74+
description: 'a nested object',
75+
value: { outer: { inner: 'value' } },
76+
},
77+
{
78+
description: 'an object with array values',
79+
value: { items: [1, 2, 3] },
80+
},
81+
])('should return true for $description', ({ value }) => {
82+
expect(isObjectSerializable(value)).toBe(true);
83+
});
84+
85+
test.each([
86+
{ description: 'null', value: null },
87+
{ description: 'undefined', value: undefined },
88+
{ description: 'a string', value: 'string' },
89+
{ description: 'a number', value: 123 },
90+
{ description: 'a boolean', value: true },
91+
{ description: 'an array', value: [1, 2, 3] },
92+
{ description: 'a function', value: () => {} },
93+
])('should return false for $description', ({ value }) => {
94+
expect(isObjectSerializable(value)).toBe(false);
95+
});
96+
97+
test('should return false for objects with circular references', () => {
98+
const circular: Record<string, unknown> = {};
99+
circular.self = circular;
100+
expect(isObjectSerializable(circular)).toBe(false);
101+
});
102+
});
61103
});

src/helpers.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,18 @@ export function isNativeModuleLoaded(module: NativeModule): boolean {
1616

1717
return true;
1818
}
19+
20+
/**
21+
* Returns true if the value is a JSON-serializable object.
22+
*/
23+
export function isObjectSerializable(value: unknown): boolean {
24+
if (!(typeof value === 'object' && value !== null && !Array.isArray(value))) {
25+
return false;
26+
}
27+
try {
28+
JSON.stringify(value);
29+
return true;
30+
} catch {
31+
return false;
32+
}
33+
}

src/index.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,49 @@ describe('OneSignal', () => {
894894
});
895895
});
896896

897+
describe('trackEvent', () => {
898+
test('should track event with name and properties', () => {
899+
const properties = { key: 'value', count: 42 };
900+
OneSignal.User.trackEvent('purchase', properties);
901+
expect(mockRNOneSignal.trackEvent).toHaveBeenCalledWith(
902+
'purchase',
903+
properties,
904+
);
905+
});
906+
907+
test('should track event with just name using default empty properties', () => {
908+
OneSignal.User.trackEvent('page_view');
909+
expect(mockRNOneSignal.trackEvent).toHaveBeenCalledWith('page_view', {});
910+
});
911+
912+
test('should not track event if native module is not loaded', () => {
913+
isNativeLoadedSpy.mockReturnValue(false);
914+
OneSignal.User.trackEvent('event');
915+
expect(mockRNOneSignal.trackEvent).not.toHaveBeenCalled();
916+
});
917+
918+
test('should not track event if properties are not serializable', () => {
919+
const circular: Record<string, unknown> = {};
920+
circular.self = circular;
921+
OneSignal.User.trackEvent('event', circular);
922+
expect(errorSpy).toHaveBeenCalledWith(
923+
'Properties must be a JSON-serializable object',
924+
);
925+
expect(mockRNOneSignal.trackEvent).not.toHaveBeenCalled();
926+
});
927+
928+
test('should not track event if properties is not an object', () => {
929+
OneSignal.User.trackEvent(
930+
'event',
931+
'invalid' as unknown as Record<string, unknown>,
932+
);
933+
expect(errorSpy).toHaveBeenCalledWith(
934+
'Properties must be a JSON-serializable object',
935+
);
936+
expect(mockRNOneSignal.trackEvent).not.toHaveBeenCalled();
937+
});
938+
});
939+
897940
describe('Notifications', () => {
898941
describe('hasPermission (deprecated)', () => {
899942
test('should log deprecation warning', () => {

src/index.ts

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ import {
1414
import type { OSNotificationPermission } from './constants/subscription';
1515
import EventManager from './events/EventManager';
1616
import NotificationWillDisplayEvent from './events/NotificationWillDisplayEvent';
17-
import { isNativeModuleLoaded, isValidCallback } from './helpers';
17+
import {
18+
isNativeModuleLoaded,
19+
isObjectSerializable,
20+
isValidCallback,
21+
} from './helpers';
1822
import type {
1923
InAppMessage,
2024
InAppMessageClickEvent,
@@ -602,7 +606,7 @@ export namespace OneSignal {
602606
if (!isNativeModuleLoaded(RNOneSignal)) return;
603607

604608
if (!isObjectSerializable(properties)) {
605-
console.error('Properties must be JSON-serializable');
609+
console.error('Properties must be a JSON-serializable object');
606610
return;
607611
}
608612

@@ -956,21 +960,6 @@ export namespace OneSignal {
956960
}
957961
}
958962

959-
/**
960-
* Returns true if the value is a JSON-serializable object.
961-
*/
962-
function isObjectSerializable(value: unknown): boolean {
963-
if (!(typeof value === 'object' && value !== null && !Array.isArray(value))) {
964-
return false;
965-
}
966-
try {
967-
JSON.stringify(value);
968-
return true;
969-
} catch (e) {
970-
return false;
971-
}
972-
}
973-
974963
export { OSNotificationPermission } from './constants/subscription';
975964
export {
976965
NotificationWillDisplayEvent,

0 commit comments

Comments
 (0)