123456789101112131415161718import "./_setup"; 19import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";20import { showAutofillSelector } from "./inject";21import { makeInput } from "./_setup";2223const ENTRIES = [24 { id: "e1", title: "Acme", username: "[email protected]" },25 { id: "e2", title: "Acme Admin", username: "[email protected]" },26];27282930function selectorHost(): HTMLElement | null {31 return document.body.querySelector<HTMLElement>('div[style*="2147483647"]');32}3334beforeEach(() => {35 document.body.innerHTML = "";36});37afterEach(() => {38 document.body.innerHTML = "";39});4041describe("T-I01: mount + dismiss (the observable contract)", () => {42 43 44 it("T-I01: showAutofillSelector mounts a host and returns a dismiss fn", () => {45 const anchor = makeInput({ type: "password" });46 const dismiss = showAutofillSelector(anchor, ENTRIES, vi.fn());47 expect(typeof dismiss).toBe("function");48 expect(selectorHost()).not.toBeNull();49 dismiss();50 });5152 53 54 55 it("T-I05/T-I06 (teardown half): the returned dismiss removes the host", async () => {56 const anchor = makeInput({ type: "password" });57 const dismiss = showAutofillSelector(anchor, ENTRIES, vi.fn());58 expect(selectorHost()).not.toBeNull();59 dismiss();60 await new Promise((r) => setTimeout(r, 180));61 expect(selectorHost()).toBeNull();62 });6364 65 it("T-I01b: locked-mode open also mounts a host", () => {66 const anchor = makeInput({ type: "password" });67 const dismiss = showAutofillSelector(anchor, [], vi.fn(), { locked: true });68 expect(selectorHost()).not.toBeNull();69 dismiss();70 });7172 73 74 75 it("T-I01c: each open creates its own host (caller owns single-instance policy)", () => {76 const anchor = makeInput({ type: "password" });77 const d1 = showAutofillSelector(anchor, ENTRIES, vi.fn());78 const d2 = showAutofillSelector(anchor, ENTRIES, vi.fn());79 expect(document.body.querySelectorAll('div[style*="2147483647"]').length).toBe(2);80 d1();81 d2();82 });83});