import { describe, it, expect } from 'vitest' import { hasAccessRoles, hasObjective, hasSummary } from '../extract.js' describe('extract-doc / hasObjective — i18n JSON', () => { it('detects a top-level objective body key (user FLAT)', () => { const json = `{ "title": "SLA", "subtitle": "...", "sections": { "objective": "Objectif", "access": "Accès" }, "objective": "Suivre les engagements de niveau de service." }` expect(hasObjective(json, 'fr/docs-support-sla.json')).toBe(true) }) it('detects sections.objective alone (the heading label)', () => { const json = `{ "sections": { "objective": "Objectif", "access": "Accès" } }` expect(hasObjective(json, 'fr/docs-support-sla.json')).toBe(true) }) it('detects overview.objective (DocRenderer nested shape)', () => { const json = `{ "docsAdministrationUsers": { "overview": { "objective": "Gérer les comptes utilisateurs." } } }` expect(hasObjective(json, 'fr/docs-administration-users.json')).toBe(true) }) it('does NOT detect "objective" appearing only as a VALUE', () => { // The word in free text / an array value is not a structural key → not a hit. const json = `{ "faq": { "1": { "answer": "Le principal objective est le suivi." } }, "labels": ["access", "objective-like"] }` expect(hasObjective(json, 'fr/docs-support-sla.json')).toBe(false) }) it('returns false when no objective key exists anywhere', () => { const json = `{ "title": "SLA", "sections": { "access": "Accès", "faq": "FAQ" }, "access": { "navigation": "Navigation" } }` expect(hasObjective(json, 'fr/docs-support-sla.json')).toBe(false) }) it('tolerates the French key spelling "objectif" (section present, just mis-keyed)', () => { // The real myspace/tenants doc keyed it `objectif` (FR) — it HAS the section, // so it must NOT be falsely reported missing. const json = `{ "title": "Mes Espaces de Travail", "sections": { "objectif": "Objectif", "access": "Accès" }, "objectif": "La page liste tous les espaces auxquels l'utilisateur a accès." }` expect(hasObjective(json, 'fr/docs-myspace-tenants.json')).toBe(true) }) it('tolerates t(\'objectif\') in TSX', () => { expect(hasObjective(`

{t('sections.objectif')}

`, 'tenants/index.tsx')).toBe(true) }) it('regression — the real Préférences i18n (no objective) is flagged missing', () => { // Mirrors docs-myspace-preferences.json: sections jump access → tabNav → … , // the objective was folded into the subtitle and dropped as a section. const json = `{ "title": "Préférences", "subtitle": "Personnalisez l'apparence, la langue, les notifications et vos modules favoris.", "sections": { "access": "Accès", "tabNav": "Navigation par onglets", "appearance": "Onglet — Apparence", "useCases": "Cas d'usage", "faq": "Questions fréquentes" } }` expect(hasObjective(json, 'fr/docs-myspace-preferences.json')).toBe(false) }) }) describe('extract-doc / hasObjective — doc-data.ts', () => { it('detects an overview.objective object key', () => { const ts = `export const docData: DocData = { overview: { objective: 'docsSupportSla.overview.objective', stats: { useCases: 4 } }, }` expect(hasObjective(ts, 'sla/doc-data.ts')).toBe(true) }) it('returns false for a doc-data.ts with no objective', () => { const ts = `export const docData: DocData = { overview: { stats: { useCases: 4 } }, features: [{ id: 'F-001', title: 'Config' }], }` expect(hasObjective(ts, 'sla/doc-data.ts')).toBe(false) }) }) describe('extract-doc / hasObjective — user TSX', () => { it('detects a t() reference to the objective body', () => { const tsx = `

{t('objective')}

` expect(hasObjective(tsx, 'sla/index.tsx')).toBe(true) }) it('detects a t() reference to the objective heading', () => { const tsx = `

{t('sections.objective')}

` expect(hasObjective(tsx, 'sla/index.tsx')).toBe(true) }) it('returns false for a page that renders no objective', () => { const tsx = `export default function P() { return (

{t('sections.access')}

{t('access.navigation')}

) }` expect(hasObjective(tsx, 'preferences/index.tsx')).toBe(false) }) }) describe('extract-doc / hasSummary — header accent tagline', () => { it('detects the canonical "summary" i18n key', () => { const json = `{ "title": "X", "summary": "Une phrase accent", "subtitle": "…" }` expect(hasSummary(json, 'fr/docs-support-sla.json')).toBe(true) }) it('detects the legacy "valueProposition" alias (so a good page is not flagged)', () => { // The real access-requests doc (the user's reference format) uses this key. const json = `{ "title": "X", "valueProposition": "Suivez vos demandes", "subtitle": "…" }` expect(hasSummary(json, 'fr/docs-myspace-tenants-access-requests.json')).toBe(true) }) it('detects t(\'summary\') / t(\'valueProposition\') in TSX', () => { expect(hasSummary(`

{t('summary')}

`, 'sla/index.tsx')).toBe(true) expect(hasSummary(`

{t('valueProposition')}

`, 'sla/index.tsx')).toBe(true) }) it('regression — the real tenants i18n (no tagline) returns false', () => { // Mirrors docs-myspace-tenants.json: title → subtitle, no summary/valueProposition. const json = `{ "title": "Mes Espaces de Travail", "subtitle": "Accédez à vos espaces professionnels et personnels…", "header": { "businessRules": "règles métier", "apiEndpoints": "endpoints API" } }` expect(hasSummary(json, 'fr/docs-myspace-tenants.json')).toBe(false) }) }) describe('extract-doc / hasAccessRoles — Section 2 role table', () => { it('detects the access.columns i18n block (canonical shape)', () => { const json = `{ "sections": { "access": "Accès & rôles" }, "access": { "columns": { "role": "Rôle", "actions": "Peut faire", "scope": "Portée" }, "roles": { "admin": { "name": "Administrateur", "actions": "Tout", "scope": "toutes" } } } }` expect(hasAccessRoles(json, 'fr/docs-crm-pipeline.json')).toBe(true) }) it('detects an access.roles-first block too', () => { const json = `{ "access": { "roles": { "admin": { "name": "Admin" } } } }` expect(hasAccessRoles(json, 'fr/docs-crm-pipeline.json')).toBe(true) }) it('does NOT match the legacy access.navigation-only shape', () => { const json = `{ "sections": { "access": "Accès" }, "access": { "navigation": "Navigation" } }` expect(hasAccessRoles(json, 'fr/docs-crm-pipeline.json')).toBe(false) }) it('detects the TSX table references, incl. the template-literal row keys', () => { expect(hasAccessRoles(`{t('access.columns.role')}`, 'pipeline/index.tsx')).toBe(true) const rowTsx = '{t(`access.roles.${r.key}.name`)}' expect(hasAccessRoles(rowTsx, 'pipeline/index.tsx')).toBe(true) }) it('returns false for a page rendering only the legacy access card', () => { const tsx = `

{t('access.navigation')}

/crm/pipeline
` expect(hasAccessRoles(tsx, 'pipeline/index.tsx')).toBe(false) }) })