12345678910111213import { beforeEach, describe, expect, it } from "vitest";1415import type { DetectedField } from "./detect";16import { resolveAutofillInteractionTargets } from "./interaction";17import { makeInput } from "./_setup";1819beforeEach(() => {20 document.body.innerHTML = "";21});2223function field(type: DetectedField["type"], el: HTMLInputElement): DetectedField {24 return { type, el };25}2627describe("resolveAutofillInteractionTargets", () => {28 29 30 it("T-IT01: standard form → {usernameEl, passwordEl} and shouldOpen", () => {31 const u = makeInput({ type: "text" });32 const p = makeInput({ type: "password" });33 const targets = resolveAutofillInteractionTargets(p, [field("username", u), field("password", p)]);34 expect(targets.usernameEl).toBe(u);35 expect(targets.passwordEl).toBe(p);36 expect(targets.emailEl).toBeNull();37 expect(targets.shouldOpen).toBe(true);38 });3940 41 42 43 44 it("T-IT02: email-first form → email becomes usernameEl, passwordEl null", () => {45 const e = makeInput({ type: "email" });46 const targets = resolveAutofillInteractionTargets(e, [field("email", e)]);47 expect(targets.usernameEl).toBe(e);48 expect(targets.passwordEl).toBeNull();49 expect(targets.emailEl).toBeNull();50 51 52 53 });5455 56 it("T-IT02b: distinct username + email → emailEl is tracked alongside usernameEl", () => {57 const u = makeInput({ type: "text", autocomplete: "username" });58 const e = makeInput({ type: "email", autocomplete: "email" });59 const p = makeInput({ type: "password" });60 const targets = resolveAutofillInteractionTargets(61 u,62 [field("username", u), field("email", e), field("password", p)]63 );64 expect(targets.usernameEl).toBe(u);65 expect(targets.emailEl).toBe(e);66 expect(targets.passwordEl).toBe(p);67 });6869 70 71 it("T-IT03: password-only form → passwordEl set, usernameEl null, shouldOpen", () => {72 const p = makeInput({ type: "password", autocomplete: "current-password" });73 const targets = resolveAutofillInteractionTargets(p, [field("password", p)]);74 expect(targets.passwordEl).toBe(p);75 expect(targets.usernameEl).toBeNull();76 expect(targets.shouldOpen).toBe(true);77 });7879 80 it("T-IT04: empty field set + passive → shouldOpen false", () => {81 const anchor = makeInput({ type: "text" });82 const targets = resolveAutofillInteractionTargets(anchor, []);83 expect(targets.shouldOpen).toBe(false);84 expect(targets.usernameEl).toBeNull();85 expect(targets.passwordEl).toBeNull();86 });8788 89 90 91 92 it("T-IT05: explicit summon on a bare password anchor falls back to the anchor", () => {93 const anchor = makeInput({ type: "password" });94 const targets = resolveAutofillInteractionTargets(anchor, [], true);95 expect(targets.shouldOpen).toBe(true);96 expect(targets.passwordEl).toBe(anchor);97 });9899 it("T-IT05b: explicit summon on a bare username anchor falls back to the anchor", () => {100 const anchor = makeInput({ type: "text" });101 const targets = resolveAutofillInteractionTargets(anchor, [], true);102 expect(targets.shouldOpen).toBe(true);103 expect(targets.usernameEl).toBe(anchor);104 });105106 107 108 it("T-IT06: detached anchor + empty fields → shouldOpen false, no throw", () => {109 const detached = document.createElement("input");110 detached.type = "text"; 111 let targets!: ReturnType<typeof resolveAutofillInteractionTargets>;112 expect(() => { targets = resolveAutofillInteractionTargets(detached, []); }).not.toThrow();113 expect(targets.shouldOpen).toBe(false);114 });115});