admin/lib/seo/metadata.test.ts
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

83 lines
3.3 KiB
TypeScript

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,
},
])
})
})