UNPKG

@critters/next

Version:

Secure bug reporting library for Next.js applications

213 lines (212 loc) 10.1 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); /** * Unit tests for Critters reporter */ const reporter_1 = require("../src/reporter"); const config_1 = require("../src/config"); // Mock config module jest.mock("../src/config", () => { const originalModule = jest.requireActual("../src/config"); return Object.assign(Object.assign({}, originalModule), { BugTrackerConfig: { init: jest.fn(), getInstance: jest.fn().mockImplementation(() => ({ isEnabled: jest.fn().mockReturnValue(true), isDebugMode: jest.fn().mockReturnValue(false), getApiKey: jest.fn().mockReturnValue("mock-api-key"), getSecretKey: jest.fn().mockReturnValue("mock-secret-key"), getEndpoint: jest .fn() .mockReturnValue("https://mock-endpoint.example.com"), getProjectId: jest.fn().mockReturnValue("mock-project-id"), getEnvironment: jest.fn().mockReturnValue("test"), getOnBeforeReport: jest.fn().mockReturnValue(null), getOnAfterReport: jest.fn().mockReturnValue(null), })), } }); }); // Mock utils module jest.mock("../src/utils", () => ({ isBrowser: jest.fn().mockReturnValue(true), isServer: jest.fn().mockReturnValue(false), getBrowserInfo: jest.fn().mockReturnValue({ name: "chrome", version: "100.0.0", os: "windows", device: "desktop", viewport: { width: 1920, height: 1080 }, }), formatError: jest.fn().mockImplementation((error) => ({ name: error.name || "Error", message: error.message || "Unknown error", stack: error.stack || "mock-stack-trace", })), generateSignature: jest.fn().mockReturnValue("mock-signature"), safeStringify: jest.fn().mockImplementation(JSON.stringify), sanitizeData: jest.fn().mockImplementation((data) => data), getTimestamp: jest.fn().mockReturnValue("2023-01-01T12:00:00.000Z"), debugLog: jest.fn(), })); // Mock fetch global.fetch = jest.fn(); describe("Critters Reporter", () => { let reporter; beforeEach(() => { // Reset mocks jest.clearAllMocks(); // Mock successful fetch response global.fetch.mockResolvedValue({ ok: true, status: 200, json: () => __awaiter(void 0, void 0, void 0, function* () { return ({ success: true, message: "Bug report submitted successfully", reportId: "mock-report-id", }); }), }); // Reset singleton instance between tests // @ts-ignore - Accessing private property for testing reporter_1.Critters.instance = undefined; // Get reporter instance reporter = reporter_1.Critters.getInstance(); }); describe("getInstance", () => { it("should create a singleton instance", () => { const instance1 = reporter_1.Critters.getInstance(); const instance2 = reporter_1.Critters.getInstance(); expect(instance1).toBe(instance2); }); }); describe("catch", () => { it("should format and send error report", () => __awaiter(void 0, void 0, void 0, function* () { const error = new Error("Test error"); const metadata = { context: "test-context" }; const result = yield reporter.catch(error, metadata); // Verify report was formatted correctly expect(global.fetch).toHaveBeenCalledTimes(1); const [url, options] = global.fetch.mock.calls[0]; expect(url).toBe("https://mock-endpoint.example.com"); expect(options.method).toBe("POST"); expect(options.headers).toEqual({ "Content-Type": "application/json", "X-API-Key": "mock-api-key", "X-Signature": "mock-signature", }); // Parse the sent payload const payload = JSON.parse(options.body); // Verify key fields were set expect(payload.title).toBe("Error: Test error"); expect(payload.error_message).toBe("Test error"); expect(payload.source).toBe("CLIENT"); expect(payload.stack_trace).toBe("mock-stack-trace"); expect(payload.metadata).toEqual({ context: "test-context" }); expect(payload.browser_info).toBeDefined(); expect(payload.timestamp).toBe("2023-01-01T12:00:00.000Z"); expect(payload.projectId).toBe("mock-project-id"); // Verify result expect(result).toEqual({ success: true, message: "Bug report submitted successfully", reportId: "mock-report-id", timestamp: "2023-01-01T12:00:00.000Z", }); })); it("should handle API errors", () => __awaiter(void 0, void 0, void 0, function* () { // Mock failed API response global.fetch.mockResolvedValue({ ok: false, status: 500, statusText: "Internal Server Error", }); const error = new Error("Test error"); const result = yield reporter.catch(error); expect(result).toEqual({ success: false, message: "Failed to submit bug report: 500 Internal Server Error", timestamp: "2023-01-01T12:00:00.000Z", }); })); it("should handle network errors", () => __awaiter(void 0, void 0, void 0, function* () { // Mock network error global.fetch.mockRejectedValue(new Error("Network error")); const error = new Error("Test error"); const result = yield reporter.catch(error); expect(result).toEqual({ success: false, message: "Failed to submit bug report: Network error", timestamp: "2023-01-01T12:00:00.000Z", }); })); it("should not send report when bug tracker is disabled", () => __awaiter(void 0, void 0, void 0, function* () { // Mock disabled config jest .spyOn(config_1.BugTrackerConfig.getInstance(), "isEnabled") .mockReturnValue(false); const error = new Error("Test error"); const result = yield reporter.catch(error); expect(global.fetch).not.toHaveBeenCalled(); expect(result).toEqual({ success: false, message: "Bug tracker is disabled", timestamp: "2023-01-01T12:00:00.000Z", }); })); it("should handle onBeforeReport hook", () => __awaiter(void 0, void 0, void 0, function* () { // Mock onBeforeReport hook const mockOnBeforeReport = jest.fn().mockImplementation((report) => (Object.assign(Object.assign({}, report), { severity: "HIGH", title: "Modified title" }))); jest .spyOn(config_1.BugTrackerConfig.getInstance(), "getOnBeforeReport") .mockReturnValue(mockOnBeforeReport); const error = new Error("Test error"); yield reporter.catch(error); // Verify hook was called expect(mockOnBeforeReport).toHaveBeenCalledTimes(1); // Check that the modified report was sent const [, options] = global.fetch.mock.calls[0]; const payload = JSON.parse(options.body); expect(payload.severity).toBe("HIGH"); expect(payload.title).toBe("Modified title"); })); it("should cancel report when onBeforeReport returns false", () => __awaiter(void 0, void 0, void 0, function* () { // Mock onBeforeReport hook that cancels report const mockOnBeforeReport = jest.fn().mockReturnValue(false); jest .spyOn(config_1.BugTrackerConfig.getInstance(), "getOnBeforeReport") .mockReturnValue(mockOnBeforeReport); const error = new Error("Test error"); const result = yield reporter.catch(error); // Verify hook was called expect(mockOnBeforeReport).toHaveBeenCalledTimes(1); // Check that fetch was not called expect(global.fetch).not.toHaveBeenCalled(); expect(result).toEqual({ success: false, message: "Report cancelled by onBeforeReport hook", timestamp: "2023-01-01T12:00:00.000Z", }); })); it("should call onAfterReport hook with report and result", () => __awaiter(void 0, void 0, void 0, function* () { // Mock onAfterReport hook const mockOnAfterReport = jest.fn(); jest .spyOn(config_1.BugTrackerConfig.getInstance(), "getOnAfterReport") .mockReturnValue(mockOnAfterReport); const error = new Error("Test error"); const result = yield reporter.catch(error); // Verify hook was called with report and result expect(mockOnAfterReport).toHaveBeenCalledTimes(1); expect(mockOnAfterReport.mock.calls[0][1]).toEqual(result); })); }); });