// Test framework'lerinden (Playwright, Cypress task, Jest, Vitest...) kullanılacak // bağımlılıksız istemci. Node 18+ ve tarayıcıda çalışır (global fetch). export interface OtpMockOptions { baseUrl: string; apiKey: string; } export interface WaitOptions { // Bu zamandan (ms) önce gelen kodlar yok sayılır. Varsayılan: waitForCode çağrıldığı an - 5 sn. since?: number; timeout?: number; interval?: number; } export interface ReceivedCode { code: string; messageSid: string; body: string; receivedAt: number; } export class OtpMock { private baseUrl: string; private apiKey: string; constructor({ baseUrl, apiKey }: OtpMockOptions) { this.baseUrl = baseUrl.replace(/\/+$/, ""); this.apiKey = apiKey; } // Paralel testlerin çakışmaması için her teste ayrı bir sanal numara ver. // +1 555 01xx aralığı kurgusal numaralar için ayrılmıştır; geri kalanı rastgele. randomPhone(prefix = "+1555"): string { const rest = Array.from({ length: 12 - prefix.replace(/\D/g, "").length }, () => Math.floor(Math.random() * 10)).join(""); return prefix + rest; } async waitForCode(phone: string, opts: WaitOptions = {}): Promise { const { timeout = 15_000, interval = 300 } = opts; // Saat kayması payı: uygulama sunucusu ile test makinesinin saatleri birkaç saniye farklı olabilir. const since = opts.since ?? Date.now() - 5_000; const deadline = Date.now() + timeout; while (true) { const res = await this.request(`/v1/inbox/${encodeURIComponent(phone)}/code?since=${since}`); if (res.ok) return (await res.json()) as ReceivedCode; if (res.status !== 404) throw new Error(`otpmock: ${res.status} ${await res.text()}`); if (Date.now() >= deadline) throw new Error(`otpmock: ${phone} için ${timeout} ms içinde kod gelmedi`); await new Promise((r) => setTimeout(r, interval)); } } async clear(phone: string): Promise { await this.request(`/v1/inbox/${encodeURIComponent(phone)}`, { method: "DELETE" }); } // Test ortamında gerçek bir uygulama yoksa SMS gönderimini simüle etmek için. async send(to: string, body: string): Promise { const res = await this.request("/v1/messages/send", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ to, body }), }); if (!res.ok) throw new Error(`otpmock: ${res.status} ${await res.text()}`); } private request(path: string, init: RequestInit = {}): Promise { return fetch(this.baseUrl + path, { ...init, headers: { ...(init.headers as Record), authorization: `Bearer ${this.apiKey}` }, }); } }