import { existsSync } from 'node:fs' import { join } from 'node:path' import { describe, expect, it } from 'vitest' import { aboutPage } from '@/content/aboutPage' import { contactPage } from '@/content/publicPages' import { FAQ_PAGE_SEO } from '@/lib/seo/faqPage' import { HOST_GUIDE_SEO } from '@/lib/seo/hostGuidePage' import { LEGAL_PAGE_SEO } from '@/lib/seo/legalPages' import { buildCanonicalUrl, DEFAULT_OG_IMAGE_PATH, resolveOgImage, SITE_URL } from '@/lib/seo/metadata' import { buildPublicPageMetadata } from '@/lib/seo/publicPageMetadata' const TITLE_SUFFIX = ' | قبیله' describe('SEO metadata policy', () => { it('uses the production canonical origin without a trailing slash', () => { expect(SITE_URL).toBe('https://ghabilee.ir') expect(buildCanonicalUrl('/city/mashhad')).toBe('https://ghabilee.ir/city/mashhad') expect(buildCanonicalUrl('category/art')).toBe('https://ghabilee.ir/category/art') }) it('ships the default Open Graph image referenced by metadata', () => { expect(DEFAULT_OG_IMAGE_PATH).toBe('/og-default.jpg') expect(resolveOgImage()).toBe('https://ghabilee.ir/og-default.jpg') expect(existsSync(join(process.cwd(), 'public', DEFAULT_OG_IMAGE_PATH))).toBe(true) }) it('prefers a page-specific Open Graph image', () => { expect(resolveOgImage('', null, 'https://cdn.example.com/category.png')).toBe('https://cdn.example.com/category.png') }) it('keeps public static page titles and descriptions in SERP bounds', () => { const legalPages = Object.values(LEGAL_PAGE_SEO) const absoluteTitlePages = [aboutPage, FAQ_PAGE_SEO, HOST_GUIDE_SEO, ...legalPages] const pages = [...absoluteTitlePages, contactPage] for (const page of pages) { const isAbsoluteTitlePage = absoluteTitlePages.includes(page as (typeof absoluteTitlePages)[number]) if (isAbsoluteTitlePage) { expect(page.metaTitle.length).toBeLessThanOrEqual(60) } else { expect(page.metaTitle.length + TITLE_SUFFIX.length).toBeLessThanOrEqual(60) } expect(page.metaDescription.length).toBeGreaterThanOrEqual(70) expect(page.metaDescription.length).toBeLessThanOrEqual(160) } }) it('builds absolute Open Graph URLs for public static pages', () => { const metadata = buildPublicPageMetadata({ path: '/faq', metaTitle: 'سؤالات متداول رزرو و میزبانی رویداد', metaDescription: 'پاسخ سؤالات متداول درباره رزرو رویداد، پرداخت، لغو رزرو، بازگشت وجه و میزبانی در قبیله را اینجا بخوانید.', }) expect(metadata.alternates).toMatchObject({ canonical: '/faq' }) expect(metadata.openGraph).toMatchObject({ url: 'https://ghabilee.ir/faq', locale: 'fa_IR', type: 'website', }) expect(metadata.twitter).toMatchObject({ card: 'summary_large_image' }) }) it('makes relative public-page Open Graph images absolute', () => { const metadata = buildPublicPageMetadata({ path: '/terms', metaTitle: LEGAL_PAGE_SEO.terms.metaTitle, metaDescription: LEGAL_PAGE_SEO.terms.metaDescription, ogImage: '/images/public/terms-hero.avif', }) expect(metadata.openGraph?.images).toEqual([ { url: 'https://ghabilee.ir/images/public/terms-hero.avif', alt: LEGAL_PAGE_SEO.terms.metaTitle, }, ]) }) })