Files
OmniRoute/tests/unit/dashboard/payload-section-collapsible-json.test.tsx
Markus Hartung 5684589ce7 feat(dashboard): collapsible JSON tree viewer for request/response payloads (#11703)
Resynced onto the release tip. Two fixes applied during boarding: (1) the branch forked before the recent optionalDependencies placement of @huggingface/transformers and onnxruntime-node — its own diff re-added both into "dependencies" as duplicates alongside the real new dependency (react18-json-view); removed the duplicates, ran npm install to sync the lockfile. (2) config/quality/dependency-allowlist.json referenced the wrong package name (react-json-view-lite, an earlier iteration per the PR body) — the code actually imports react18-json-view; fixed the allowlist entry to match. RequestLoggerDetail.tsx crossed its frozen file-size cap (1018->1111); rebaselined with a note — the PR does split out the new logic (RequestLoggerDetail.sections.tsx, JsonTreeExpandControls.tsx, useTimestampTitles.ts, jsonTreeExpandStore.ts, all well under cap), the growth here is irreducible wiring. typecheck:core, check:dashboard-typecheck, check:file-size, check-deps all green after resync; 8/8 vitest + 11/11 native tests pass. Nice, well-structured 6-commit feature with full i18n and good test coverage. Thanks!
2026-08-30 04:26:15 -03:00

82 lines
2.8 KiB
TypeScript

// @vitest-environment jsdom
//
// Regression guard: PayloadSection rendered every payload (request/response
// bodies, pipeline stages) as a flat <pre> text dump, forcing operators to
// scroll through the entire raw JSON to find anything in a nested request.
// It now renders parseable JSON payloads as a collapsible tree
// (react-json-view-lite) instead, while still falling back to the plain
// <pre> dump for anything that isn't valid JSON (e.g. a captured error
// string), since the `json` prop is display text that isn't guaranteed
// parseable.
import React, { act } from "react";
import { createRoot, type Root } from "react-dom/client";
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
vi.mock("next-intl", () => ({
useTranslations: () => (key: string) => key,
}));
vi.mock("@/shared/hooks/useTheme", () => ({
useTheme: () => ({ isDark: false }),
}));
const { PayloadSection } =
await import("../../../src/shared/components/RequestLoggerDetail.sections.tsx");
let container: HTMLDivElement;
let root: Root;
beforeEach(() => {
container = document.createElement("div");
document.body.appendChild(container);
root = createRoot(container);
});
afterEach(() => {
act(() => root.unmount());
container.remove();
vi.clearAllMocks();
});
function renderPayload(json: string) {
act(() => {
root.render(
<PayloadSection title="Test Payload" json={json} onCopy={vi.fn().mockResolvedValue(true)} />
);
});
}
describe("PayloadSection collapsible JSON rendering", () => {
it("renders a valid JSON object payload as a collapsible tree, not a flat <pre> dump", () => {
renderPayload(JSON.stringify({ model: "gpt-5.6-luna", input: [{ role: "user" }] }, null, 2));
expect(container.querySelector("pre")).toBeNull();
// react-json-view-lite renders expand/collapse controls for object nodes.
expect(container.querySelectorAll("[aria-label]").length).toBeGreaterThan(0);
expect(container.textContent).toContain("model");
expect(container.textContent).toContain("gpt-5.6-luna");
});
it("renders a valid JSON array payload as a collapsible tree", () => {
renderPayload(JSON.stringify([{ type: "output_text", text: "answer" }], null, 2));
expect(container.querySelector("pre")).toBeNull();
expect(container.textContent).toContain("output_text");
});
it("falls back to the plain <pre> dump for non-JSON payload text", () => {
renderPayload("Upstream request failed: Model is unavailable.");
const pre = container.querySelector("pre");
expect(pre).not.toBeNull();
expect(pre?.textContent).toBe("Upstream request failed: Model is unavailable.");
});
it("falls back to the plain <pre> dump for a JSON primitive (not an object/array)", () => {
renderPayload(JSON.stringify("just a string"));
const pre = container.querySelector("pre");
expect(pre).not.toBeNull();
});
});