import { TestBed } from '@angular/core/testing';
import { NotificationService } from './notification.service';
describe('NotificationService', () => {
let service: NotificationService;
let mockNotification: any;
beforeEach(() => {
// Mock Notification API
mockNotification = jest.fn();
mockNotification.permission = 'default';
mockNotification.requestPermission = jest.fn();
(global as any).Notification = mockNotification;
TestBed.configureTestingModule({
providers: [NotificationService]
});
service = TestBed.inject(NotificationService);
});
describe('requestPermission', () => {
it('should request notification permission', async () => {
mockNotification.requestPermission = jest.fn().mockResolvedValue('granted');
const result = await service.requestPermission();
expect(mockNotification.requestPermission).toHaveBeenCalled();
expect(result).toBe('granted');
expect(service.permission()).toBe('granted');
});
it('should handle permission denial', async () => {
mockNotification.requestPermission = jest.fn().mockResolvedValue('denied');
const result = await service.requestPermission();
expect(result).toBe('denied');
expect(service.permission()).toBe('denied');
});
it('should handle browsers without notification support', async () => {
// Save original Notification
const originalNotification = (global as any).Notification;
// Remove Notification
delete (global as any).Notification;
// Create a new service instance
const testService = new NotificationService();
const result = await testService.requestPermission();
expect(result).toBe('denied');
expect(testService.permission()).toBe('denied');
// Restore Notification
(global as any).Notification = originalNotification;
});
});
describe('showNotification', () => {
it('should show notification when permitted', () => {
mockNotification.permission = 'granted';
service['permission'].set('granted'); // Update the signal
const mockInstance = { close: jest.fn() };
mockNotification.mockReturnValue(mockInstance);
service.showNotification('Test Title', {
body: 'Test body',
icon: '/icon.png'
});
expect(mockNotification).toHaveBeenCalledWith('Test Title', {
body: 'Test body',
icon: '/icon.png',
tag: undefined,
requireInteraction: false
});
});
it('should not show notification when not permitted', () => {
mockNotification.permission = 'denied';
service['permission'].set('denied'); // Update the signal
service.showNotification('Test Title', {
body: 'Test body'
});
expect(mockNotification).not.toHaveBeenCalled();
});
it('should handle notification click', () => {
mockNotification.permission = 'granted';
service['permission'].set('granted'); // Update the signal
const mockInstance = {
onclick: null,
close: jest.fn()
};
mockNotification.mockReturnValue(mockInstance);
const onClick = jest.fn();
const windowFocusSpy = jest.spyOn(window, 'focus').mockImplementation();
service.showNotification('Test Title', {
body: 'Test body',
onClick
});
// Simulate click
mockInstance.onclick({ preventDefault: jest.fn() });
expect(onClick).toHaveBeenCalled();
expect(mockInstance.close).toHaveBeenCalled();
expect(windowFocusSpy).toHaveBeenCalled();
windowFocusSpy.mockRestore();
});
it('should auto-close notification after timeout', () => {
jest.useFakeTimers();
mockNotification.permission = 'granted';
service['permission'].set('granted'); // Update the signal
const mockInstance = { close: jest.fn() };
mockNotification.mockReturnValue(mockInstance);
service.showNotification('Test Title', {
body: 'Test body',
timeout: 5000
});
// Execute the timeout
jest.advanceTimersByTime(5000);
expect(mockInstance.close).toHaveBeenCalled();
jest.useRealTimers();
});
});
describe('notifyNewRequest', () => {
it('should show notification for new request', () => {
mockNotification.permission = 'granted';
service['permission'].set('granted'); // Update the signal
const mockInstance = { close: jest.fn() };
mockNotification.mockReturnValue(mockInstance);
const request = {
id: 'req-1',
sessionId: 'session-1',
question: 'Should we proceed with deployment?',
timestamp: new Date()
};
service.notifyNewRequest(request);
expect(mockNotification).toHaveBeenCalledWith(
'New MCP Request',
expect.objectContaining({
body: 'Should we proceed with deployment?',
icon: '/assets/icons/mcp-icon.png',
requireInteraction: true
})
);
});
it('should truncate long questions', () => {
mockNotification.permission = 'granted';
service['permission'].set('granted'); // Update the signal
const mockInstance = { close: jest.fn() };
mockNotification.mockReturnValue(mockInstance);
const longQuestion = 'a'.repeat(200);
const request = {
id: 'req-1',
sessionId: 'session-1',
question: longQuestion,
timestamp: new Date()
};
service.notifyNewRequest(request);
expect(mockNotification).toHaveBeenCalledWith(
'New MCP Request',
expect.objectContaining({
body: expect.stringContaining('...')
})
);
});
});
describe('isSupported', () => {
it('should return true when Notification API is available', () => {
expect(service.isSupported()).toBe(true);
});
it('should return false when Notification API is not available', () => {
// Save original Notification
const originalNotification = (global as any).Notification;
// Remove Notification
delete (global as any).Notification;
// Create a new instance of the service after removing Notification
const newService = new NotificationService();
expect(newService.isSupported()).toBe(false);
// Restore Notification
(global as any).Notification = originalNotification;
});
});
});