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,129 @@
|
||||
/**
|
||||
* Tests du plugin slide-dists en dev VitePress (`/slides/<slug>/` → dist-<slug>/).
|
||||
*
|
||||
* Régression couverte (soumission initiale) :
|
||||
* En dev, des requêtes du type `GET /slides/<slug>/1.md?import&…` recevaient la page HTML
|
||||
* du portail VitePress (Content-Type: text/html) au lieu d’être ignorées ou traitées
|
||||
* comme des modules JS. Le navigateur affichait alors :
|
||||
* « Expected a JavaScript-or-Wasm module script but the server responded with a MIME type of text/html »
|
||||
* (middleware sirv trop large + repli SPA VitePress sur du HTML pour chemins inconnus.)
|
||||
*
|
||||
* Contrat : pour ces URLs « module Vite » sous /slides/, la réponse ne doit **jamais**
|
||||
* être du `text/html` (404 texte ou autre non-HTML est acceptable).
|
||||
*/
|
||||
import { test } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import http from 'node:http'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { createServer } from 'vitepress'
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const deckIndex = path.join(root, 'dist-kubernetes-operators', 'index.html')
|
||||
if (!fs.existsSync(deckIndex)) {
|
||||
const e = { ...process.env, TALKS_PORTAL_DEV: '1' }
|
||||
delete e.SITE_BASE
|
||||
execFileSync(process.execPath, [path.join(root, 'tools', 'build-deck.mjs'), 'kubernetes-operators'], {
|
||||
cwd: root,
|
||||
stdio: 'inherit',
|
||||
env: e,
|
||||
})
|
||||
}
|
||||
|
||||
function get(url, headers = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const u = new URL(url)
|
||||
const opts = { hostname: u.hostname, port: u.port, path: u.pathname + u.search, headers }
|
||||
http
|
||||
.get(opts, (res) => {
|
||||
let body = ''
|
||||
res.on('data', (c) => {
|
||||
body += c
|
||||
})
|
||||
res.on('end', () => resolve({ status: res.statusCode, body, headers: res.headers }))
|
||||
})
|
||||
.on('error', reject)
|
||||
})
|
||||
}
|
||||
|
||||
/** Lance VitePress en mode dev local (TALKS_PORTAL_DEV), exécute le callback, puis ferme. */
|
||||
async function withDevPortal(callback) {
|
||||
const prevDev = process.env.TALKS_PORTAL_DEV
|
||||
const prevSite = process.env.SITE_BASE
|
||||
process.env.TALKS_PORTAL_DEV = '1'
|
||||
delete process.env.SITE_BASE
|
||||
const server = await createServer('portal', {
|
||||
server: { strictPort: true, host: true },
|
||||
})
|
||||
const port = 28740 + Math.floor(Math.random() * 200)
|
||||
await server.listen(port)
|
||||
try {
|
||||
await callback(port)
|
||||
} finally {
|
||||
await server.close()
|
||||
if (prevDev !== undefined) process.env.TALKS_PORTAL_DEV = prevDev
|
||||
else delete process.env.TALKS_PORTAL_DEV
|
||||
if (prevSite !== undefined) process.env.SITE_BASE = prevSite
|
||||
else delete process.env.SITE_BASE
|
||||
}
|
||||
}
|
||||
|
||||
test('VitePress dev : racine deck → 302 vers …/1 (première planche Slidev)', async () => {
|
||||
await withDevPortal(async (port) => {
|
||||
const base = `http://127.0.0.1:${port}`
|
||||
for (const path of ['/slides/kubernetes-operators/', '/slides/kubernetes-operators']) {
|
||||
const { status, headers } = await get(`${base}${path}`)
|
||||
assert.equal(status, 302, `${path} → attendu 302`)
|
||||
assert.equal(headers.location, '/slides/kubernetes-operators/1', `Location pour ${path}`)
|
||||
}
|
||||
const q = await get(`${base}/slides/kubernetes-operators/?embedded=1`)
|
||||
assert.equal(q.status, 302)
|
||||
assert.equal(q.headers.location, '/slides/kubernetes-operators/1?embedded=1')
|
||||
})
|
||||
})
|
||||
|
||||
test('VitePress dev : GET /slides/kubernetes-operators/1 → 200 (HTML Slidev)', async () => {
|
||||
await withDevPortal(async (port) => {
|
||||
const { status, body } = await get(`http://127.0.0.1:${port}/slides/kubernetes-operators/1`)
|
||||
assert.equal(status, 200, `attendu 200, reçu ${status}`)
|
||||
assert.match(body, /slidev|Slidev|#app/i, 'corps doit ressembler à une page Slidev')
|
||||
})
|
||||
})
|
||||
|
||||
test('Régression MIME module : *.md?import sous /slides/<slug>/ ne renvoie pas text/html (HAR)', async () => {
|
||||
const paths = [
|
||||
'/slides/kubernetes-operators/1.md?import&t=1775912428995',
|
||||
'/slides/kubernetes-operators/index.md?import',
|
||||
'/slides/kubernetes-operators/slides.md?import',
|
||||
]
|
||||
await withDevPortal(async (port) => {
|
||||
for (const p of paths) {
|
||||
const { status, headers, body } = await get(`http://127.0.0.1:${port}${p}`, {
|
||||
Accept: '*/*',
|
||||
'Sec-Fetch-Dest': 'script',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
})
|
||||
const ct = String(headers['content-type'] || '')
|
||||
assert.ok(
|
||||
!ct.toLowerCase().includes('text/html'),
|
||||
`${p} : interdit text/html (reçu ${status}, Content-Type: ${ct || '(vide)'}) — régression MIME module`,
|
||||
)
|
||||
assert.ok(
|
||||
!/^<\s*!DOCTYPE\s+html/i.test(body.trim()),
|
||||
`${p} : le corps ne doit pas être un document HTML (ex. shell VitePress)`,
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
test('VitePress dev : GET …/2 (route client Slidev) → 200 HTML deck', async () => {
|
||||
await withDevPortal(async (port) => {
|
||||
const { status, body } = await get(`http://127.0.0.1:${port}/slides/kubernetes-operators/2`, {
|
||||
Accept: 'text/html',
|
||||
})
|
||||
assert.equal(status, 200)
|
||||
assert.match(body, /slidev|Slidev|#app/i)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user