Skip to content
Merged
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
1 change: 1 addition & 0 deletions __mocks__/react-native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ const mockRNOneSignal = {
addUniqueOutcome: vi.fn(),
addOutcomeWithValue: vi.fn(),
displayNotification: vi.fn(),
preventDefault: vi.fn(),
};

const mockPlatform = {
Expand Down
107 changes: 107 additions & 0 deletions src/events/NotificationWillDisplayEvent.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { NativeModules } from 'react-native';
import OSNotification, { type BaseNotificationData } from '../OSNotification';
import NotificationWillDisplayEvent from './NotificationWillDisplayEvent';

const mockRNOneSignal = NativeModules.OneSignal;

describe('NotificationWillDisplayEvent', () => {
const notificationId = 'test-notification-id';
const baseNotificationData: BaseNotificationData = {
body: 'Test notification body',
sound: 'default',
title: 'Test Title',
launchURL: 'https://example.com',
rawPayload: { key: 'value' },
actionButtons: [{ id: 'btn1', text: 'Button 1' }],
additionalData: { custom: 'data' },
notificationId,
};

describe('constructor', () => {
test('should initialize with OSNotification instance', () => {
const notification = new OSNotification(baseNotificationData);
const event = new NotificationWillDisplayEvent(notification);

expect(event.notification).toBeInstanceOf(OSNotification);
expect(event.notification.notificationId).toBe(notificationId);
expect(event.notification.body).toBe('Test notification body');
expect(event.notification.title).toBe('Test Title');
});

test('should create a new OSNotification instance from the provided notification', () => {
const notification = new OSNotification(baseNotificationData);
const event = new NotificationWillDisplayEvent(notification);

expect(event.notification).not.toBe(notification);
expect(event.notification.notificationId).toBe(notificationId);
expect(event.notification.body).toBe(notification.body);
});

test('should initialize with notification containing all optional fields', () => {
const fullData = {
...baseNotificationData,
sound: 'custom-sound',
launchURL: 'https://example.com/launch',
actionButtons: [
{ id: 'btn1', text: 'Button 1' },
{ id: 'btn2', text: 'Button 2' },
],
additionalData: { key1: 'value1', key2: 'value2' },
};
const notification = new OSNotification(fullData);
const event = new NotificationWillDisplayEvent(notification);

expect(event.notification.sound).toBe('custom-sound');
expect(event.notification.launchURL).toBe('https://example.com/launch');
expect(event.notification.actionButtons).toHaveLength(2);
expect(event.notification.additionalData).toEqual({
key1: 'value1',
key2: 'value2',
});
});
});

describe('preventDefault', () => {
test('should call native preventDefault with notificationId', () => {
const notification = new OSNotification(baseNotificationData);
const event = new NotificationWillDisplayEvent(notification);
const result = event.preventDefault();

expect(mockRNOneSignal.preventDefault).toHaveBeenCalledWith(
notificationId,
);
expect(result).toBeUndefined();
});

test('should allow multiple calls to preventDefault', () => {
const notification = new OSNotification(baseNotificationData);
const event = new NotificationWillDisplayEvent(notification);

event.preventDefault();
event.preventDefault();
event.preventDefault();

expect(mockRNOneSignal.preventDefault).toHaveBeenCalledTimes(3);
expect(mockRNOneSignal.preventDefault).toHaveBeenCalledWith(
'test-notification-id',
);
});
});

describe('getNotification', () => {
test('should return the notification instance', () => {
const notification = new OSNotification(baseNotificationData);
const event = new NotificationWillDisplayEvent(notification);
const returnedNotification = event.getNotification();

expect(returnedNotification).toBe(event.notification);
expect(returnedNotification).toBeInstanceOf(OSNotification);

expect(returnedNotification.notificationId).toBe('test-notification-id');
expect(returnedNotification.body).toBe('Test notification body');
expect(returnedNotification.title).toBe('Test Title');
expect(returnedNotification.sound).toBe('default');
expect(returnedNotification.rawPayload).toEqual({ key: 'value' });
});
});
});
1 change: 0 additions & 1 deletion src/events/NotificationWillDisplayEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export default class NotificationWillDisplayEvent {

preventDefault(): void {
RNOneSignal.preventDefault(this.notification.notificationId);
return;
}

getNotification(): OSNotification {
Expand Down
61 changes: 61 additions & 0 deletions src/helpers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { NativeModule } from 'react-native';
import type { MockInstance } from 'vitest';
import { isNativeModuleLoaded, isValidCallback } from './helpers';

describe('helpers', () => {
let errorSpy: MockInstance;

beforeEach(() => {
errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
});

describe('isValidCallback', () => {
test('should not throw when handler is a function', () => {
const handler = () => {};
expect(() => isValidCallback(handler)).not.toThrow();
});

test.each([
{ description: 'null', value: null },
{ description: 'undefined', value: undefined },
{ description: 'a string', value: 'not a function' },
{ description: 'a number', value: 123 },
{ description: 'an object', value: {} },
{ description: 'an array', value: [] },
{ description: 'a boolean', value: true },
])(
'should throw invariant error when handler is $description',
({ value }) => {
expect(() => isValidCallback(value as unknown as Function)).toThrow(
'Must provide a valid callback',
);
},
);
});

describe('isNativeModuleLoaded', () => {
test.each([
{ description: 'null', value: null as unknown as NativeModule },
{
description: 'undefined',
value: undefined as unknown as NativeModule,
},
])(
'should return false and log error when module is $description',
({ value }) => {
const result = isNativeModuleLoaded(value);

expect(result).toBe(false);
expect(errorSpy).toHaveBeenCalledTimes(1);
expect(errorSpy).toHaveBeenCalledWith(
'Could not load RNOneSignal native module. Make sure native dependencies are properly linked.',
);
},
);

test('should return true when module is loaded', () => {
const result = isNativeModuleLoaded({} as NativeModule);
expect(result).toBe(true);
});
});
});
6 changes: 6 additions & 0 deletions vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ export default defineConfig({
reporter: ['text-summary', 'lcov'],
reportOnFailure: true,
reportsDirectory: 'coverage',
thresholds: {
statements: 95,
branches: 95,
functions: 95,
lines: 95,
},
},
},
resolve: {
Expand Down
Loading