123import { beforeEach, describe, expect, it } from "vitest";45import {6 detectCardForm,7 detectLoginFields,8 getPageDomain,9 hasPaymentProcessorIframe,10 isLoginForm,11 isSignupForm,12} from "./detect";1314describe("content detect", () => {15 beforeEach(() => {16 document.body.innerHTML = "";17 });1819 function visibleInput(20 attributes: Record<string, string> = {},21 tagName: "input" = "input"22 ): HTMLInputElement {23 const input = document.createElement(tagName);24 Object.entries(attributes).forEach(([key, value]) => input.setAttribute(key, value));25 Object.defineProperty(input, "getBoundingClientRect", {26 configurable: true,27 value: () => ({28 width: 100,29 height: 20,30 top: 0,31 left: 0,32 right: 100,33 bottom: 20,34 x: 0,35 y: 0,36 toJSON: () => ({}),37 }),38 });39 document.body.appendChild(input);40 return input as HTMLInputElement;41 }4243 it("isLoginForm returns true for a username/password pair", () => {44 const username = visibleInput({ type: "email", name: "email" });45 const password = visibleInput({ type: "password", autocomplete: "current-password" });4647 expect(48 isLoginForm([49 { type: "username", el: username },50 { type: "password", el: password },51 ])52 ).toBe(true);53 });5455 it("isLoginForm returns false for fields without a password", () => {56 const username = visibleInput({ type: "text", name: "username" });5758 expect(isLoginForm([{ type: "username", el: username }])).toBe(false);59 });6061 it("isSignupForm returns true for confirm-password flows", () => {62 const password = visibleInput({ type: "password", name: "password" });63 const confirm = visibleInput({ type: "password", name: "confirmPassword" });6465 expect(66 isSignupForm([67 { type: "password", el: password },68 { type: "password", el: confirm },69 ])70 ).toBe(true);71 });7273 it("isSignupForm returns false for a single current-password field", () => {74 const password = visibleInput({ type: "password", autocomplete: "current-password" });7576 expect(isSignupForm([{ type: "password", el: password }])).toBe(false);77 });7879 it("detectLoginFields matches email and username-style inputs but not tag-name fields", () => {80 const email = visibleInput({ type: "email", name: "email" });81 const username = visibleInput({ type: "text", name: "login-id" });82 const tagName = visibleInput({ type: "text", name: "tag-name" });83 const password = visibleInput({ type: "password", autocomplete: "current-password" });84 const hidden = visibleInput({ type: "hidden", name: "username" });8586 const fields = detectLoginFields(document);8788 expect(fields.some((field) => field.el === email && field.type === "username")).toBe(true);89 expect(fields.some((field) => field.el === username && field.type === "username")).toBe(true);90 expect(fields.some((field) => field.el === password && field.type === "password")).toBe(true);91 expect(fields.some((field) => field.el === tagName)).toBe(false);92 expect(fields.some((field) => field.el === hidden)).toBe(false);93 });9495 it("getPageDomain falls back to the current hostname when no registrable domain exists", () => {96 expect(getPageDomain()).toBe(window.location.hostname);97 });98});99100describe("card form detection", () => {101 beforeEach(() => {102 document.body.innerHTML = "";103 });104105 it("detectCardForm — finds number field by autocomplete attribute", () => {106 document.body.innerHTML = '<input autocomplete="cc-number" /><input autocomplete="cc-exp-month" /><input autocomplete="cc-exp-year" /><input autocomplete="cc-csc" />';107 const result = detectCardForm(document);108 expect(result).not.toBeNull();109 expect(result!.number).toBeTruthy();110 expect(result!.expMonth).toBeTruthy();111 });112113 it("detectCardForm — finds fields by name/id heuristics when no autocomplete", () => {114 document.body.innerHTML = '<input name="cardnum" /><input name="expiry" /><input name="cvv" />';115 const result = detectCardForm(document);116 expect(result).not.toBeNull();117 expect(result!.number).toBeTruthy();118 });119120 it("detectCardForm — returns null when no card number field found", () => {121 document.body.innerHTML = '<input type="text" name="username" />';122 expect(detectCardForm(document)).toBeNull();123 });124125 it("hasPaymentProcessorIframe — returns true when Stripe iframe present", () => {126 document.body.innerHTML = '<iframe src="https://js.stripe.com/v3/"></iframe>';127 expect(hasPaymentProcessorIframe()).toBe(true);128 });129130 it("hasPaymentProcessorIframe — returns false on standard checkout page", () => {131 document.body.innerHTML = '<input name="cardnum" />';132 expect(hasPaymentProcessorIframe()).toBe(false);133 });134});