import { resolve } from 'node:path' import { fileURLToPath } from 'node:url' import { generateIconsCSS, resolveIconSVG } from 'node/icons' import { parseIconName } from 'shared/shared' // the e2e workspace has @iconify-json/lucide installed — use it as the // resolution root for collection-loading tests const e2eRoot = resolve(fileURLToPath(import.meta.url), '../../../e2e') describe('node/icons', () => { describe('parseIconName', () => { test('parses qualified names', () => { expect(parseIconName('lucide:heart')).toEqual({ collection: 'lucide', icon: 'heart' }) expect(parseIconName('simple-icons:github')).toEqual({ collection: 'simple-icons', icon: 'github' }) }) test('rejects bare names and anything outside iconify grammar', () => { for (const name of [ 'github', 'GitHub', 'foo bar', 'foo:', ':bar', 'a { test('emits base rules and per-icon rules, no legacy common rule', async () => { // simple-icons is not in the e2e workspace's package.json — this also // covers the fallback to vitepress's own dependency const { css, warnings } = await generateIconsCSS( e2eRoot, new Set(['simple-icons:github']), 'compressed' ) expect(warnings).toEqual([]) expect(css).toContain( '.vpi-simple-icons-github{--icon:url("data:image/svg+xml' ) expect(css).toContain(":where([class^='vpi-']") expect(css).toContain('display:inline-block') expect(css).not.toContain('.vpi-social') }) test('suggests qualification for bare names', async () => { const { css, warnings } = await generateIconsCSS( e2eRoot, new Set(['github']), 'compressed' ) expect(css).toBe('') expect(warnings).toEqual([ expect.stringContaining('"github" has no collection prefix') ]) expect(warnings[0]).toContain('simple-icons:github') }) test('groups collections and stays deterministic across insertion order', async () => { const a = await generateIconsCSS( e2eRoot, new Set(['lucide:heart', 'simple-icons:github', 'lucide:egg']), 'compressed' ) const b = await generateIconsCSS( e2eRoot, new Set(['simple-icons:github', 'lucide:egg', 'lucide:heart']), 'compressed' ) expect(a.css).toBe(b.css) expect(a.css).toContain('.vpi-lucide-heart') expect(a.css).toContain('.vpi-lucide-egg') expect(a.css).toContain('.vpi-simple-icons-github') }) test('warns on icons missing from an installed collection', async () => { const { css, warnings } = await generateIconsCSS( e2eRoot, new Set(['simple-icons:github', 'simple-icons:thisiconisnotreal']), 'compressed' ) expect(css).toContain('.vpi-simple-icons-github') expect(css).not.toContain('thisiconisnotreal') expect(warnings).toEqual([ expect.stringContaining( '"thisiconisnotreal" was not found in the "simple-icons"' ) ]) }) test('warns on uninstalled collections with an install hint', async () => { const { css, warnings } = await generateIconsCSS( e2eRoot, new Set(['notinstalled:foo']), 'compressed' ) expect(css).toBe('') expect(warnings).toEqual([ expect.stringContaining('@iconify-json/notinstalled') ]) }) test('warns on invalid names', async () => { const { warnings } = await generateIconsCSS( e2eRoot, new Set(['Not A Name']), 'compressed' ) expect(warnings).toEqual([ expect.stringContaining('"Not A Name" is not a valid icon name') ]) }) test('returns empty css for an empty set', async () => { const { css, warnings } = await generateIconsCSS( e2eRoot, new Set(), 'compressed' ) expect(css).toBe('') expect(warnings).toEqual([]) }) }) describe('resolveIconSVG', () => { test('resolves an svg offline', async () => { const resolved = await resolveIconSVG(e2eRoot, 'lucide', 'heart') expect(resolved).toHaveProperty('svg') const svg = (resolved as { svg: string }).svg expect(svg).toContain(' { expect(await resolveIconSVG(e2eRoot, 'lucide', 'noicon')).toEqual({ error: expect.stringContaining('was not found in the "lucide"') }) expect(await resolveIconSVG(e2eRoot, 'nocollection', 'x')).toEqual({ error: expect.stringContaining('@iconify-json/nocollection') }) expect(await resolveIconSVG(e2eRoot, 'Bad Name', 'x')).toEqual({ error: expect.stringContaining('not a valid icon name') }) }) }) })