import { setKeybindings, type TUI } from "@earendil-works/pi-tui"; import { beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import { KeybindingsManager } from "../../../src/core/keybindings.ts"; import { LoginDialogComponent } from "../../../src/modes/interactive/components/login-dialog.ts"; import { initTheme } from "../../../src/modes/interactive/theme/theme.ts"; import { stripAnsi } from "../../../src/utils/ansi.ts"; vi.mock("../../../src/utils/open-browser.ts", () => ({ openBrowser: vi.fn(), })); function createDialog(): LoginDialogComponent { return new LoginDialogComponent( { requestRender: vi.fn() } as unknown as TUI, "prompt-repro", () => {}, "Prompt Repro", ); } function renderDialog(dialog: LoginDialogComponent): string[] { return stripAnsi(dialog.render(120).join("\n")) .split("\n") .map((line) => line.trimEnd()); } function countRenderedValue(lines: string[], value: string): number { return lines.filter((line) => line.trim() === `> ${value}`).length; } describe("LoginDialogComponent OAuth prompts", () => { beforeAll(() => { initTheme("dark"); }); beforeEach(() => { setKeybindings(new KeybindingsManager()); }); test("keeps previous prompt input stable when a later prompt is active", async () => { const dialog = createDialog(); const firstPrompt = dialog.showPrompt("First prompt:", "first-value"); dialog.handleInput("first-value"); dialog.handleInput("\n"); await expect(firstPrompt).resolves.toBe("first-value"); const secondPrompt = dialog.showPrompt("Second prompt:"); dialog.handleInput("second-secret-demo"); const lines = renderDialog(dialog); expect(lines.join("\n")).toContain("First prompt:"); expect(lines.join("\n")).toContain("Second prompt:"); expect(countRenderedValue(lines, "first-value")).toBe(1); expect(countRenderedValue(lines, "second-secret-demo")).toBe(1); dialog.handleInput("\n"); await expect(secondPrompt).resolves.toBe("second-secret-demo"); }); test("preserves auth instructions when showing a prompt", () => { const dialog = createDialog(); dialog.showAuth("https://example.invalid/login", "Authorize the extension"); dialog.showPrompt("First prompt:"); const output = renderDialog(dialog).join("\n"); expect(output).toContain("https://example.invalid/login"); expect(output).toContain("Authorize the extension"); expect(output).toContain("First prompt:"); }); test("keeps previous manual input stable when a later prompt is active", async () => { const dialog = createDialog(); const manualInput = dialog.showManualInput("Paste callback URL:"); dialog.handleInput("callback-value"); dialog.handleInput("\n"); await expect(manualInput).resolves.toBe("callback-value"); const prompt = dialog.showPrompt("Second prompt:"); dialog.handleInput("second-secret-demo"); const lines = renderDialog(dialog); expect(lines.join("\n")).toContain("Paste callback URL:"); expect(lines.join("\n")).toContain("Second prompt:"); expect(countRenderedValue(lines, "callback-value")).toBe(1); expect(countRenderedValue(lines, "second-secret-demo")).toBe(1); dialog.handleInput("\n"); await expect(prompt).resolves.toBe("second-secret-demo"); }); });