/** * Tests for the Daily Notification plugin * * @author Matthew Raymer */ import { DailyNotification } from '../src/daily-notification'; import { DailyNotificationPlugin, NotificationOptions, NotificationStatus, NotificationResponse, NotificationSettings } from '../src/definitions'; import { describe, it, expect, beforeEach, jest } from '@jest/globals'; describe('DailyNotification Plugin', () => { let plugin: DailyNotification; let mockPlugin: jest.Mocked; const mockOptions: NotificationOptions = { url: 'https://api.example.com/daily-content', time: '08:00', title: 'Test Notification', body: 'This is a test notification' }; 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 jest.clearAllMocks(); }); describe('scheduleDailyNotification', () => { it('should schedule a basic notification', async () => { await plugin.scheduleDailyNotification(mockOptions); // Verify the mock was called with correct parameters expect(mockPlugin.scheduleDailyNotification).toHaveBeenCalledWith(mockOptions); }); it('should handle network errors gracefully', async () => { mockPlugin.scheduleDailyNotification.mockRejectedValueOnce( new Error('Network error') ); const errorOptions: NotificationOptions = { ...mockOptions, url: 'https://invalid-url.com' }; await expect(plugin.scheduleDailyNotification(errorOptions)) .rejects .toThrow('Network error'); }); it('should validate required parameters', async () => { const invalidOptions = { time: '08:00' } as NotificationOptions; await expect(plugin.scheduleDailyNotification(invalidOptions)) .rejects .toThrow('URL is required'); }); }); describe('getLastNotification', () => { it('should return the last notification', async () => { const mockResponse: NotificationResponse = { id: 'test-notification', title: 'Last Notification', body: 'This was the last notification', timestamp: Date.now() }; mockPlugin.getLastNotification.mockResolvedValueOnce(mockResponse); const result = await plugin.getLastNotification(); expect(result).toEqual(mockResponse); }); it('should return null when no notifications exist', async () => { mockPlugin.getLastNotification.mockResolvedValueOnce(null); const result = await plugin.getLastNotification(); expect(result).toBeNull(); }); }); describe('cancelAllNotifications', () => { it('should cancel all scheduled notifications', async () => { await plugin.cancelAllNotifications(); // Verify the mock was called expect(mockPlugin.cancelAllNotifications).toHaveBeenCalled(); }); }); describe('getNotificationStatus', () => { it('should return current notification status', async () => { const mockStatus: NotificationStatus = { isScheduled: true, nextNotificationTime: Date.now() + 86400000, // 24 hours from now lastNotificationTime: Date.now(), settings: {} }; mockPlugin.getNotificationStatus.mockResolvedValueOnce(mockStatus); const result = await plugin.getNotificationStatus(); expect(result).toEqual(mockStatus); }); it('should handle error status', async () => { const mockErrorStatus: NotificationStatus = { isScheduled: false, error: 'Failed to schedule notification', lastNotificationTime: 0, nextNotificationTime: 0, settings: {} }; mockPlugin.getNotificationStatus.mockResolvedValueOnce(mockErrorStatus); const result = await plugin.getNotificationStatus(); expect(result).toEqual(mockErrorStatus); }); }); describe('updateSettings', () => { it('should update notification settings', async () => { const settings: NotificationSettings = { sound: false, priority: 'high', timezone: 'UTC' }; await plugin.updateSettings(settings); expect(mockPlugin.updateSettings).toHaveBeenCalledWith(settings); }); }); describe('getBatteryStatus', () => { it('should return battery status', async () => { const mockBatteryStatus = { level: 85, isCharging: false, powerState: 1, isOptimizationExempt: false }; mockPlugin.getBatteryStatus.mockResolvedValueOnce(mockBatteryStatus); const result = await plugin.getBatteryStatus(); expect(result).toEqual(mockBatteryStatus); }); }); describe('requestBatteryOptimizationExemption', () => { it('should request battery optimization exemption', async () => { await plugin.requestBatteryOptimizationExemption(); expect(mockPlugin.requestBatteryOptimizationExemption).toHaveBeenCalled(); }); }); describe('setAdaptiveScheduling', () => { it('should set adaptive scheduling', async () => { await plugin.setAdaptiveScheduling({ enabled: true }); expect(mockPlugin.setAdaptiveScheduling).toHaveBeenCalledWith({ enabled: true }); }); }); describe('getPowerState', () => { it('should return power state', async () => { const mockPowerState = { powerState: 1, 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'); }); }); });