Files
kandev/apps/web/lib/logger/intercept.test.ts
T
2026-05-01 15:18:18 +01:00

105 lines
3.0 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import { _resetForTesting, snapshotLogs } from "./buffer";
import { _resetInstalledForTesting, installConsoleInterceptor } from "./intercept";
describe("console interceptor", () => {
// Hold originals so each test starts with a clean console.
const originals = {
debug: console.debug,
info: console.info,
warn: console.warn,
error: console.error,
};
let infoSpy: ReturnType<typeof vi.fn>;
let warnSpy: ReturnType<typeof vi.fn>;
let errorSpy: ReturnType<typeof vi.fn>;
beforeEach(() => {
_resetForTesting();
_resetInstalledForTesting();
infoSpy = vi.fn();
warnSpy = vi.fn();
errorSpy = vi.fn();
console.debug = vi.fn();
console.info = infoSpy;
console.warn = warnSpy;
console.error = errorSpy;
});
afterEach(() => {
Object.assign(console, originals);
});
it("captures console.* into the ring buffer and still calls the original", () => {
installConsoleInterceptor();
console.info("hello", { user: "x" });
console.warn("careful");
console.error(new Error("boom"));
const snap = snapshotLogs();
expect(snap).toHaveLength(3);
expect(snap[0].level).toBe("info");
expect(snap[0].message).toBe("hello");
expect(snap[0].source).toBe("console");
expect(snap[1].level).toBe("warn");
expect(snap[2].level).toBe("error");
expect(snap[2].message).toBe("boom");
// Error stack must survive on the entry so the bundle preserves it.
expect(typeof snap[2].stack).toBe("string");
expect(snap[2].stack).toContain("Error");
expect(infoSpy).toHaveBeenCalledWith("hello", { user: "x" });
expect(warnSpy).toHaveBeenCalledWith("careful");
expect(errorSpy).toHaveBeenCalled();
});
it("is idempotent — second install does not double-wrap", () => {
installConsoleInterceptor();
installConsoleInterceptor();
console.info("once");
expect(snapshotLogs()).toHaveLength(1);
});
it("captures window error events", () => {
installConsoleInterceptor();
const err = new Error("explode");
window.dispatchEvent(
new ErrorEvent("error", {
message: err.message,
error: err,
filename: "x.ts",
lineno: 1,
colno: 2,
}),
);
const snap = snapshotLogs();
expect(snap.some((e) => e.source === "window.onerror" && e.message === "explode")).toBe(true);
});
it("captures unhandled promise rejections", () => {
installConsoleInterceptor();
const reason = new Error("rejected");
const event = new Event("unhandledrejection") as Event & {
reason: unknown;
promise: Promise<unknown>;
};
Object.defineProperty(event, "reason", { value: reason });
Object.defineProperty(event, "promise", {
value: Promise.reject(reason).catch(() => undefined),
});
window.dispatchEvent(event);
const snap = snapshotLogs();
expect(snap.some((e) => e.source === "unhandledrejection" && e.message === "rejected")).toBe(
true,
);
});
});