templates2.Content.Nuxt/tests/unit/content.test.ts
w4c-templates 940313f8c5
Some checks failed
CI / quality (push) Has been cancelled
chore(template): publish Content.Nuxt
2026-10-11 23:26:40 +03:00

105 lines
3.4 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, expect, it } from 'vitest';
import {
extractHeadings,
filterPublished,
headingId,
highlightCode,
isPublished,
readingMinutes,
renderBlocks,
renderInline,
slugify,
} from '#shared/content';
describe('slugify', () => {
it('produces a url-safe slug', () => {
expect(slugify('Hello, World!')).toBe('hello-world');
expect(slugify(' Already-slug ')).toBe('already-slug');
});
it('matches headingId for a heading', () => {
expect(headingId('Designing for dark mode')).toBe('designing-for-dark-mode');
});
});
describe('readingMinutes', () => {
it('is at least one minute for short content', () => {
expect(readingMinutes('a short paragraph')).toBe(1);
});
it('scales with word count and ignores code fences', () => {
expect(readingMinutes('word '.repeat(600))).toBe(3);
const fenced = `intro\n\n\`\`\`ts\n${'x'.repeat(2000)}\n\`\`\``;
expect(readingMinutes(fenced)).toBe(1);
});
});
describe('draft filtering', () => {
const items = [
{ slug: 'a', status: 'published' },
{ slug: 'b', status: 'draft' },
{ slug: 'c', status: 'published' },
];
it('detects a published status', () => {
expect(isPublished({ status: 'published' })).toBe(true);
expect(isPublished({ status: 'draft' })).toBe(false);
});
it('keeps only published items', () => {
expect(filterPublished(items).map((i) => i.slug)).toEqual(['a', 'c']);
});
});
describe('renderBlocks', () => {
it('parses a fenced code block with a language', () => {
const blocks = renderBlocks('```ts\nconst x = 1;\n```');
expect(blocks).toHaveLength(1);
expect(blocks[0]?.type).toBe('code');
expect(blocks[0]?.lang).toBe('ts');
expect(blocks[0]?.code).toBe('const x = 1;');
});
it('parses headings, lists and paragraphs', () => {
const blocks = renderBlocks('## Setup\n\nIntro text.\n\n- one\n- two\n\nDone.');
expect(blocks.map((b) => b.type)).toEqual(['heading', 'paragraph', 'list', 'paragraph']);
expect(blocks[2]?.items).toEqual(['one', 'two']);
expect(blocks[1]?.text).toBe('Intro text.');
});
});
describe('extractHeadings (table of contents)', () => {
it('collects h2–h4 with slugified anchors and ignores h1', () => {
const toc = extractHeadings('# Title\n\n## Setup\n\n### Details\n\n#### Deep\n\nplain');
expect(toc.map((t) => t.id)).toEqual(['setup', 'details', 'deep']);
expect(toc.map((t) => t.level)).toEqual([2, 3, 4]);
});
it('deduplicates repeated heading ids', () => {
const toc = extractHeadings('## Setup\n\n## Setup\n');
expect(toc.map((t) => t.id)).toEqual(['setup', 'setup-2']);
});
});
describe('renderInline', () => {
it('renders code, bold and links', () => {
expect(renderInline('a `b` c')).toContain('<code>b</code>');
expect(renderInline('**bold**')).toContain('<strong>bold</strong>');
expect(renderInline('[docs](/docs)')).toContain('<a href="/docs">docs</a>');
});
it('escapes HTML and drops unsafe link targets', () => {
expect(renderInline('<script>alert(1)</script>')).toBe('&lt;script&gt;alert(1)&lt;/script&gt;');
expect(renderInline('[x](javascript:alert(1))')).toContain('href="#"');
});
});
describe('highlightCode', () => {
it('marks keywords and numbers without leaking raw tags', () => {
const html = highlightCode('const x = 1', 'ts');
expect(html).toContain('tok-kw');
expect(html).toContain('tok-num');
expect(highlightCode('<b>')).not.toContain('<b>');
});
});