// @vitest-environment jsdom // // E22 S5: "Saját napló" shows this tab's own log live, newest first, filters it by level group and // text, and copies exactly what is filtered (oldest first, one JSON event per line). import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { act, cleanup, fireEvent, render, screen, within } from "@testing-library/react"; import { clear, log, setLevel } from "../lib/log"; vi.mock("react-i18next", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, useTranslation: () => ({ t: (key: string, opts?: { count?: number }) => opts?.count != null ? `${key}:${opts.count}` : key, }), }; }); const copied: string[] = []; vi.mock("../lib/clipboard", () => ({ copyText: (text: string) => { copied.push(text); return Promise.resolve(true); }, })); import MyLogViewer from "./MyLogViewer"; beforeEach(() => { setLevel("DEBUG"); log.info("nav.view", "push feed", { page: "feed" }); log.warn("http.request", "GET /api/feed 503", { status: 503 }); log.debug("ui.click", "click feed-refresh", { control: "feed-refresh" }); }); afterEach(() => { cleanup(); clear(); setLevel("INFO"); copied.length = 0; }); const rows = () => within(screen.getByTestId("my-log-lines")) .queryAllByRole("button") .map((b) => b.textContent); describe("MyLogViewer", () => { it("lists the tab's events newest first and follows new ones", async () => { render(); expect(rows().map((r) => r!.includes("ui.click"))).toEqual([true, false, false]); await act(async () => { log.error("app.uncaught", "TypeError: x is undefined"); await new Promise((r) => requestAnimationFrame(() => r(null))); }); expect(rows()[0]).toContain("app.uncaught"); }); it("a row keeps its place and the focus as new events arrive above it", async () => { render(); const row = within(screen.getByTestId("my-log-lines")).getAllByRole("button")[0]!; row.focus(); fireEvent.click(row); // at Részletes this click is itself a new event in real use await act(async () => { log.debug("ui.click", "a newer event"); await new Promise((r) => requestAnimationFrame(() => r(null))); }); expect(row.isConnected).toBe(true); expect(document.activeElement).toBe(row); expect(row.getAttribute("aria-expanded")).toBe("true"); }); it("filters by level group and by text, and copies only what is shown", async () => { render(); fireEvent.click(screen.getByRole("button", { name: /settings\.myLog\.filters\.warning/ })); expect(rows()).toHaveLength(1); expect(rows()[0]).toContain("GET /api/feed 503"); fireEvent.click(screen.getByRole("button", { name: /settings\.myLog\.filters\.all/ })); fireEvent.change(screen.getByRole("searchbox"), { target: { value: "FEED-REFRESH" } }); expect(rows()).toHaveLength(1); const copy = async () => { await act(async () => { fireEvent.click(screen.getByRole("button", { name: "settings.myLog.copy" })); }); return copied[copied.length - 1]!.split("\n").map((l) => JSON.parse(l).event); }; expect(await copy()).toEqual(["ui.click"]); // only what the search left fireEvent.change(screen.getByRole("searchbox"), { target: { value: "" } }); expect(await copy()).toEqual(["nav.view", "http.request", "ui.click"]); // oldest first }); it("opens a row to its details and clears the log", async () => { render(); const first = within(screen.getByTestId("my-log-lines")).getAllByRole("button")[0]!; fireEvent.click(first); expect(first.getAttribute("aria-expanded")).toBe("true"); expect(screen.getByTestId("my-log-lines").textContent).toContain('"control": "feed-refresh"'); await act(async () => { fireEvent.click(screen.getByRole("button", { name: "settings.myLog.clear" })); await new Promise((r) => requestAnimationFrame(() => r(null))); }); expect(rows()).toEqual([]); expect(screen.getByText("settings.myLog.emptyAll")).toBeTruthy(); }); });