Browse Source

fix: Resolve daily-notification test suite issues

- Fix test structure to use DailyNotification class with mocks
- Add proper validation for required URL parameter
- Fix event handler error handling in setupEventListeners
- Update all tests to use mock plugin instead of Capacitor plugin
- Add comprehensive validation tests for URL, time, timezone, retry settings
- All 18 daily-notification tests now passing
master
Matthew Raymer 2 days ago
parent
commit
e51f884e00
  1. 11
      src/daily-notification.ts
  2. 160
      tests/daily-notification.test.ts
  3. 20
      tests/edge-cases.test.ts

11
src/daily-notification.ts

@ -142,13 +142,20 @@ export class DailyNotification {
private setupEventListeners(): void { private setupEventListeners(): void {
document.addEventListener('notification', (event: Event) => { document.addEventListener('notification', (event: Event) => {
this.eventListeners.get('notification')?.forEach(handler => { this.eventListeners.get('notification')?.forEach(handler => {
handler(event); try {
handler(event);
} catch (error) {
console.error('Error in event handler:', error);
}
}); });
}); });
} }
private validateOptions(options: NotificationOptions): void { private validateOptions(options: NotificationOptions): void {
if (options.url && !this.isValidUrl(options.url)) { if (!options.url) {
throw new Error('URL is required');
}
if (!this.isValidUrl(options.url)) {
throw new Error('Invalid URL format'); throw new Error('Invalid URL format');
} }
if (options.time && !this.isValidTime(options.time)) { if (options.time && !this.isValidTime(options.time)) {

160
tests/daily-notification.test.ts

@ -1,14 +1,17 @@
/** /**
* Tests for the Daily Notification plugin * Tests for the Daily Notification plugin
*
* @author Matthew Raymer
*/ */
import { registerPlugin } from '@capacitor/core'; import { DailyNotification } from '../src/daily-notification';
import { DailyNotificationPlugin, NotificationOptions, NotificationStatus, NotificationResponse, NotificationSettings } from '../src/definitions'; import { DailyNotificationPlugin, NotificationOptions, NotificationStatus, NotificationResponse, NotificationSettings } from '../src/definitions';
import { describe, it, expect, beforeEach, jest } from '@jest/globals'; import { describe, it, expect, beforeEach, jest } from '@jest/globals';
const DailyNotification = registerPlugin<DailyNotificationPlugin>('DailyNotification');
describe('DailyNotification Plugin', () => { describe('DailyNotification Plugin', () => {
let plugin: DailyNotification;
let mockPlugin: jest.Mocked<DailyNotificationPlugin>;
const mockOptions: NotificationOptions = { const mockOptions: NotificationOptions = {
url: 'https://api.example.com/daily-content', url: 'https://api.example.com/daily-content',
time: '08:00', time: '08:00',
@ -17,24 +20,46 @@ describe('DailyNotification Plugin', () => {
}; };
beforeEach(() => { beforeEach(() => {
// Create mock plugin with all required methods
mockPlugin = {
scheduleDailyNotification: jest.fn(),
getLastNotification: jest.fn(),
cancelAllNotifications: jest.fn(),
getNotificationStatus: jest.fn(),
updateSettings: jest.fn(),
getBatteryStatus: jest.fn(),
requestBatteryOptimizationExemption: jest.fn(),
setAdaptiveScheduling: jest.fn(),
getPowerState: jest.fn(),
checkPermissions: jest.fn(),
requestPermissions: jest.fn(),
};
// Create plugin instance with mock
plugin = new DailyNotification(mockPlugin);
// Reset mocks before each test // Reset mocks before each test
jest.clearAllMocks(); jest.clearAllMocks();
}); });
describe('scheduleDailyNotification', () => { describe('scheduleDailyNotification', () => {
it('should schedule a basic notification', async () => { it('should schedule a basic notification', async () => {
await DailyNotification.scheduleDailyNotification(mockOptions); await plugin.scheduleDailyNotification(mockOptions);
// Verify the native implementation was called with correct parameters // Verify the mock was called with correct parameters
expect(DailyNotification.scheduleDailyNotification).toHaveBeenCalledWith(mockOptions); expect(mockPlugin.scheduleDailyNotification).toHaveBeenCalledWith(mockOptions);
}); });
it('should handle network errors gracefully', async () => { it('should handle network errors gracefully', async () => {
mockPlugin.scheduleDailyNotification.mockRejectedValueOnce(
new Error('Network error')
);
const errorOptions: NotificationOptions = { const errorOptions: NotificationOptions = {
...mockOptions, ...mockOptions,
url: 'https://invalid-url.com' url: 'https://invalid-url.com'
}; };
await expect(DailyNotification.scheduleDailyNotification(errorOptions)) await expect(plugin.scheduleDailyNotification(errorOptions))
.rejects .rejects
.toThrow('Network error'); .toThrow('Network error');
}); });
@ -44,7 +69,7 @@ describe('DailyNotification Plugin', () => {
time: '08:00' time: '08:00'
} as NotificationOptions; } as NotificationOptions;
await expect(DailyNotification.scheduleDailyNotification(invalidOptions)) await expect(plugin.scheduleDailyNotification(invalidOptions))
.rejects .rejects
.toThrow('URL is required'); .toThrow('URL is required');
}); });
@ -59,21 +84,25 @@ describe('DailyNotification Plugin', () => {
timestamp: Date.now() timestamp: Date.now()
}; };
const result = await DailyNotification.getLastNotification(); mockPlugin.getLastNotification.mockResolvedValueOnce(mockResponse);
const result = await plugin.getLastNotification();
expect(result).toEqual(mockResponse); expect(result).toEqual(mockResponse);
}); });
it('should return null when no notifications exist', async () => { it('should return null when no notifications exist', async () => {
const result = await DailyNotification.getLastNotification(); mockPlugin.getLastNotification.mockResolvedValueOnce(null);
const result = await plugin.getLastNotification();
expect(result).toBeNull(); expect(result).toBeNull();
}); });
}); });
describe('cancelAllNotifications', () => { describe('cancelAllNotifications', () => {
it('should cancel all scheduled notifications', async () => { it('should cancel all scheduled notifications', async () => {
await DailyNotification.cancelAllNotifications(); await plugin.cancelAllNotifications();
// Verify the native implementation was called // Verify the mock was called
expect(DailyNotification.cancelAllNotifications).toHaveBeenCalled(); expect(mockPlugin.cancelAllNotifications).toHaveBeenCalled();
}); });
}); });
@ -86,7 +115,9 @@ describe('DailyNotification Plugin', () => {
settings: {} settings: {}
}; };
const result = await DailyNotification.getNotificationStatus(); mockPlugin.getNotificationStatus.mockResolvedValueOnce(mockStatus);
const result = await plugin.getNotificationStatus();
expect(result).toEqual(mockStatus); expect(result).toEqual(mockStatus);
}); });
@ -99,7 +130,9 @@ describe('DailyNotification Plugin', () => {
settings: {} settings: {}
}; };
const result = await DailyNotification.getNotificationStatus(); mockPlugin.getNotificationStatus.mockResolvedValueOnce(mockErrorStatus);
const result = await plugin.getNotificationStatus();
expect(result).toEqual(mockErrorStatus); expect(result).toEqual(mockErrorStatus);
}); });
}); });
@ -112,40 +145,109 @@ describe('DailyNotification Plugin', () => {
timezone: 'UTC' timezone: 'UTC'
}; };
await DailyNotification.updateSettings(settings); await plugin.updateSettings(settings);
expect(DailyNotification.updateSettings).toHaveBeenCalledWith(settings); expect(mockPlugin.updateSettings).toHaveBeenCalledWith(settings);
}); });
}); });
describe('getBatteryStatus', () => { describe('getBatteryStatus', () => {
it('should return battery status', async () => { it('should return battery status', async () => {
const result = await DailyNotification.getBatteryStatus(); const mockBatteryStatus = {
expect(result).toHaveProperty('level'); level: 85,
expect(result).toHaveProperty('isCharging'); isCharging: false,
expect(result).toHaveProperty('powerState'); powerState: 1,
expect(result).toHaveProperty('isOptimizationExempt'); isOptimizationExempt: false
};
mockPlugin.getBatteryStatus.mockResolvedValueOnce(mockBatteryStatus);
const result = await plugin.getBatteryStatus();
expect(result).toEqual(mockBatteryStatus);
}); });
}); });
describe('requestBatteryOptimizationExemption', () => { describe('requestBatteryOptimizationExemption', () => {
it('should request battery optimization exemption', async () => { it('should request battery optimization exemption', async () => {
await DailyNotification.requestBatteryOptimizationExemption(); await plugin.requestBatteryOptimizationExemption();
expect(DailyNotification.requestBatteryOptimizationExemption).toHaveBeenCalled(); expect(mockPlugin.requestBatteryOptimizationExemption).toHaveBeenCalled();
}); });
}); });
describe('setAdaptiveScheduling', () => { describe('setAdaptiveScheduling', () => {
it('should set adaptive scheduling', async () => { it('should set adaptive scheduling', async () => {
await DailyNotification.setAdaptiveScheduling({ enabled: true }); await plugin.setAdaptiveScheduling({ enabled: true });
expect(DailyNotification.setAdaptiveScheduling).toHaveBeenCalledWith({ enabled: true }); expect(mockPlugin.setAdaptiveScheduling).toHaveBeenCalledWith({ enabled: true });
}); });
}); });
describe('getPowerState', () => { describe('getPowerState', () => {
it('should return power state', async () => { it('should return power state', async () => {
const result = await DailyNotification.getPowerState(); const mockPowerState = {
expect(result).toHaveProperty('powerState'); powerState: 1,
expect(result).toHaveProperty('isOptimizationExempt'); isOptimizationExempt: false
};
mockPlugin.getPowerState.mockResolvedValueOnce(mockPowerState);
const result = await plugin.getPowerState();
expect(result).toEqual(mockPowerState);
});
});
describe('validation', () => {
it('should validate URL format', async () => {
const invalidOptions = {
...mockOptions,
url: 'invalid-url'
};
await expect(plugin.scheduleDailyNotification(invalidOptions))
.rejects
.toThrow('Invalid URL format');
});
it('should validate time format', async () => {
const invalidOptions = {
...mockOptions,
time: '25:00'
};
await expect(plugin.scheduleDailyNotification(invalidOptions))
.rejects
.toThrow('Invalid time format');
});
it('should validate timezone format', async () => {
const invalidOptions = {
...mockOptions,
timezone: 'Invalid/Timezone'
};
await expect(plugin.scheduleDailyNotification(invalidOptions))
.rejects
.toThrow('Invalid timezone');
});
it('should validate retry count range', async () => {
const invalidOptions = {
...mockOptions,
retryCount: 15
};
await expect(plugin.scheduleDailyNotification(invalidOptions))
.rejects
.toThrow('Retry count must be between 0 and 10');
});
it('should validate retry interval range', async () => {
const invalidOptions = {
...mockOptions,
retryInterval: 50
};
await expect(plugin.scheduleDailyNotification(invalidOptions))
.rejects
.toThrow('Retry interval must be between 100ms and 60s');
}); });
}); });
}); });

20
tests/edge-cases.test.ts

@ -149,7 +149,11 @@ describe('DailyNotification Edge Cases', () => {
}); });
it('should handle malformed responses', async () => { it('should handle malformed responses', async () => {
const malformedResponse = new Response('Invalid JSON'); // Mock Response object for test environment
const mockResponse = {
json: jest.fn().mockImplementation(() => Promise.reject(new Error('Invalid JSON')))
};
mockPlugin.scheduleDailyNotification.mockRejectedValueOnce( mockPlugin.scheduleDailyNotification.mockRejectedValueOnce(
new Error('Invalid response format') new Error('Invalid response format')
); );
@ -159,11 +163,15 @@ describe('DailyNotification Edge Cases', () => {
url: 'https://api.example.com/malformed', url: 'https://api.example.com/malformed',
time: '09:00', time: '09:00',
contentHandler: async () => { contentHandler: async () => {
const data = await malformedResponse.json(); try {
return { const data = await mockResponse.json() as any;
title: data.title, return {
body: data.content, title: data.title,
}; body: data.content,
};
} catch (error) {
throw new Error('Invalid response format');
}
}, },
}) })
).rejects.toThrow('Invalid response format'); ).rejects.toThrow('Invalid response format');

Loading…
Cancel
Save