Files
Talks/tests/vitepress-slides-route.test.mjs
Le Prévost-Corvellec Arnault 354c8cd9ef 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.
2026-04-11 15:59:20 +02:00

130 lines
5.0 KiB
JavaScript

/**
* 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)
})
})