/** * The "write silently failed" path, isolated. * * Mocked at the ADAPTER seam rather than by patching Web Storage: this repo runs vitest on a * Node that ships its own experimental `localStorage`/`sessionStorage`, so neither an instance * spy on `window.sessionStorage` nor a `Storage.prototype` patch reliably intercepts what the * adapter actually calls. The contract under test belongs to the adapter boundary anyway — * `save()` returns `void` and swallows its errors, so the only way to know a write stuck is * to read it back. */ import { beforeEach, describe, expect, it, vi } from 'vitest'; const store: { value: unknown } = { value: null }; /** When true, `save()` accepts the call and drops the value — a blocked/full quota. */ let swallowWrites = false; vi.mock('../local-storage-adapter', () => ({ createLocalStorageAdapter: () => ({ load: () => store.value, save: (value: unknown) => { if (swallowWrites) return; store.value = value; }, clear: () => { store.value = null; }, }), })); const { captureFirstTouchAttribution, getFirstTouchAttribution } = await import('../first-touch-attribution'); beforeEach(() => { store.value = null; swallowWrites = false; Object.defineProperty(window, 'location', { value: new URL('https://www.openmsp.ai/waitlist?utm_source=reddit'), writable: true, configurable: true, }); }); describe('captureFirstTouchAttribution persistence', () => { it('returns the record when the write sticks', () => { expect(captureFirstTouchAttribution().utm_source).toBe('reddit'); expect(getFirstTouchAttribution().utm_source).toBe('reddit'); }); it('returns {} — NOT the record — when the write is swallowed', () => { swallowWrites = true; // Returning the record here would hand the caller attribution that no later read can // recover: it would be replayed on this page view and absent on the next, which is worse // than never replaying it, because it looks like it worked. expect(captureFirstTouchAttribution()).toEqual({}); expect(getFirstTouchAttribution()).toEqual({}); }); });