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