Add runtime-configured Lab 3 browser terminal

This commit is contained in:
2026-04-13 16:40:14 -06:00
parent 7e4d35b6a3
commit ca6a966ad6
6 changed files with 270 additions and 96 deletions
+80 -11
View File
@@ -1,10 +1,16 @@
import { fireEvent, render, screen } from "@testing-library/react";
import { afterEach, describe, expect, it } from "vitest";
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,
Lab3TerminalFrame,
LAB3_DEFAULT_WORKING_DIRECTORY,
fetchLab3RuntimeConfig,
getLab3TerminalPath,
} from "~/components/labs/Lab3TerminalFrame";
LAB3_RUNTIME_CONFIG_PATH,
normalizeLab3RuntimeConfig,
} from "~/lib/lab3-runtime";
describe("getLab3TerminalPath", () => {
it("falls back to the default same-origin terminal path", () => {
@@ -20,17 +26,54 @@ describe("getLab3TerminalPath", () => {
});
});
describe("Lab3TerminalFrame", () => {
const originalEnvValue = process.env.NEXT_PUBLIC_LAB3_TERMINAL_PATH;
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(() => {
process.env.NEXT_PUBLIC_LAB3_TERMINAL_PATH = originalEnvValue;
vi.restoreAllMocks();
});
it("renders the embedded terminal with the configured path", () => {
process.env.NEXT_PUBLIC_LAB3_TERMINAL_PATH = "lab-terminal";
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 },
),
);
render(<Lab3TerminalFrame />);
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(<Lab3TerminalFrame srcPath="lab-terminal" />);
const frame = screen.getByTitle("Lab 3 terminal session");
expect(frame).toHaveAttribute("src", "/lab-terminal");
@@ -42,10 +85,36 @@ describe("Lab3TerminalFrame", () => {
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(<Lab3TerminalFrame />);
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(<Lab3TerminalFrame srcPath="/wetty" />);
const toggle = screen.getAllByRole("button", { name: /open terminal|hide terminal|lab 3 terminal/i })[0];
const toggle = screen.getAllByRole("button", {
name: /open terminal|hide terminal|lab 3 terminal/i,
})[0];
expect(toggle).toHaveAttribute("aria-expanded", "false");
fireEvent.click(toggle);