feat: add fetchSiteContent data function
This commit is contained in:
59
src/data/siteContent.test.ts
Normal file
59
src/data/siteContent.test.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const singleMock = vi.fn()
|
||||
const eqMock = vi.fn(() => ({ single: singleMock }))
|
||||
const selectMock = vi.fn(() => ({ eq: eqMock }))
|
||||
const fromMock = vi.fn<(table: string) => { select: typeof selectMock }>(() => ({ select: selectMock }))
|
||||
const resolveImageUrlMock = vi.fn((path: string) => `https://cdn.example/${path}`)
|
||||
|
||||
vi.mock('../lib/supabase', () => ({
|
||||
supabase: { from: (table: string) => fromMock(table) },
|
||||
}))
|
||||
vi.mock('../lib/resolveImageUrl', () => ({
|
||||
resolveImageUrl: (path: string) => resolveImageUrlMock(path),
|
||||
}))
|
||||
|
||||
const ROW = {
|
||||
id: 'default',
|
||||
hero_headline: 'Vos cheveux, votre couronne',
|
||||
hero_tagline: 'Coiffures protectrices par Mira',
|
||||
about_bio: 'Bio text.',
|
||||
about_portrait_image: 'about/mira-portrait.jpg',
|
||||
contact_email: 'hello@braidsbymira.com',
|
||||
contact_hours: 'Mar–Sam, 9h à 18h',
|
||||
}
|
||||
|
||||
import { fetchSiteContent, type SiteContent } from './siteContent'
|
||||
|
||||
describe('fetchSiteContent', () => {
|
||||
beforeEach(() => {
|
||||
singleMock.mockReset()
|
||||
eqMock.mockClear()
|
||||
selectMock.mockClear()
|
||||
fromMock.mockClear()
|
||||
resolveImageUrlMock.mockClear()
|
||||
})
|
||||
|
||||
it('fetches the default row and maps snake_case columns to the SiteContent shape', async () => {
|
||||
singleMock.mockResolvedValue({ data: ROW, error: null })
|
||||
|
||||
const content = await fetchSiteContent()
|
||||
|
||||
expect(fromMock).toHaveBeenCalledWith('site_content')
|
||||
expect(eqMock).toHaveBeenCalledWith('id', 'default')
|
||||
expect(resolveImageUrlMock).toHaveBeenCalledWith('about/mira-portrait.jpg')
|
||||
expect(content).toEqual<SiteContent>({
|
||||
heroHeadline: 'Vos cheveux, votre couronne',
|
||||
heroTagline: 'Coiffures protectrices par Mira',
|
||||
aboutBio: 'Bio text.',
|
||||
aboutPortraitImage: 'https://cdn.example/about/mira-portrait.jpg',
|
||||
contactEmail: 'hello@braidsbymira.com',
|
||||
contactHours: 'Mar–Sam, 9h à 18h',
|
||||
})
|
||||
})
|
||||
|
||||
it('throws when the fetch fails', async () => {
|
||||
singleMock.mockResolvedValue({ data: null, error: { message: 'network error' } })
|
||||
await expect(fetchSiteContent()).rejects.toEqual({ message: 'network error' })
|
||||
})
|
||||
})
|
||||
34
src/data/siteContent.ts
Normal file
34
src/data/siteContent.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { supabase } from '../lib/supabase'
|
||||
import { resolveImageUrl } from '../lib/resolveImageUrl'
|
||||
|
||||
export interface SiteContent {
|
||||
heroHeadline: string
|
||||
heroTagline: string
|
||||
aboutBio: string
|
||||
aboutPortraitImage: string
|
||||
contactEmail: string
|
||||
contactHours: string
|
||||
}
|
||||
|
||||
interface SiteContentRow {
|
||||
hero_headline: string
|
||||
hero_tagline: string
|
||||
about_bio: string
|
||||
about_portrait_image: string
|
||||
contact_email: string
|
||||
contact_hours: string
|
||||
}
|
||||
|
||||
export async function fetchSiteContent(): Promise<SiteContent> {
|
||||
const { data, error } = await supabase.from('site_content').select('*').eq('id', 'default').single()
|
||||
if (error) throw error
|
||||
const row = data as SiteContentRow
|
||||
return {
|
||||
heroHeadline: row.hero_headline,
|
||||
heroTagline: row.hero_tagline,
|
||||
aboutBio: row.about_bio,
|
||||
aboutPortraitImage: resolveImageUrl(row.about_portrait_image),
|
||||
contactEmail: row.contact_email,
|
||||
contactHours: row.contact_hours,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user