123import { beforeEach, describe, expect, it, vi } from "vitest";45import { fillFields, fillCardForm, formatCardNumber } from "./fill";67describe("content fill", () => {8 beforeEach(() => {9 document.body.innerHTML = "";10 });1112 function makeVisibleInput(type: string): HTMLInputElement {13 const input = document.createElement("input");14 input.type = type;15 Object.defineProperty(input, "getBoundingClientRect", {16 configurable: true,17 value: () => ({18 width: 100,19 height: 20,20 top: 0,21 left: 0,22 right: 100,23 bottom: 20,24 x: 0,25 y: 0,26 toJSON: () => ({}),27 }),28 });29 document.body.appendChild(input);30 return input;31 }3233 it("fillFields writes username and password into matching inputs", () => {34 const username = makeVisibleInput("text");35 const password = makeVisibleInput("password");3637 fillFields(username, password, "june", "hunter2");3839 expect(username.value).toBe("june");40 expect(password.value).toBe("hunter2");41 });4243 it("fillFields refuses to fill when the form action domain does not match", () => {44 const form = document.createElement("form");45 form.action = "https://evil.com/login";46 const username = makeVisibleInput("text");47 const password = makeVisibleInput("password");48 form.append(username, password);49 document.body.appendChild(form);5051 fillFields(username, password, "june", "hunter2", form, "github.com");5253 expect(username.value).toBe("");54 expect(password.value).toBe("");55 });5657 it("fillFields still fills the username when the username field is hidden", () => {58 const username = makeVisibleInput("text");59 const password = makeVisibleInput("password");60 Object.defineProperty(username, "getBoundingClientRect", {61 value: () => ({62 width: 0,63 height: 0,64 top: 0,65 left: 0,66 right: 0,67 bottom: 0,68 x: 0,69 y: 0,70 toJSON: () => ({}),71 }),72 });7374 fillFields(username, password, "june", "hunter2");7576 expect(username.value).toBe("june");77 expect(password.value).toBe("hunter2");78 });7980 it("fillFields dispatches standard input/change events rather than custom password events", () => {81 const username = makeVisibleInput("text");82 const password = makeVisibleInput("password");83 const usernameDispatch = vi.spyOn(username, "dispatchEvent");84 const passwordDispatch = vi.spyOn(password, "dispatchEvent");8586 fillFields(username, password, "june", "hunter2");8788 expect(usernameDispatch.mock.calls.map(([event]) => event.type)).toEqual(["input", "change"]);89 expect(passwordDispatch.mock.calls.map(([event]) => event.type)).toEqual(["input", "change"]);90 expect(passwordDispatch.mock.calls.every(([event]) => !(event instanceof CustomEvent))).toBe(true);91 });92});9394describe("card fill", () => {95 beforeEach(() => {96 document.body.innerHTML = "";97 });9899 function makeInput(attrs: Record<string, string> = {}): HTMLInputElement {100 const input = document.createElement("input");101 for (const [k, v] of Object.entries(attrs)) input.setAttribute(k, v);102 document.body.appendChild(input);103 return input;104 }105106 it("fillCardForm — fills number, expiry, CVC, and name fields", () => {107 const number = makeInput({ type: "text" });108 const expMonth = makeInput({ type: "text" });109 const expYear = makeInput({ type: "text" });110 const cvc = makeInput({ type: "text" });111 const name = makeInput({ type: "text" });112113 fillCardForm(114 { number, expMonth, expYear, expiryCombo: null, cvc, name },115 { cardNumber: "4111111111111111", expMonth: "05", expYear: "2028", cvc: "123", cardholderName: "Jane Smith" }116 );117118 expect(number.value).toBe("4111 1111 1111 1111");119 expect(expMonth.value).toBe("05");120 expect(expYear.value).toBe("2028");121 expect(cvc.value).toBe("123");122 expect(name.value).toBe("Jane Smith");123 });124125 it("fillCardForm — handles combined expiry field (MM/YY)", () => {126 const number = makeInput({ type: "text" });127 const expiryCombo = makeInput({ type: "text" });128 const cvc = makeInput({ type: "text" });129130 fillCardForm(131 { number, expMonth: null, expYear: null, expiryCombo, cvc, name: null },132 { cardNumber: "5555555555554444", expMonth: "12", expYear: "2026", cvc: "456", cardholderName: "John Doe" }133 );134135 expect(expiryCombo.value).toBe("12/26");136 });137138 it("fillCardForm — dispatches input and change events for React compatibility", () => {139 const number = makeInput({ type: "text" });140 const numberDispatch = vi.spyOn(number, "dispatchEvent");141142 fillCardForm(143 { number, expMonth: null, expYear: null, expiryCombo: null, cvc: null, name: null },144 { cardNumber: "4111111111111111", expMonth: "05", expYear: "2028", cvc: "123", cardholderName: "Jane" }145 );146147 expect(numberDispatch.mock.calls.map(([event]) => event.type)).toEqual(["input", "change"]);148 });149150 it("formatCardNumber — formats Visa as 4-4-4-4", () => {151 expect(formatCardNumber("4111111111111111")).toBe("4111 1111 1111 1111");152 });153154 it("formatCardNumber — formats Amex as 4-6-5", () => {155 expect(formatCardNumber("378282246310005")).toBe("3782 822463 10005");156 });157});