- B1: Next.js 15 + Payload CMS 3.0 + Postgres 16, ESLint, Prettier, Husky, Vitest - B2: 9 collections, 6 globals, 12 Page Builder blocks, access control, slugify/revalidate hooks - B3: ezy.com.ua payments, Binotel HMAC webhook, leads API, Telegram bot, Resend email, rate limiting - B4: Tariffs collection with ezy API sync (cron + manual), dynamic pricing source-of-truth - B5: 13 test files covering unit libs and all API routes - B6: Dockerfile multi-stage, docker-compose.prod.yml, nginx.conf SSL, GitHub Actions CI/CD, health endpoint - B7: docs/admin-guide-ua.md (marketer guide), docs/deploy.md (VPS instructions), README quickstart Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
60 lines
1.9 KiB
TypeScript
60 lines
1.9 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import { parseQuery } from '@/lib/utm'
|
|
|
|
function params(obj: Record<string, string>): URLSearchParams {
|
|
return new URLSearchParams(obj)
|
|
}
|
|
|
|
describe('parseQuery', () => {
|
|
it('extracts all whitelisted UTM params', () => {
|
|
const result = parseQuery(
|
|
params({
|
|
utm_source: 'google',
|
|
utm_medium: 'cpc',
|
|
utm_campaign: 'summer',
|
|
utm_content: 'banner',
|
|
utm_term: 'shoes',
|
|
gclid: 'abc123',
|
|
})
|
|
)
|
|
expect(result).toEqual({
|
|
utm_source: 'google',
|
|
utm_medium: 'cpc',
|
|
utm_campaign: 'summer',
|
|
utm_content: 'banner',
|
|
utm_term: 'shoes',
|
|
gclid: 'abc123',
|
|
})
|
|
})
|
|
|
|
it('returns empty object when no UTM params are present', () => {
|
|
expect(parseQuery(params({ page: '1', foo: 'bar' }))).toEqual({})
|
|
})
|
|
|
|
it('ignores non-whitelisted params', () => {
|
|
const result = parseQuery(params({ utm_source: 'fb', referrer: 'https://example.com' }))
|
|
expect(result).toEqual({ utm_source: 'fb' })
|
|
expect(result).not.toHaveProperty('referrer')
|
|
})
|
|
|
|
it('omits params with empty string values', () => {
|
|
// URLSearchParams.get returns '' for empty value — the lib checks `if (value)` so it skips
|
|
const result = parseQuery(params({ utm_source: '', utm_medium: 'email' }))
|
|
expect(result).not.toHaveProperty('utm_source')
|
|
expect(result.utm_medium).toBe('email')
|
|
})
|
|
|
|
it('handles only gclid present', () => {
|
|
expect(parseQuery(params({ gclid: 'Cj0KCQ' }))).toEqual({ gclid: 'Cj0KCQ' })
|
|
})
|
|
|
|
it('handles empty URLSearchParams', () => {
|
|
expect(parseQuery(new URLSearchParams())).toEqual({})
|
|
})
|
|
|
|
it('returns a partial object when only some UTM params are present', () => {
|
|
const result = parseQuery(params({ utm_source: 'newsletter', utm_campaign: 'may' }))
|
|
expect(result).toEqual({ utm_source: 'newsletter', utm_campaign: 'may' })
|
|
expect(Object.keys(result)).toHaveLength(2)
|
|
})
|
|
})
|