// @vitest-environment jsdom import { beforeEach, describe, expect, it } from "vitest"; import { detectCardForm, detectLoginFields, getPageDomain, hasPaymentProcessorIframe, isLoginForm, isSignupForm, isTOTPField, } from "./detect"; describe("content detect", () => { beforeEach(() => { document.body.innerHTML = ""; }); function visibleInput( attributes: Record = {}, tagName: "input" = "input" ): HTMLInputElement { const input = document.createElement(tagName); Object.entries(attributes).forEach(([key, value]) => input.setAttribute(key, value)); Object.defineProperty(input, "getBoundingClientRect", { configurable: true, value: () => ({ width: 100, height: 20, top: 0, left: 0, right: 100, bottom: 20, x: 0, y: 0, toJSON: () => ({}), }), }); document.body.appendChild(input); return input as HTMLInputElement; } it("isLoginForm returns true for a username/password pair", () => { const username = visibleInput({ type: "email", name: "email" }); const password = visibleInput({ type: "password", autocomplete: "current-password" }); expect( isLoginForm([ { type: "username", el: username }, { type: "password", el: password }, ]) ).toBe(true); }); it("isLoginForm returns false for fields without a password", () => { const username = visibleInput({ type: "text", name: "username" }); expect(isLoginForm([{ type: "username", el: username }])).toBe(false); }); it("isSignupForm returns true for confirm-password flows", () => { const password = visibleInput({ type: "password", name: "password" }); const confirm = visibleInput({ type: "password", name: "confirmPassword" }); expect( isSignupForm([ { type: "password", el: password }, { type: "password", el: confirm }, ]) ).toBe(true); }); it("isSignupForm returns false for a single current-password field", () => { const password = visibleInput({ type: "password", autocomplete: "current-password" }); expect(isSignupForm([{ type: "password", el: password }])).toBe(false); }); it("detectLoginFields matches email and username-style inputs but not tag-name fields", () => { const email = visibleInput({ type: "email", name: "email" }); const username = visibleInput({ type: "text", name: "login-id" }); const tagName = visibleInput({ type: "text", name: "tag-name" }); const password = visibleInput({ type: "password", autocomplete: "current-password" }); const hidden = visibleInput({ type: "hidden", name: "username" }); const fields = detectLoginFields(document); expect(fields.some((field) => field.el === email && field.type === "username")).toBe(true); expect(fields.some((field) => field.el === username && field.type === "username")).toBe(true); expect(fields.some((field) => field.el === password && field.type === "password")).toBe(true); expect(fields.some((field) => field.el === tagName)).toBe(false); expect(fields.some((field) => field.el === hidden)).toBe(false); }); it("getPageDomain falls back to the current hostname when no registrable domain exists", () => { expect(getPageDomain()).toBe(window.location.hostname); }); it("isTOTPField detects one-time-code fields without matching generic code words", () => { const oneTimeCode = visibleInput({ autocomplete: "one-time-code" }); const otpName = visibleInput({ name: "otp" }); const verification = visibleInput({ placeholder: "Verification code" }); const zipCode = visibleInput({ name: "zipcode" }); expect(isTOTPField(oneTimeCode)).toBe(true); expect(isTOTPField(otpName)).toBe(true); expect(isTOTPField(verification)).toBe(true); expect(isTOTPField(zipCode)).toBe(false); }); it("isTOTPField detects an adjacent 2FA label", () => { const label = document.createElement("label"); label.textContent = "2FA Code"; document.body.appendChild(label); const input = visibleInput({ placeholder: "If enabled" }); expect(isTOTPField(input)).toBe(true); }); }); describe("card form detection", () => { beforeEach(() => { document.body.innerHTML = ""; }); it("detectCardForm — finds number field by autocomplete attribute", () => { document.body.innerHTML = ''; const result = detectCardForm(document); expect(result).not.toBeNull(); expect(result!.number).toBeTruthy(); expect(result!.expMonth).toBeTruthy(); }); it("detectCardForm — finds fields by name/id heuristics when no autocomplete", () => { document.body.innerHTML = ''; const result = detectCardForm(document); expect(result).not.toBeNull(); expect(result!.number).toBeTruthy(); }); it("detectCardForm — returns null when no card number field found", () => { document.body.innerHTML = ''; expect(detectCardForm(document)).toBeNull(); }); it("hasPaymentProcessorIframe — returns true when Stripe iframe present", () => { document.body.innerHTML = ''; expect(hasPaymentProcessorIframe()).toBe(true); }); it("hasPaymentProcessorIframe — returns false on standard checkout page", () => { document.body.innerHTML = ''; expect(hasPaymentProcessorIframe()).toBe(false); }); });