// @vitest-environment jsdom // DRAFT DESTINATION: src/popup/views/AdoptVaultView.test.tsx plus onboarding // contract additions in src/popup/views/contracts.test.tsx. // T-A21..T-A25. import { describe, expect, it, vi } from "vitest"; import { AdoptVaultView } from "./AdoptVaultView"; import { inputText, renderView } from "./view-test-utils"; function button(container: HTMLElement, text: string): HTMLButtonElement { const found = Array.from(container.querySelectorAll("button")).find((b) => (b.textContent ?? "").includes(text), ); if (!found) throw new Error(`button not found: ${text}`); return found as HTMLButtonElement; } describe("vault adoption onboarding", () => { it("T-A21: Onboarding offers Create vault, Restore from backup, and I already have Peach", () => { // Integrate into contracts.test.tsx where Onboarding + LockView are mounted together. document.body.innerHTML = ` `; expect(document.body.textContent).toContain("Create vault"); expect(document.body.textContent).toContain("Restore from backup"); expect(document.body.textContent).toContain("I already have Peach"); }); it("T-A22: password screen uses the same minimum-length gate as create-vault", () => { const view = renderView( {}} onDone={() => {}} startAdoption={vi.fn()} />); const continueButton = button(view.container, "Continue"); expect(continueButton.disabled).toBe(true); inputText(view.container.querySelector("#adopt-password") as HTMLInputElement, "short"); inputText(view.container.querySelector("#adopt-confirm-password") as HTMLInputElement, "short"); expect(view.container.textContent).toContain("need 7 more characters"); expect(continueButton.disabled).toBe(true); }); it("T-A23: adoption flow reaches the SAS screen", async () => { const startAdoption = vi.fn(async ({ confirmSAS }: { confirmSAS(sas: string): Promise }) => { await confirmSAS("895245"); }); const view = renderView( {}} onDone={() => {}} startAdoption={startAdoption} />); inputText(view.container.querySelector("#adopt-password") as HTMLInputElement, "receiver-passphrase-123"); inputText(view.container.querySelector("#adopt-confirm-password") as HTMLInputElement, "receiver-passphrase-123"); button(view.container, "Continue").click(); button(view.container, "Pair a device").click(); await Promise.resolve(); expect(view.container.textContent).toContain("Confirm this code matches"); expect(view.container.textContent).toContain("8 9 5 2 4 5"); }); it("T-A24: success leaves a vault and opens Peach", async () => { const onDone = vi.fn(); const view = renderView( {}} onDone={onDone} startAdoption={vi.fn(async () => {})} />, ); inputText(view.container.querySelector("#adopt-password") as HTMLInputElement, "receiver-passphrase-123"); inputText(view.container.querySelector("#adopt-confirm-password") as HTMLInputElement, "receiver-passphrase-123"); button(view.container, "Continue").click(); button(view.container, "Pair a device").click(); await Promise.resolve(); expect(view.container.textContent).toContain("Joined your vault."); button(view.container, "Open Peach").click(); expect(onDone).toHaveBeenCalledTimes(1); }); it("T-A25: SAS cancel keeps receiver vault-less and returns to the password step", async () => { let resolveSas: ((ok: boolean) => void) | null = null; const createVault = vi.fn(); const view = renderView( {}} onDone={() => {}} startAdoption={vi.fn(async ({ confirmSAS }) => { await new Promise((resolve) => { void confirmSAS("895245").then((ok) => { if (ok) createVault(); resolve(); }); resolveSas = (ok) => { void ok; resolve(); }; }); })} />, ); inputText(view.container.querySelector("#adopt-password") as HTMLInputElement, "receiver-passphrase-123"); inputText(view.container.querySelector("#adopt-confirm-password") as HTMLInputElement, "receiver-passphrase-123"); button(view.container, "Continue").click(); button(view.container, "Pair a device").click(); await Promise.resolve(); button(view.container, "Cancel").click(); resolveSas?.(false); await Promise.resolve(); expect(createVault).not.toHaveBeenCalled(); expect(view.container.textContent).toContain("Set a password for this device."); }); });