import { expect, type Mock, test as baseTest, vi } from "vitest"; import { LogLevel } from "../../../types/logger-interface"; import { loggerFactory } from "./logger.client"; // Start up the logger lib with defaults only const Logger = loggerFactory(); const test = baseTest .extend<{ logger: InstanceType; SentryMock: { captureException: Mock }; }>({ logger: async ({}, use) => { const logger = new Logger("testClient"); await use(logger); }, SentryMock: async ({}, use) => { const SentryMock = { captureException: vi.fn(), }; await use(SentryMock); }, }) .extend<{ sentryLogger: InstanceType }>({ sentryLogger: async ({ SentryMock }, use) => { const SentryLogger = loggerFactory( { console: { enabled: true, }, }, SentryMock as any, ); await use(new SentryLogger("sentryClient")); }, }); test("console - should default to being silent", () => { expect(Logger._silent).toBe(true); }); test('console - should default to level "info"', () => { expect(Logger._level).toBe(LogLevel.Info); }); test("replicant - should default to false", () => { expect(Logger._shouldLogReplicants).toBe(false); }); test("replicant - should do nothing when Logger._shouldLogReplicants is false", ({ logger, }) => { const info = vi.spyOn(console, "info"); logger.replicants("replicants"); expect(info).not.toBeCalled(); }); test("logging methods should all do nothing when _silent is true", ({ logger, }) => { Logger._silent = true; Logger._level = LogLevel.Trace; const info = vi.spyOn(console, "info"); // Trace logger.trace("trace"); expect(info).not.toBeCalled(); info.mockClear(); // Debug logger.debug("debug"); expect(info).not.toBeCalled(); info.mockClear(); // Info logger.info("info"); expect(info).not.toBeCalled(); info.mockClear(); // Warn const warn = vi.spyOn(console, "warn"); logger.warn("warn"); expect(warn).not.toBeCalled(); warn.mockRestore(); // Error const error = vi.spyOn(console, "error"); logger.error("error"); expect(error).not.toBeCalled(); error.mockRestore(); // Replicants logger.replicants("replicants"); expect(info).not.toBeCalled(); info.mockRestore(); }); test("logging methods should all do nothing when the log level is above them", ({ logger, }) => { Logger._silent = false; Logger._level = LogLevel.Silent; const info = vi.spyOn(console, "info"); // Trace logger.trace("trace"); expect(info).not.toBeCalled(); info.mockClear(); // Debug logger.debug("debug"); expect(info).not.toBeCalled(); info.mockClear(); // Info logger.info("info"); expect(info).not.toBeCalled(); info.mockClear(); // Warn const warn = vi.spyOn(console, "warn"); logger.warn("warn"); expect(warn).not.toBeCalled(); warn.mockRestore(); // Error const error = vi.spyOn(console, "error"); logger.error("error"); expect(error).not.toBeCalled(); error.mockRestore(); info.mockRestore(); }); test("logging methods should all prepend the instance name to the output", ({ logger, }) => { Logger._level = LogLevel.Trace; Logger._silent = false; const info = vi.spyOn(console, "info"); // Trace logger.trace("trace"); expect(info).toBeCalledWith("[testClient]", "trace"); info.mockClear(); // Debug logger.debug("debug"); expect(info).toBeCalledWith("[testClient]", "debug"); info.mockClear(); // Info logger.info("info"); expect(info).toBeCalledWith("[testClient]", "info"); info.mockClear(); // Warn const warn = vi.spyOn(console, "warn"); logger.warn("warn"); expect(warn).toBeCalledWith("[testClient]", "warn"); warn.mockRestore(); // Error const error = vi.spyOn(console, "error"); logger.error("error"); expect(error).toBeCalledWith("[testClient]", "error"); error.mockRestore(); // Replicants Logger._shouldLogReplicants = true; logger.replicants("replicants"); expect(info).toBeCalledWith("[testClient]", "replicants"); Logger._shouldLogReplicants = false; info.mockRestore(); }); test("Sentry - should log errors to Sentry when global.sentryEnabled is true", ({ sentryLogger, SentryMock, }) => { sentryLogger.error("error message"); expect(SentryMock.captureException).toBeCalledTimes(1); expect(SentryMock.captureException.mock.calls[0]![0]).toBeInstanceOf(Error); expect( SentryMock.captureException.mock.calls[0]![0].message, ).toMatchInlineSnapshot(`"[sentryClient] error message"`); }); test("Sentry - should prettyprint objects", ({ sentryLogger, SentryMock }) => { sentryLogger.error("error message:", { foo: { bar: "baz" } }); expect( SentryMock.captureException.mock.calls[0]![0].message, ).toMatchInlineSnapshot( `"[sentryClient] error message: { foo: { bar: 'baz' } }"`, ); });