import { describe, it, expect } from 'vitest'; import { getFaviconBasePath, getFaviconSkipDefaultLinks, getFaviconLinksHtml, getFaviconSourceDir, getFaviconIcons, } from '../../utils/favicon-config-access'; /** * Regression test for the snake_case/camelCase mismatch reported against * 0.7.109 (user-filed bug, 2026-08-01): * * "source_dir reads snake_case — CLI reads faviconCfg.source_dir but * loadSiteConfig normalizes to sourceDir. Same bug as skip_default_links, * base_path, links_html. The canonical files never get copied." * * Root cause: two loaders, two conventions. * - scripts/load-site-config.mjs normalizes snake_case → camelCase. * - src/lib/site-config.ts (Astro) does NOT normalize. * * Fix: the accessors below accept BOTH forms. They check camelCase first * (the post-normalization form) and fall back to snake_case (the raw YAML * wire form). Either loader's output works. */ describe('getFaviconBasePath', () => { it('returns undefined for null/undefined', () => { expect(getFaviconBasePath(null)).toBeUndefined(); expect(getFaviconBasePath(undefined)).toBeUndefined(); expect(getFaviconBasePath({})).toBeUndefined(); }); it('reads camelCase basePath', () => { expect(getFaviconBasePath({ basePath: '/vocab/' })).toBe('/vocab/'); }); it('reads snake_case base_path', () => { expect(getFaviconBasePath({ base_path: '/vocab/' })).toBe('/vocab/'); }); it('camelCase wins when both forms are set', () => { expect(getFaviconBasePath({ basePath: '/camel/', base_path: '/snake/' })).toBe('/camel/'); }); }); describe('getFaviconSkipDefaultLinks', () => { it('returns false for null/undefined', () => { expect(getFaviconSkipDefaultLinks(null)).toBe(false); expect(getFaviconSkipDefaultLinks(undefined)).toBe(false); expect(getFaviconSkipDefaultLinks({})).toBe(false); }); it('reads camelCase skipDefaultLinks', () => { expect(getFaviconSkipDefaultLinks({ skipDefaultLinks: true })).toBe(true); expect(getFaviconSkipDefaultLinks({ skipDefaultLinks: false })).toBe(false); }); it('reads snake_case skip_default_links', () => { expect(getFaviconSkipDefaultLinks({ skip_default_links: true })).toBe(true); }); }); describe('getFaviconLinksHtml', () => { it('returns undefined for null/undefined', () => { expect(getFaviconLinksHtml(null)).toBeUndefined(); expect(getFaviconLinksHtml({})).toBeUndefined(); }); it('reads camelCase linksHtml', () => { expect(getFaviconLinksHtml({ linksHtml: '' })).toBe(''); }); it('reads snake_case links_html', () => { expect(getFaviconLinksHtml({ links_html: '' })).toBe(''); }); }); describe('getFaviconSourceDir', () => { it('returns undefined for null/undefined', () => { expect(getFaviconSourceDir(null)).toBeUndefined(); expect(getFaviconSourceDir({})).toBeUndefined(); }); it('reads camelCase sourceDir', () => { expect(getFaviconSourceDir({ sourceDir: 'assets/favicons' })).toBe('assets/favicons'); }); /** * THE LOAD-BEARING TEST — pins the exact bug the user reported. * "source_dir reads snake_case — CLI reads faviconCfg.source_dir but * loadSiteConfig normalizes to sourceDir. The canonical files never * get copied." * * After loadSiteConfig normalization, the config object has sourceDir * (camelCase), NOT source_dir. The accessor must return the value. */ it('reads snake_case source_dir (the user-reported regression form)', () => { expect(getFaviconSourceDir({ source_dir: 'assets/favicons' })).toBe('assets/favicons'); }); it('camelCase wins when both forms are set', () => { expect(getFaviconSourceDir({ sourceDir: 'a', source_dir: 'b' })).toBe('a'); }); }); describe('getFaviconIcons', () => { it('returns undefined for null/undefined', () => { expect(getFaviconIcons(null)).toBeUndefined(); expect(getFaviconIcons({})).toBeUndefined(); }); it('reads icons (same key in both forms)', () => { const icons = [{ rel: 'icon', href: 'favicon.ico' }]; expect(getFaviconIcons({ icons })).toBe(icons); }); }); describe('regression — both loader outputs work end-to-end', () => { // Simulate the two loader paths: // - Astro's getSiteConfig (no normalization): snake_case keys preserved // - CLI's loadSiteConfig (normalizes): camelCase keys const snakeCaseForm = { base_path: '/cie-eilv/', skip_default_links: true, source_dir: 'assets/favicons', links_html: '', icons: [{ rel: 'icon', href: 'favicon.ico' }], }; const camelCaseForm = { basePath: '/cie-eilv/', skipDefaultLinks: true, sourceDir: 'assets/favicons', linksHtml: '', icons: [{ rel: 'icon', href: 'favicon.ico' }], }; for (const [label, cfg] of [['snake_case (Astro)', snakeCaseForm], ['camelCase (CLI)', camelCaseForm]] as const) { it(`accessors return identical values for ${label}`, () => { expect(getFaviconBasePath(cfg)).toBe('/cie-eilv/'); expect(getFaviconSkipDefaultLinks(cfg)).toBe(true); expect(getFaviconSourceDir(cfg)).toBe('assets/favicons'); expect(getFaviconLinksHtml(cfg)).toBe(''); expect(getFaviconIcons(cfg)).toEqual([{ rel: 'icon', href: 'favicon.ico' }]); }); } });