AdoptionConsentModal.test.tsx

3.2 KB · raw

1// @vitest-environment jsdom23import { afterEach, describe, expect, it, vi } from "vitest";4import { AdoptionConsentModal } from "./AdoptionConsentModal";5import { flushView, inputText, renderView } from "../views/view-test-utils";67function button(container: HTMLElement, text: string): HTMLButtonElement {8  const found = Array.from(container.querySelectorAll("button")).find((b) =>9    (b.textContent ?? "").includes(text),10  );11  if (!found) throw new Error(`button not found: ${text}`);12  return found as HTMLButtonElement;13}1415function mount(overrides: Partial<Parameters<typeof AdoptionConsentModal>[0]> = {}) {16  const props = {17    receiverDeviceName: "June's iPhone",18    sas: "89524517",19    verifyMasterPassword: vi.fn(async () => true),20    onCancel: vi.fn(),21    onApprove: vi.fn(async () => {}),22    ...overrides,23  };24  const rendered = renderView(<AdoptionConsentModal {...props} />);25  return { ...rendered, props };26}2728describe("AdoptionConsentModal", () => {29  const cleanups: Array<() => void> = [];3031  afterEach(() => {32    while (cleanups.length) cleanups.pop()!();33  });3435  function mounted(overrides: Partial<Parameters<typeof AdoptionConsentModal>[0]> = {}) {36    const rendered = mount(overrides);37    cleanups.push(rendered.dispose);38    return rendered;39  }4041  it("T-A16: renders receiver device name, consequence copy, and SAS code", () => {42    const { container } = mounted();43    expect(container.textContent).toContain("June's iPhone");44    expect(container.textContent).toContain("copies your entire vault including all passwords and Pro");45    expect(container.textContent).toContain("89524517");46  });4748  it("T-A17: Share vault button is disabled until a password is entered", () => {49    const { container } = mounted();50    const share = button(container, "Share vault");51    expect(share.disabled).toBe(true);52    inputText(container.querySelector("#adoption-consent-password") as HTMLInputElement, "pw");53    expect(share.disabled).toBe(false);54  });5556  it("T-A18: wrong password shows inline error, clears the field, and does not approve", async () => {57    const { container, props } = mounted({ verifyMasterPassword: vi.fn(async () => false) });58    const input = container.querySelector("#adoption-consent-password") as HTMLInputElement;59    inputText(input, "wrong");60    button(container, "Share vault").click();61    await flushView();6263    expect(container.textContent).toContain("Incorrect password");64    expect(input.value).toBe("");65    expect(document.activeElement).toBe(input);66    expect(props.onApprove).not.toHaveBeenCalled();67  });6869  it("T-A19: correct password calls onApprove", async () => {70    const { container, props } = mounted({ verifyMasterPassword: vi.fn(async () => true) });71    inputText(container.querySelector("#adoption-consent-password") as HTMLInputElement, "correct");72    button(container, "Share vault").click();73    await flushView();7475    expect(props.verifyMasterPassword).toHaveBeenCalledWith("correct");76    expect(props.onApprove).toHaveBeenCalledTimes(1);77  });7879  it("T-A20: cancel closes without approval", () => {80    const { container, props } = mounted();81    button(container, "Cancel").click();82    expect(props.onCancel).toHaveBeenCalledTimes(1);83    expect(props.onApprove).not.toHaveBeenCalled();84  });85});