Add Makefile, package.json, and initial configuration for VitePress and Slidev integration
- Introduced a Makefile for streamlined commands related to building, running, and managing presentations. - Created package.json to define project metadata, dependencies, and scripts for development and testing. - Added .gitignore entries to exclude build artifacts and temporary files from version control. - Included playwright.config.mjs for end-to-end testing setup with Playwright. - Added README.md for project documentation, outlining setup instructions and development guidelines.
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
import { test, describe } from 'node:test'
|
||||
import assert from 'node:assert'
|
||||
import fs from 'node:fs'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
import {
|
||||
escHtml,
|
||||
escMd,
|
||||
pickFeatured,
|
||||
buildFeaturedHtml,
|
||||
readAllMeta,
|
||||
generateListPageMarkdown,
|
||||
generateDetailPageMarkdown,
|
||||
patchIndexFeaturedContent,
|
||||
FEATURED_START,
|
||||
FEATURED_END,
|
||||
} from '../tools/presentation-contracts.mjs'
|
||||
|
||||
function mockSitePath(p) {
|
||||
return p.startsWith('/') ? p : `/${p}`
|
||||
}
|
||||
|
||||
describe('escHtml / escMd', () => {
|
||||
test('escHtml neutralise < > & "', () => {
|
||||
assert.strictEqual(escHtml('<a href="x">'), '<a href="x">')
|
||||
})
|
||||
|
||||
test('escMd échappe les pipes tableaux markdown', () => {
|
||||
assert.strictEqual(escMd('a|b'), 'a\\|b')
|
||||
})
|
||||
})
|
||||
|
||||
describe('pickFeatured', () => {
|
||||
test('priorité aux entrées featured puis order', () => {
|
||||
const items = [
|
||||
{ slug: 'c', title: 'C', order: 3, featured: false },
|
||||
{ slug: 'a', title: 'A', order: 1, featured: true },
|
||||
{ slug: 'b', title: 'B', order: 2, featured: true },
|
||||
]
|
||||
const got = pickFeatured(items)
|
||||
assert.deepStrictEqual(
|
||||
got.map((x) => x.slug),
|
||||
['a', 'b'],
|
||||
)
|
||||
})
|
||||
|
||||
test('sans featured : trois premiers par order', () => {
|
||||
const items = [
|
||||
{ slug: 'z', title: 'Z', order: 10 },
|
||||
{ slug: 'a', title: 'A', order: 1 },
|
||||
{ slug: 'b', title: 'B', order: 2 },
|
||||
{ slug: 'c', title: 'C', order: 3 },
|
||||
]
|
||||
const got = pickFeatured(items)
|
||||
assert.deepStrictEqual(
|
||||
got.map((x) => x.slug),
|
||||
['a', 'b', 'c'],
|
||||
)
|
||||
})
|
||||
|
||||
test('liste vide', () => {
|
||||
assert.deepStrictEqual(pickFeatured([]), [])
|
||||
})
|
||||
})
|
||||
|
||||
describe('buildFeaturedHtml', () => {
|
||||
test('génère des liens et échappe le titre', () => {
|
||||
const html = buildFeaturedHtml(
|
||||
[{ slug: 'x', title: '<evil>', description: 'd & co', order: 1 }],
|
||||
mockSitePath,
|
||||
)
|
||||
assert.match(html, /href="\/presentations\/x\/"/)
|
||||
assert.match(html, /<evil>/)
|
||||
assert.match(html, /d & co/)
|
||||
})
|
||||
|
||||
test('aucune présentation : commentaire', () => {
|
||||
assert.strictEqual(buildFeaturedHtml([], mockSitePath), '<!-- (aucune présentation) -->')
|
||||
})
|
||||
})
|
||||
|
||||
describe('readAllMeta (répertoire temporaire)', () => {
|
||||
test('ignore dossier sans meta.json', () => {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'talks-slides-'))
|
||||
fs.mkdirSync(path.join(dir, 'empty'), { recursive: true })
|
||||
const okDir = path.join(dir, 'ok')
|
||||
fs.mkdirSync(okDir, { recursive: true })
|
||||
fs.writeFileSync(path.join(okDir, 'meta.json'), JSON.stringify({ title: 'T', description: '', order: 1 }))
|
||||
const list = readAllMeta(dir)
|
||||
assert.strictEqual(list.length, 1)
|
||||
assert.strictEqual(list[0].slug, 'ok')
|
||||
assert.strictEqual(list[0].title, 'T')
|
||||
})
|
||||
})
|
||||
|
||||
describe('generateListPageMarkdown / generateDetailPageMarkdown', () => {
|
||||
const p = {
|
||||
slug: 'demo',
|
||||
title: 'Démo',
|
||||
description: 'Desc',
|
||||
tags: ['a', 'b'],
|
||||
order: 1,
|
||||
}
|
||||
|
||||
test('liste contient fiche + slides + tags', () => {
|
||||
const md = generateListPageMarkdown([p], mockSitePath)
|
||||
assert.match(md, /href="\/presentations\/demo\/"/)
|
||||
assert.match(md, /href="\/slides\/demo\/1"/)
|
||||
assert.match(md, /vp-raw/)
|
||||
assert.match(md, /`a` · `b`/)
|
||||
})
|
||||
|
||||
test('détail contient QrEncode et boutons', () => {
|
||||
const md = generateDetailPageMarkdown(p, mockSitePath)
|
||||
assert.match(md, /<QrEncode path="\/slides\/demo\/1" \/>/)
|
||||
assert.match(md, /class="vp-button vp-raw"/)
|
||||
assert.match(md, /Voir les slides/)
|
||||
assert.match(md, /## Partager les slides/)
|
||||
})
|
||||
|
||||
test('détail sans tags : pas de ligne Tags', () => {
|
||||
const md = generateDetailPageMarkdown({ ...p, tags: [] }, mockSitePath)
|
||||
assert.ok(!md.includes('**Tags :**'))
|
||||
})
|
||||
})
|
||||
|
||||
describe('patchIndexFeaturedContent', () => {
|
||||
test('remplace la zone entre marqueurs', () => {
|
||||
const raw = `head\n${FEATURED_START}\nold\n${FEATURED_END}\ntail`
|
||||
const { ok, content } = patchIndexFeaturedContent(
|
||||
raw,
|
||||
[{ slug: 's', title: 'T', description: 'D', order: 1 }],
|
||||
mockSitePath,
|
||||
)
|
||||
assert.strictEqual(ok, true)
|
||||
assert.ok(content.includes('talk-card'))
|
||||
assert.ok(content.includes('head'))
|
||||
assert.ok(content.includes('tail'))
|
||||
assert.ok(!content.includes('old'))
|
||||
})
|
||||
|
||||
test('marqueurs absents : ok false', () => {
|
||||
const { ok } = patchIndexFeaturedContent('nope', [], mockSitePath)
|
||||
assert.strictEqual(ok, false)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user