mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
## Changes Introduces two new files in `packages/common`: - **`telemetry-first-touch-store.ts`** — a module-scoped singleton that holds first-touch attribution data (referrer, UTM params, page URL) in memory. Writes once on first load, cleared after the initial pageview event fires or on opt-out. No device storage involved. - **`useFirstTouchStore.tsx`** — a React hook that captures attribution data on initial page load and writes it into the store, gated on the `enabled` flag so it only runs where consent has been handled. Trade-off: data is lost on a hard reload before consent is granted — accepted edge case per GROWTH-656. Follows the same module-scope pattern already used by `posthogClient` and `consentState`. ## Testing - Verify first-touch data is captured on initial load and readable by `PageTelemetry` after consent - Verify no cookie is set before consent - Verify data is cleared after initial pageview fires GROWTH-656
61 lines
1.6 KiB
TypeScript
61 lines
1.6 KiB
TypeScript
import { afterEach, describe, expect, it } from 'vitest'
|
|
|
|
import {
|
|
clearFirstTouchData,
|
|
getFirstTouchData,
|
|
setFirstTouchData,
|
|
} from './telemetry-first-touch-store'
|
|
|
|
const makeFakeData = (pathname: string) =>
|
|
({
|
|
page_url: `https://supabase.com${pathname}`,
|
|
pathname,
|
|
page_title: 'Test',
|
|
session_id: 'test-session',
|
|
ph: { referrer: 'https://google.com' },
|
|
}) as ReturnType<typeof getFirstTouchData> & {}
|
|
|
|
describe('telemetry-first-touch-store', () => {
|
|
// Reset between tests so module-scoped state doesn't leak
|
|
afterEach(() => {
|
|
clearFirstTouchData()
|
|
})
|
|
|
|
it('returns null before any write', () => {
|
|
expect(getFirstTouchData()).toBeNull()
|
|
})
|
|
|
|
it('stores data and returns it on read', () => {
|
|
const data = makeFakeData('/pricing')
|
|
setFirstTouchData(data)
|
|
expect(getFirstTouchData()).toEqual(data)
|
|
})
|
|
|
|
it('is write-once: a second call with different data is a no-op', () => {
|
|
const first = makeFakeData('/pricing')
|
|
const second = makeFakeData('/docs')
|
|
|
|
setFirstTouchData(first)
|
|
setFirstTouchData(second)
|
|
|
|
expect(getFirstTouchData()).toEqual(first)
|
|
})
|
|
|
|
it('clearFirstTouchData resets to null', () => {
|
|
setFirstTouchData(makeFakeData('/pricing'))
|
|
clearFirstTouchData()
|
|
expect(getFirstTouchData()).toBeNull()
|
|
})
|
|
|
|
it('allows writing again after clearFirstTouchData', () => {
|
|
const first = makeFakeData('/pricing')
|
|
const second = makeFakeData('/docs')
|
|
|
|
setFirstTouchData(first)
|
|
clearFirstTouchData()
|
|
|
|
setFirstTouchData(second)
|
|
expect(getFirstTouchData()).toEqual(second)
|
|
})
|
|
})
|