import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import { afterEach, describe, expect, it, vi } from "vitest"; import { Lab3TerminalFrame, } from "~/components/labs/Lab3TerminalFrame"; import { LAB3_DEFAULT_TERMINAL_PATH, LAB3_DEFAULT_WORKING_DIRECTORY, fetchLab3RuntimeConfig, getLab3TerminalPath, LAB3_RUNTIME_CONFIG_PATH, normalizeLab3RuntimeConfig, } from "~/lib/lab3-runtime"; describe("getLab3TerminalPath", () => { it("falls back to the default same-origin terminal path", () => { expect(getLab3TerminalPath()).toBe(LAB3_DEFAULT_TERMINAL_PATH); expect(getLab3TerminalPath("")).toBe(LAB3_DEFAULT_TERMINAL_PATH); }); it("normalizes relative and absolute terminal values to a same-origin path", () => { expect(getLab3TerminalPath("wetty")).toBe("/wetty"); expect(getLab3TerminalPath("https://labs.example.com/wetty?view=lab3")).toBe( "https://labs.example.com/wetty?view=lab3", ); }); }); describe("normalizeLab3RuntimeConfig", () => { it("fills in the default student terminal metadata", () => { expect(normalizeLab3RuntimeConfig()).toEqual({ terminalPath: LAB3_DEFAULT_TERMINAL_PATH, username: "student", workingDirectory: LAB3_DEFAULT_WORKING_DIRECTORY, }); }); }); describe("fetchLab3RuntimeConfig", () => { afterEach(() => { vi.restoreAllMocks(); }); it("reads the runtime config artifact from the public path", async () => { const fetchMock = vi .spyOn(globalThis, "fetch") .mockResolvedValue( new Response( JSON.stringify({ lab3TerminalUrl: "http://127.0.0.1:7681/wetty", lab3Username: "student", lab3WorkingDirectory: "/home/student/lab3", }), { status: 200 }, ), ); await expect(fetchLab3RuntimeConfig()).resolves.toEqual({ terminalPath: "http://127.0.0.1:7681/wetty", username: "student", workingDirectory: "/home/student/lab3", }); expect(fetchMock).toHaveBeenCalledWith(LAB3_RUNTIME_CONFIG_PATH, { cache: "no-store", }); }); }); describe("Lab3TerminalFrame", () => { afterEach(() => { vi.restoreAllMocks(); }); it("renders the embedded terminal with the configured path override", () => { render(); const frame = screen.getByTitle("Lab 3 terminal session"); expect(frame).toHaveAttribute("src", "/lab-terminal"); expect( screen.getAllByRole("link", { name: "Open in New Tab" }).every((link) => { return link.getAttribute("href") === "/lab-terminal"; }), ).toBe(true); expect(screen.getAllByText("/home/student/lab3")).toHaveLength(2); }); it("loads the terminal settings from the runtime config artifact", async () => { vi.spyOn(globalThis, "fetch").mockResolvedValue( new Response( JSON.stringify({ lab3TerminalUrl: "http://127.0.0.1:7681/wetty", lab3Username: "student", lab3WorkingDirectory: "/srv/labs/lab3", }), { status: 200 }, ), ); render(); await waitFor(() => { expect(screen.getByTitle("Lab 3 terminal session")).toHaveAttribute( "src", "http://127.0.0.1:7681/wetty", ); }); expect(screen.getAllByText("/srv/labs/lab3")).toHaveLength(2); }); it("toggles the dock open and closed", () => { render(); const toggle = screen.getAllByRole("button", { name: /open terminal|hide terminal|lab 3 terminal/i, })[0]; expect(toggle).toHaveAttribute("aria-expanded", "false"); fireEvent.click(toggle); expect(toggle).toHaveAttribute("aria-expanded", "true"); }); it("renders fallback guidance alongside the embedded frame", () => { render(); expect(screen.getByText(/If the terminal page does not appear/i)).toBeInTheDocument(); }); });