- 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.
130 lines
5.0 KiB
JavaScript
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)
|
|
})
|
|
})
|