teqo-backup/tests/unit/siteMetadata.unit.spec.ts
Francisco Solla 8b74aa705a fix(S19): imagem do cartão no origem do deploy, não no domínio canônico
O resolveShareLinkOgImageUrl absolutizava /api/media/file/… contra o siteUrl do global metadata (hoje https://jorgesolla.com.br, servido pelo WordPress antigo), que responde 404 no proxy de mídia — o WhatsApp lia título/descrição mas derrubava a miniatura (caso real do cartão plenaria-cultura, da plenária de 19/09).

- resolveDeploymentOrigin (novo em seo.ts): NEXT_PUBLIC_SITE_URL primeiro (inlinado no build de produção, mesmo carrier do isStagingSite), fallback no URL do global; canonical/og:url seguem a cadeia global-first documentada — só a URL do asset muda.
- Unit do resolver (env vence/fallback/vazio); int pina o env sobre um URL de global estrangeiro.
2026-09-19 16:28:32 -03:00

130 lines
4.3 KiB
TypeScript

// @vitest-environment node
import { afterEach, describe, expect, it } from 'vitest'
import {
SITE_METADATA_DEFAULTS,
absoluteSitePath,
resolveDeploymentOrigin,
resolveSiteMetadata,
} from '@/utilities/seo'
const originalSiteUrl = process.env.NEXT_PUBLIC_SITE_URL
afterEach(() => {
if (originalSiteUrl === undefined) {
delete process.env.NEXT_PUBLIC_SITE_URL
} else {
process.env.NEXT_PUBLIC_SITE_URL = originalSiteUrl
}
})
describe('resolveSiteMetadata', () => {
it('returns defaults and null siteUrl when the global is empty and env is unset', () => {
delete process.env.NEXT_PUBLIC_SITE_URL
const resolved = resolveSiteMetadata({})
expect(resolved).toEqual({
siteUrl: null,
title: SITE_METADATA_DEFAULTS.title,
siteName: SITE_METADATA_DEFAULTS.siteName,
description: SITE_METADATA_DEFAULTS.description,
twitterCreator: SITE_METADATA_DEFAULTS.twitterCreator,
twitterDescription: SITE_METADATA_DEFAULTS.description,
keywords: [],
})
})
it('falls back to NEXT_PUBLIC_SITE_URL when the global has no URL', () => {
process.env.NEXT_PUBLIC_SITE_URL = 'https://example.com/'
const resolved = resolveSiteMetadata({})
expect(resolved.siteUrl).toBe('https://example.com')
})
it('prefers the global URL over the env fallback and strips trailing slashes', () => {
process.env.NEXT_PUBLIC_SITE_URL = 'https://env.example/'
const resolved = resolveSiteMetadata({ URL: 'https://global.example///' })
expect(resolved.siteUrl).toBe('https://global.example')
})
it('treats blank URL / env as missing', () => {
process.env.NEXT_PUBLIC_SITE_URL = ' '
expect(resolveSiteMetadata({ URL: ' ' }).siteUrl).toBeNull()
})
it('fills textual fields when openGraph / twitter / description are absent', () => {
delete process.env.NEXT_PUBLIC_SITE_URL
const resolved = resolveSiteMetadata({ URL: 'https://jorgesolla.com.br' })
expect(resolved.siteUrl).toBe('https://jorgesolla.com.br')
expect(resolved.siteName).toBe(SITE_METADATA_DEFAULTS.siteName)
expect(resolved.twitterCreator).toBe(SITE_METADATA_DEFAULTS.twitterCreator)
expect(resolved.description).toBe(SITE_METADATA_DEFAULTS.description)
})
it('preserves populated fields from a complete global', () => {
const resolved = resolveSiteMetadata({
URL: 'https://jorgesolla.com.br',
title: 'Site Title',
description: 'Site description',
keywords: [{ keyword: 'bahia' }, 'saude', { keyword: null }, null, ' '],
openGraph: { siteName: 'OG Name' },
twitter: { creator: '@creator', description: 'Twitter desc' },
})
expect(resolved).toEqual({
siteUrl: 'https://jorgesolla.com.br',
title: 'Site Title',
siteName: 'OG Name',
description: 'Site description',
twitterCreator: '@creator',
twitterDescription: 'Twitter desc',
keywords: ['bahia', 'saude'],
})
})
it('flattens null / empty keywords to an empty list', () => {
expect(resolveSiteMetadata({ keywords: null }).keywords).toEqual([])
expect(resolveSiteMetadata({ keywords: [] }).keywords).toEqual([])
})
})
describe('absoluteSitePath', () => {
it('returns undefined when siteUrl is null', () => {
expect(absoluteSitePath(null, '/noticia')).toBeUndefined()
})
it('joins origin and path without doubling slashes', () => {
expect(absoluteSitePath('https://example.com', '/noticia')).toBe('https://example.com/noticia')
expect(absoluteSitePath('https://example.com', 'noticia')).toBe('https://example.com/noticia')
})
})
describe('resolveDeploymentOrigin', () => {
it('prefers NEXT_PUBLIC_SITE_URL over the global fallback and strips trailing slashes', () => {
process.env.NEXT_PUBLIC_SITE_URL = 'https://deploy.example///'
expect(resolveDeploymentOrigin('https://global.example')).toBe('https://deploy.example')
})
it('falls back to the global site URL when env is missing or blank', () => {
delete process.env.NEXT_PUBLIC_SITE_URL
expect(resolveDeploymentOrigin('https://global.example')).toBe('https://global.example')
process.env.NEXT_PUBLIC_SITE_URL = ' '
expect(resolveDeploymentOrigin('https://global.example')).toBe('https://global.example')
})
it('returns null when neither source has a usable URL', () => {
delete process.env.NEXT_PUBLIC_SITE_URL
expect(resolveDeploymentOrigin(null)).toBeNull()
})
})