// @vitest-environment jsdom import { beforeEach, describe, expect, it, vi } from "vitest"; import { fillFields, fillCardForm, formatCardNumber } from "./fill"; describe("content fill", () => { beforeEach(() => { document.body.innerHTML = ""; }); function makeVisibleInput(type: string): HTMLInputElement { const input = document.createElement("input"); input.type = type; 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; } it("fillFields writes username and password into matching inputs", () => { const username = makeVisibleInput("text"); const password = makeVisibleInput("password"); fillFields(username, password, "june", "hunter2"); expect(username.value).toBe("june"); expect(password.value).toBe("hunter2"); }); it("fillFields refuses to fill when the form action domain does not match", () => { const form = document.createElement("form"); form.action = "https://evil.com/login"; const username = makeVisibleInput("text"); const password = makeVisibleInput("password"); form.append(username, password); document.body.appendChild(form); fillFields(username, password, "june", "hunter2", form, "github.com"); expect(username.value).toBe(""); expect(password.value).toBe(""); }); it("fillFields still fills the username when the username field is hidden", () => { const username = makeVisibleInput("text"); const password = makeVisibleInput("password"); Object.defineProperty(username, "getBoundingClientRect", { value: () => ({ width: 0, height: 0, top: 0, left: 0, right: 0, bottom: 0, x: 0, y: 0, toJSON: () => ({}), }), }); fillFields(username, password, "june", "hunter2"); expect(username.value).toBe("june"); expect(password.value).toBe("hunter2"); }); it("fillFields dispatches standard input/change events rather than custom password events", () => { const username = makeVisibleInput("text"); const password = makeVisibleInput("password"); const usernameDispatch = vi.spyOn(username, "dispatchEvent"); const passwordDispatch = vi.spyOn(password, "dispatchEvent"); fillFields(username, password, "june", "hunter2"); expect(usernameDispatch.mock.calls.map(([event]) => event.type)).toEqual(["input", "change"]); expect(passwordDispatch.mock.calls.map(([event]) => event.type)).toEqual(["input", "change"]); expect(passwordDispatch.mock.calls.every(([event]) => !(event instanceof CustomEvent))).toBe(true); }); }); describe("card fill", () => { beforeEach(() => { document.body.innerHTML = ""; }); function makeInput(attrs: Record = {}): HTMLInputElement { const input = document.createElement("input"); for (const [k, v] of Object.entries(attrs)) input.setAttribute(k, v); document.body.appendChild(input); return input; } it("fillCardForm — fills number, expiry, CVC, and name fields", () => { const number = makeInput({ type: "text" }); const expMonth = makeInput({ type: "text" }); const expYear = makeInput({ type: "text" }); const cvc = makeInput({ type: "text" }); const name = makeInput({ type: "text" }); fillCardForm( { number, expMonth, expYear, expiryCombo: null, cvc, name }, { cardNumber: "4111111111111111", expMonth: "05", expYear: "2028", cvc: "123", cardholderName: "Jane Smith" } ); expect(number.value).toBe("4111 1111 1111 1111"); expect(expMonth.value).toBe("05"); expect(expYear.value).toBe("2028"); expect(cvc.value).toBe("123"); expect(name.value).toBe("Jane Smith"); }); it("fillCardForm — handles combined expiry field (MM/YY)", () => { const number = makeInput({ type: "text" }); const expiryCombo = makeInput({ type: "text" }); const cvc = makeInput({ type: "text" }); fillCardForm( { number, expMonth: null, expYear: null, expiryCombo, cvc, name: null }, { cardNumber: "5555555555554444", expMonth: "12", expYear: "2026", cvc: "456", cardholderName: "John Doe" } ); expect(expiryCombo.value).toBe("12/26"); }); it("fillCardForm — dispatches input and change events for React compatibility", () => { const number = makeInput({ type: "text" }); const numberDispatch = vi.spyOn(number, "dispatchEvent"); fillCardForm( { number, expMonth: null, expYear: null, expiryCombo: null, cvc: null, name: null }, { cardNumber: "4111111111111111", expMonth: "05", expYear: "2028", cvc: "123", cardholderName: "Jane" } ); expect(numberDispatch.mock.calls.map(([event]) => event.type)).toEqual(["input", "change"]); }); it("formatCardNumber — formats Visa as 4-4-4-4", () => { expect(formatCardNumber("4111111111111111")).toBe("4111 1111 1111 1111"); }); it("formatCardNumber — formats Amex as 4-6-5", () => { expect(formatCardNumber("378282246310005")).toBe("3782 822463 10005"); }); });