import { existsSync, readdirSync, readFileSync } from 'node:fs' import path from 'node:path' import { describe, expect, it } from 'vitest' import { isReservedShareLinkSlug, isValidShareLinkDestination, isValidShareLinkSlug, normalizeShareLinkDescription, SHARE_LINK_RESERVED_SLUGS, SHARE_LINK_SLUG_PATTERN, shareLinkPath, } from '@/lib/shareLink' describe('shareLink slug', () => { it('accepts lowercase kebab-case slugs', () => { expect(isValidShareLinkSlug('plenaria-saude-15-07')).toBe(true) expect(isValidShareLinkSlug('meet')).toBe(true) expect(isValidShareLinkSlug('a1')).toBe(true) }) it.each([ 'Plenaria', 'plenaria_saude', 'plenaria--saude', '-plenaria', 'plenaria-', '', 'a/b', 'a.b', ])('rejects %s', (value) => { expect(isValidShareLinkSlug(value)).toBe(false) expect(SHARE_LINK_SLUG_PATTERN.test(value)).toBe(false) }) it('flags the reserved slugs literally', () => { expect([...SHARE_LINK_RESERVED_SLUGS].sort()).toEqual( [ 'abaixo-assinado', 'admin', 'api', 'artigo', 'artigos', 'campanha', 'cards', 'corte', 'evento', 'jingles', 'mandato-no-whatsapp', 'noticia', 'privacidade', ].sort(), ) expect(isReservedShareLinkSlug('corte')).toBe(true) expect(isReservedShareLinkSlug('plenaria-saude')).toBe(false) }) it('builds the single-segment public path', () => { expect(shareLinkPath('plenaria-saude')).toBe('/plenaria-saude') }) }) describe('shareLink destination', () => { it.each([ 'https://meet.google.com/abc-defg-hij', 'http://example.com', 'https://example.com/path?x=1#y', ])('accepts %s', (value) => { expect(isValidShareLinkDestination(value)).toBe(true) }) it.each([ 'meet.google.com/abc', 'ftp://example.com/file', 'javascript:alert(1)', 'data:text/html,x', 'mailto:x@example.com', 'http:foo', 'https:example.com', '', ])('rejects %s', (value) => { expect(isValidShareLinkDestination(value)).toBe(false) }) }) describe('normalizeShareLinkDescription', () => { it('collapses newlines and repeated spaces so the meta tag stays single-line', () => { expect(normalizeShareLinkDescription(' Venha para a\n\nplenária de saúde ')).toBe( 'Venha para a plenária de saúde', ) }) }) describe('reserved slugs drift', () => { const ROUTE_FILES = new Set(['page.tsx', 'page.ts', 'route.ts', 'route.tsx']) /** Any route file below `dir` (dynamic segments included) makes the segment a URL. */ const hasRouteFile = (dir: string): boolean => { if (!existsSync(dir)) return false for (const entry of readdirSync(dir, { withFileTypes: true })) { if (entry.isFile() && ROUTE_FILES.has(entry.name)) return true if (entry.isDirectory() && hasRouteFile(path.join(dir, entry.name))) return true } return false } const staticRouteSegments = (): string[] => { const roots = ['src/app/(frontend)', 'src/app/(frontend)/(home)'] const segments = new Set() for (const root of roots) { const absolute = path.resolve(process.cwd(), root) if (!existsSync(absolute)) continue for (const entry of readdirSync(absolute, { withFileTypes: true })) { if (!entry.isDirectory() || entry.name.startsWith('[') || entry.name.startsWith('(')) continue if (hasRouteFile(path.join(absolute, entry.name))) segments.add(entry.name) } } return [...segments] } /** The post type enum is private to the server-only `posts.ts`; read it as source. */ const postTypesOnDisk = (): string[] => { const source = readFileSync(path.resolve(process.cwd(), 'src/utilities/posts.ts'), 'utf8') const match = source.match(/const POST_TYPES = \[([^\]]+)\]/) if (!match?.[1]) return [] return [...match[1].matchAll(/'([^']+)'/g)].map((entry) => entry[1]!) } it('reserves every static route segment at the root of the public site', () => { const missing = staticRouteSegments().filter((segment) => !isReservedShareLinkSlug(segment)) expect(missing).toEqual([]) }) it('reserves every post type value (the `[type]` branch answers them first)', () => { const types = postTypesOnDisk() expect(types).toEqual(['noticia', 'campanha', 'artigo', 'evento']) const missing = types.filter((type) => !isReservedShareLinkSlug(type)) expect(missing).toEqual([]) }) })