1 /* eslint-disable no-console */
2 import { traceError } from '@proton/shared/lib/helpers/sentry';
4 import { Logger } from './logs';
6 jest.mock('@proton/shared/lib/helpers/sentry', () => ({
10 const mockTraceError = jest.mocked(traceError);
12 describe('Logger', () => {
16 logger = new Logger('test-logger');
17 console.log = jest.fn();
18 console.warn = jest.fn();
19 console.error = jest.fn();
24 localStorage.removeItem('test-logger-debug');
27 test('should log debug messages if verbose is true', () => {
28 localStorage.setItem('test-logger-debug', 'true');
29 logger = new Logger('test-logger', 'test-logger-debug');
30 logger.debug('This is a debug message');
32 expect(console.log).toHaveBeenCalledTimes(1);
33 expect(logger.getLogs()).toContain('This is a debug message');
36 test('should not log debug messages if verbose is false', () => {
37 localStorage.setItem('test-logger-debug', 'false');
38 logger = new Logger('test-logger', 'test-logger-debug');
39 logger.debug('This is a debug message');
41 expect(console.log).toHaveBeenCalledTimes(0);
42 expect(logger.getLogs()).not.toContain('This is a debug message');
45 test('should log info messages', () => {
46 logger.info('This is an info message');
48 expect(console.log).toHaveBeenCalledTimes(1);
49 expect(logger.getLogs()).toContain('This is an info message');
52 test('should log warn messages', () => {
53 logger.warn('This is a warn message');
55 expect(console.warn).toHaveBeenCalledWith('This is a warn message');
56 expect(logger.getLogs()).toContain('This is a warn message');
59 test('should log error messages', () => {
60 const error = new Error('This is an error message');
63 expect(console.error).toHaveBeenCalledWith(error);
64 expect(logger.getLogs()).toContain('This is an error message');
67 test('should log error messages with no Error object', () => {
68 logger.error('This is an error message');
70 expect(console.error).toHaveBeenCalledWith('This is an error message');
71 expect(logger.getLogs()).toContain('This is an error message');
72 expect(mockTraceError).toHaveBeenCalledWith(expect.any(Error), {
79 test('should log error messages with Error and extras', () => {
80 const error = new Error('This is an error message');
81 logger.error(error, 'extra', 123);
83 expect(console.error).toHaveBeenCalledWith(error, 'extra', 123);
84 expect(logger.getLogs()).toContain('This is an error message');
85 expect(mockTraceError).toHaveBeenCalledWith(expect.any(Error), {
96 test('should not log if enabled is false', () => {
97 logger.setEnabled(false);
98 logger.info('This is an info message');
100 expect(console.log).not.toHaveBeenCalled();
101 expect(logger.getLogs()).not.toContain('This is an info message');
104 test('should not warn if enabled is false', () => {
105 logger.setEnabled(false);
106 logger.warn('This is a warn message');
108 expect(console.warn).not.toHaveBeenCalled();
109 expect(logger.getLogs()).not.toContain('This is a warn message');
112 test('should not error if enabled is false', () => {
113 logger.setEnabled(false);
114 logger.error('This is an error message');
116 expect(console.error).not.toHaveBeenCalled();
117 expect(logger.getLogs()).not.toContain('This is an error message');
120 test('should save logs', () => {
121 logger.info('First message');
122 logger.info('Second message');
124 const logs = logger.getLogs();
125 expect(logs).toContain('First message');
126 expect(logs).toContain('Second message');
129 test('should save logs until limit is reached', () => {
130 logger = new Logger('test-logger', undefined, 2);
131 logger.info('First message');
132 logger.info('Second message');
133 logger.info('Third message');
135 const logs = logger.getLogs();
136 expect(logs).not.toContain('First message');
137 expect(logs).toContain('Second message');
138 expect(logs).toContain('Third message');
141 test('should save logs with objects', () => {
142 const obj = { hello: 'world', test: true, docs: 123 };
143 logger.info('First message');
144 logger.info('Second message', obj);
146 const logs = logger.getLogs();
147 expect(logs).toContain('First message');
148 expect(logs).toContain(`Second message hello:world test:true docs:123`);
151 test('should clear logs', () => {
152 logger.info('First message');
155 expect(logger.getLogs()).toBe('');
158 test('should not add duplicate messages but increase times', () => {
159 logger.info('Repeated message');
160 logger.info('Repeated message');
162 const logs = logger.getLogs();
163 expect(logs).toContain('Repeated message');
165 logs.indexOf(new Date().getFullYear().toString()) !== logs.lastIndexOf(new Date().getFullYear().toString())
169 test('should download logs', () => {
170 const createElementSpy = jest.spyOn(document, 'createElement');
172 logger.info('Message to download');
173 logger.downloadLogs();
175 expect(createElementSpy).toHaveBeenCalled();
176 createElementSpy.mockRestore();
179 test('should not download logs if not logs', () => {
180 const createElementSpy = jest.spyOn(document, 'createElement');
182 logger.downloadLogs();
184 expect(createElementSpy).not.toHaveBeenCalled();
185 createElementSpy.mockRestore();
188 test('should call downloadLogs on Ctrl+Shift+H in main frame', () => {
189 const createElementSpy = jest.spyOn(document, 'createElement');
190 // Simulate Ctrl+Shift+H keydown event
191 const event = new KeyboardEvent('keydown', {
196 window.dispatchEvent(event);
198 // Assert that downloadLogs has been called
199 expect(createElementSpy).toHaveBeenCalled();
202 test('should call downloadLogs on message event in main frame', () => {
203 const createElementSpy = jest.spyOn(document, 'createElement');
204 // Simulate a message event from a child frame
205 const event = new MessageEvent('message', {
206 data: { type: '@proton/utils/logs:downloadLogs' },
208 window.dispatchEvent(event);
210 // Assert that downloadLogs has been called
211 expect(createElementSpy).toHaveBeenCalled();
214 test('should log report on main frame when receiving message from child frames', () => {
215 const err = new Error('test');
217 const event = new MessageEvent('message', {
218 data: { type: '@proton/utils/logs:report', tag, args: [err] },
220 window.dispatchEvent(event);
221 expect(mockTraceError).toHaveBeenCalledWith(err, {
229 test('should log report on main frame when receiving message from child frames in the wrong format', () => {
230 const err = new Error('test');
231 const event = new MessageEvent('message', {
232 data: { type: '@proton/utils/logs:report', not: 'good format', args: [err] },
234 window.dispatchEvent(event);
235 expect(mockTraceError).toHaveBeenCalledWith(
236 new Error('@proton/utils/logs:report message does not contain args or is not spreadable'),