/** * @jest-environment jsdom */ import { Logger } from '../../resources/js/core/logger.js'; // Mock the frameloop dependency jest.mock('../../resources/js/core/frameloop.js', () => ({ monitor: { log: jest.fn() } })); import { monitor } from '../../resources/js/core/frameloop.js'; describe('Logger', () => { beforeEach(() => { Logger.enabled = true; jest.clearAllMocks(); }); describe('Basic logging functionality', () => { test('should log messages when enabled', () => { const consoleSpy = jest.spyOn(console, 'log').mockImplementation(); Logger.log('test message'); expect(consoleSpy).toHaveBeenCalledWith( expect.stringContaining('[LOG]') ); expect(consoleSpy).toHaveBeenCalledWith( expect.stringContaining('test message') ); consoleSpy.mockRestore(); }); test('should not log messages when disabled', () => { Logger.enabled = false; const consoleSpy = jest.spyOn(console, 'info').mockImplementation(); Logger.info('test message'); expect(consoleSpy).not.toHaveBeenCalled(); consoleSpy.mockRestore(); }); test('should handle different log levels', () => { const methods = ['log', 'warn', 'info', 'error']; methods.forEach(method => { const consoleSpy = jest.spyOn(console, method).mockImplementation(); Logger[method]('test message'); expect(consoleSpy).toHaveBeenCalledWith( expect.stringContaining(`[${method.toUpperCase()}]`) ); consoleSpy.mockRestore(); }); }); test('should format timestamps correctly', () => { const consoleSpy = jest.spyOn(console, 'info').mockImplementation(); Logger.info('test message'); const logCall = consoleSpy.mock.calls[0][0]; expect(logCall).toMatch(/\[\d{2}:\d{2}:\d{2}\]/); consoleSpy.mockRestore(); }); test('should stringify objects', () => { const consoleSpy = jest.spyOn(console, 'info').mockImplementation(); const testObj = { key: 'value', number: 42 }; Logger.info('Object:', testObj); const logCall = consoleSpy.mock.calls[0][0]; expect(logCall).toContain('{"key":"value","number":42}'); consoleSpy.mockRestore(); }); test('should handle mixed argument types', () => { const consoleSpy = jest.spyOn(console, 'log').mockImplementation(); Logger.log('Message:', 42, { key: 'value' }, true); const logCall = consoleSpy.mock.calls[0][0]; expect(logCall).toContain('Message: 42 {"key":"value"} true'); consoleSpy.mockRestore(); }); }); describe('Monitor integration', () => { test('should call monitor.log when available', () => { const consoleSpy = jest.spyOn(console, 'info').mockImplementation(); Logger.info('test message'); expect(monitor.log).toHaveBeenCalledWith( expect.stringContaining('test message') ); consoleSpy.mockRestore(); }); test('should handle missing monitor gracefully', () => { // Temporarily remove monitor const originalMonitor = monitor.log; monitor.log = undefined; const consoleSpy = jest.spyOn(console, 'info').mockImplementation(); expect(() => { Logger.info('test message'); }).not.toThrow(); monitor.log = originalMonitor; consoleSpy.mockRestore(); }); }); describe('Error handling', () => { test('should handle console method not available', () => { const originalConsole = console.debug; console.debug = undefined; expect(() => { Logger._write('debug', '[DEBUG]', ['test']); }).not.toThrow(); console.debug = originalConsole; }); test('should handle JSON.stringify errors', () => { const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); // Create circular reference const circular = {}; circular.self = circular; expect(() => { Logger.error('Circular:', circular); }).not.toThrow(); consoleSpy.mockRestore(); }); }); describe('Performance', () => { test('should be performant with many log calls', () => { Logger.enabled = false; // Disable actual logging for performance test const start = performance.now(); for (let i = 0; i < 1000; i++) { Logger.info(`Log message ${i}`); } const end = performance.now(); const duration = end - start; // Should complete 1000 calls in under 100ms when disabled expect(duration).toBeLessThan(100); }); }); });