12345678910111213141516171819import { vi } from "vitest";202122232425export type MessageResponder = (msg: any) => any;2627let messageResponder: MessageResponder = (msg) => {28 29 30 switch (msg?.type) {31 case "GET_SETTINGS":32 return { ok: true, data: { accentColor: "#ff9776", theme: "dark" } };33 case "GET_VAULT_STATUS":34 return { ok: true, data: { unlocked: false } };35 default:36 return { ok: true, data: null };37 }38};394041export const sentMessages: any[] = [];4243export const messageListeners: Array<44 (msg: any, sender: any, sendResponse: (r?: any) => void) => unknown45> = [];464748export function setMessageResponder(fn: MessageResponder): void {49 messageResponder = fn;50}51525354const sendMessage = vi.fn((msg: any, maybeCb?: (resp: any) => void) => {55 sentMessages.push(msg);56 const result = messageResponder(msg);57 const resolved = Promise.resolve(result);58 if (typeof maybeCb === "function") {59 resolved.then((r) => maybeCb(r));60 return undefined as unknown as Promise<any>;61 }62 return resolved;63});6465const sessionStore = new Map<string, unknown>();6667const chromeMock = {68 runtime: {69 id: "peach-test-extension-id",70 sendMessage,71 getURL: (path: string) => `chrome-extension://peach-test/${path}`,72 onMessage: {73 addListener: (fn: any) => messageListeners.push(fn),74 removeListener: (fn: any) => {75 const i = messageListeners.indexOf(fn);76 if (i >= 0) messageListeners.splice(i, 1);77 },78 },79 lastError: undefined as undefined | { message: string },80 },81 storage: {82 session: {83 get: (key: string) =>84 Promise.resolve(sessionStore.has(key) ? { [key]: sessionStore.get(key) } : {}),85 set: (obj: Record<string, unknown>) => {86 for (const [k, v] of Object.entries(obj)) sessionStore.set(k, v);87 return Promise.resolve();88 },89 },90 local: {91 get: () => Promise.resolve({}),92 set: () => Promise.resolve(),93 },94 },95};96979899(globalThis as any).chrome = chromeMock;100101102103104if (typeof (globalThis as any).ResizeObserver === "undefined") {105 (globalThis as any).ResizeObserver = class {106 observe(): void {}107 unobserve(): void {}108 disconnect(): void {}109 };110}111112113114export const clipboardWrite = vi.fn(() => Promise.resolve());115if (!("clipboard" in navigator)) {116 Object.defineProperty(navigator, "clipboard", {117 configurable: true,118 value: { writeText: clipboardWrite },119 });120} else {121 Object.defineProperty(navigator.clipboard, "writeText", {122 configurable: true,123 value: clipboardWrite,124 });125}126127128129export function resetChromeMock(): void {130 sentMessages.length = 0;131 sessionStore.clear();132 sendMessage.mockClear();133 clipboardWrite.mockClear();134 messageResponder = (msg) => {135 switch (msg?.type) {136 case "GET_SETTINGS":137 return { ok: true, data: { accentColor: "#ff9776", theme: "dark" } };138 case "GET_VAULT_STATUS":139 return { ok: true, data: { unlocked: false } };140 default:141 return { ok: true, data: null };142 }143 };144}145146147148const VISIBLE_RECT = {149 width: 160,150 height: 32,151 top: 10,152 left: 10,153 right: 170,154 bottom: 42,155 x: 10,156 y: 10,157 toJSON: () => ({}),158} as const;159160161162163export function makeFieldVisible(el: HTMLElement): void {164 Object.defineProperty(el, "getBoundingClientRect", {165 configurable: true,166 value: () => ({ ...VISIBLE_RECT }),167 });168}169170171export function makeInput(172 attrs: Record<string, string> = {},173 parent: HTMLElement = document.body174): HTMLInputElement {175 const input = document.createElement("input");176 for (const [k, v] of Object.entries(attrs)) input.setAttribute(k, v);177 parent.appendChild(input);178 makeFieldVisible(input);179 return input;180}181182183export function createLoginForm(): {184 form: HTMLFormElement;185 username: HTMLInputElement;186 password: HTMLInputElement;187} {188 const form = document.createElement("form");189 document.body.appendChild(form);190 const username = makeInput(191 { type: "text", name: "username", autocomplete: "username" },192 form193 );194 const password = makeInput(195 { type: "password", name: "password", autocomplete: "current-password" },196 form197 );198 const button = document.createElement("button");199 button.type = "submit";200 button.textContent = "Log in";201 form.appendChild(button);202 return { form, username, password };203}204205206export function createEmailFirstForm(): {207 form: HTMLFormElement;208 email: HTMLInputElement;209} {210 const form = document.createElement("form");211 document.body.appendChild(form);212 const email = makeInput(213 { type: "email", name: "email", autocomplete: "email" },214 form215 );216 const button = document.createElement("button");217 button.type = "submit";218 button.textContent = "Next";219 form.appendChild(button);220 return { form, email };221}222223224225