|
| 1 | +// ABOUTME: End-to-end tests that boot the Next.js dev server and verify |
| 2 | +// ABOUTME: .md-suffixed docs URLs serve markdown while canonical URLs stay HTML. |
| 3 | +import { afterAll, beforeAll, describe, expect, setDefaultTimeout, test } from 'bun:test'; |
| 4 | +import { fileURLToPath } from 'node:url'; |
| 5 | + |
| 6 | +// The dev server compiles the middleware and markdown route on first request. |
| 7 | +setDefaultTimeout(120000); |
| 8 | + |
| 9 | +const PROJECT_ROOT = fileURLToPath(new URL('../..', import.meta.url)); |
| 10 | +const PORT = 3300 + Math.floor(Math.random() * 300); |
| 11 | +const BASE_URL = `http://localhost:${PORT}`; |
| 12 | + |
| 13 | +const BROWSER_HEADERS = { |
| 14 | + accept: 'text/html,application/xhtml+xml', |
| 15 | + 'sec-fetch-dest': 'document', |
| 16 | + 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Safari/605.1.15', |
| 17 | +}; |
| 18 | + |
| 19 | +let server: ReturnType<typeof Bun.spawn>; |
| 20 | + |
| 21 | +async function waitForServer(): Promise<void> { |
| 22 | + const deadline = Date.now() + 90000; |
| 23 | + while (Date.now() < deadline) { |
| 24 | + try { |
| 25 | + // Any HTTP response means the server is up; fetch throws until the |
| 26 | + // port accepts connections. Don't probe generated files like |
| 27 | + // /llms.txt, which don't exist in CI when tests run. |
| 28 | + await fetch(BASE_URL, { headers: BROWSER_HEADERS }); |
| 29 | + return; |
| 30 | + } catch { |
| 31 | + // Server not accepting connections yet |
| 32 | + } |
| 33 | + await Bun.sleep(1000); |
| 34 | + } |
| 35 | + throw new Error(`Dev server did not become ready on port ${PORT}`); |
| 36 | +} |
| 37 | + |
| 38 | +describe('.md suffix end-to-end', () => { |
| 39 | + beforeAll(async () => { |
| 40 | + server = Bun.spawn(['bunx', 'next', 'dev', '--turbopack', '-p', String(PORT)], { |
| 41 | + cwd: PROJECT_ROOT, |
| 42 | + stdout: 'ignore', |
| 43 | + stderr: 'ignore', |
| 44 | + }); |
| 45 | + await waitForServer(); |
| 46 | + }); |
| 47 | + |
| 48 | + afterAll(async () => { |
| 49 | + // Wait for the process to fully exit so the dev server's CPU and port |
| 50 | + // are released before later test files (Chromium renders) start. |
| 51 | + server?.kill(); |
| 52 | + await server?.exited; |
| 53 | + }); |
| 54 | + |
| 55 | + test('serves markdown at a .md-suffixed docs URL', async () => { |
| 56 | + const response = await fetch(`${BASE_URL}/overview/sessions-api/quickstart.md`, { |
| 57 | + headers: BROWSER_HEADERS, |
| 58 | + }); |
| 59 | + expect(response.status).toBe(200); |
| 60 | + expect(response.headers.get('content-type')).toStartWith('text/markdown'); |
| 61 | + expect(await response.text()).toStartWith('# Quickstart'); |
| 62 | + }); |
| 63 | + |
| 64 | + test('returns 404 for a .md URL with no matching page', async () => { |
| 65 | + const response = await fetch(`${BASE_URL}/nonexistent-page.md`, { |
| 66 | + headers: BROWSER_HEADERS, |
| 67 | + }); |
| 68 | + expect(response.status).toBe(404); |
| 69 | + }); |
| 70 | + |
| 71 | + test('still serves HTML at the canonical URL for browsers', async () => { |
| 72 | + const response = await fetch(`${BASE_URL}/overview/sessions-api/quickstart`, { |
| 73 | + headers: BROWSER_HEADERS, |
| 74 | + }); |
| 75 | + expect(response.status).toBe(200); |
| 76 | + expect(response.headers.get('content-type')).toStartWith('text/html'); |
| 77 | + }); |
| 78 | +}); |
0 commit comments