Skip to content

Commit bf9f6e9

Browse files
committed
tests: inital framework
1 parent cada9db commit bf9f6e9

19 files changed

+590
-23
lines changed

adex/package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@
5959
"runtime"
6060
],
6161
"scripts": {
62-
"next": "bumpp"
62+
"next": "bumpp",
63+
"test": "vitest"
6364
},
6465
"dependencies": {
6566
"@barelyhuman/tiny-use": "^0.0.2",
@@ -82,7 +83,8 @@
8283
"adex-adapter-node": "^0.0.17",
8384
"autoprefixer": "^10.4.19",
8485
"tailwindcss": "^3.4.4",
85-
"vite": "^5.3.1"
86+
"vite": "^5.3.1",
87+
"vitest": "^3.0.5"
8688
},
8789
"peerDependenciesMeta": {
8890
"adex-adapter-node": {

adex/runtime/client.js

+3-9
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,6 @@ import 'virtual:adex:global.css'
1313
// @ts-expect-error injected by vite
1414
import { routes } from '~routes'
1515

16-
const withComponents = routes.map(d => {
17-
return {
18-
...d,
19-
component: lazy(d.module),
20-
}
21-
})
22-
2316
function ComponentWrapper({ url = '' }) {
2417
return h(
2518
LocationProvider,
@@ -31,8 +24,8 @@ function ComponentWrapper({ url = '' }) {
3124
h(
3225
Router,
3326
{},
34-
withComponents.map(d =>
35-
h(Route, { path: d.routePath, component: d.component })
27+
routes.map(d =>
28+
h(Route, { path: d.routePath, component: lazy(d.module) })
3629
)
3730
)
3831
)
@@ -46,6 +39,7 @@ export const App = ({ url = '' }) => {
4639
async function hydrate() {
4740
preactHydrate(h(ComponentWrapper, {}), document.getElementById('app'))
4841
}
42+
4943
if (typeof window !== 'undefined') {
5044
hydrate()
5145
}

adex/runtime/handler.js

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { CONSTANTS, emitToHooked } from 'adex/hook'
22
import { prepareRequest, prepareResponse } from 'adex/http'
33
import { toStatic } from 'adex/ssr'
4-
import { renderToString } from 'adex/utils/isomorphic'
4+
import { renderToStringAsync } from 'adex/utils/isomorphic'
55
import { h } from 'preact'
66

77
// @ts-expect-error injected by vite
@@ -60,9 +60,7 @@ export async function handler(req, res) {
6060
// @ts-expect-error
6161
global.location = new URL(req.url, 'http://localhost')
6262

63-
const rendered = await renderToString(
64-
h(App, { url: [baseURL, search].filter(Boolean).join('?') })
65-
)
63+
const rendered = await renderToStringAsync(h(App, { url: req.url }), {})
6664

6765
const htmlString = HTMLTemplate({
6866
metas,
@@ -73,7 +71,7 @@ export async function handler(req, res) {
7371
routeParams: Buffer.from(JSON.stringify(routeParams), 'utf8').toString(
7472
'base64'
7573
),
76-
body: rendered.html,
74+
body: rendered,
7775
})
7876
const modifiableContext = {
7977
req: req,

adex/src/head.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export type {
1+
export {
22
useHead,
33
useLang,
44
useLink,

adex/src/utils/isomorphic.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
export { parse as pathToRegex } from 'regexparam'
2-
export { prerender as renderToString } from 'preact-iso'
2+
export { renderToStringAsync } from 'preact-render-to-string'

adex/src/vite.js

+66-1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,71 @@ export function adex({
6161
'virtual:adex:handler',
6262
readFileSync(join(__dirname, '../runtime/handler.js'), 'utf8')
6363
),
64+
createVirtualModule(
65+
'virtual:adex:server',
66+
`import { createServer } from '${adapterMap[adapter]}'
67+
import { dirname, join } from 'node:path'
68+
import { fileURLToPath } from 'node:url'
69+
import { existsSync, readFileSync } from 'node:fs'
70+
import { env } from 'adex/env'
71+
72+
import 'virtual:adex:font.css'
73+
import 'virtual:adex:global.css'
74+
75+
const __dirname = dirname(fileURLToPath(import.meta.url))
76+
77+
const PORT = parseInt(env.get('PORT', '3000'), 10)
78+
const HOST = env.get('HOST', 'localhost')
79+
80+
const paths = {
81+
assets: join(__dirname, './assets'),
82+
islands: join(__dirname, './islands'),
83+
client: join(__dirname, '../client'),
84+
}
85+
86+
function getServerManifest() {
87+
const manifestPath = join(__dirname, 'manifest.json')
88+
if (existsSync(manifestPath)) {
89+
const manifestFile = readFileSync(manifestPath, 'utf8')
90+
return parseManifest(manifestFile)
91+
}
92+
return {}
93+
}
94+
95+
function getClientManifest() {
96+
const manifestPath = join(__dirname, '../client/manifest.json')
97+
if (existsSync(manifestPath)) {
98+
const manifestFile = readFileSync(manifestPath, 'utf8')
99+
return parseManifest(manifestFile)
100+
}
101+
return {}
102+
}
103+
104+
function parseManifest(manifestString) {
105+
try {
106+
const manifestJSON = JSON.parse(manifestString)
107+
return manifestJSON
108+
} catch (err) {
109+
return {}
110+
}
111+
}
112+
113+
const server = createServer({
114+
port: PORT,
115+
host: HOST,
116+
adex:{
117+
manifests:{server:getServerManifest(),client:getClientManifest()},
118+
paths,
119+
}
120+
})
121+
122+
if ('run' in server) {
123+
server.run()
124+
}
125+
126+
export default server.fetch
127+
`
128+
),
64129
addFontsPlugin(fonts),
65130
adexDevServer({ islands }),
66131
adexBuildPrep({ islands }),
@@ -655,7 +720,7 @@ function adexGuards() {
655720

656721
// ignore usage of `process.env` in `adex/env`
657722
const envLoadId = await this.resolve('adex/env')
658-
if (id === envLoadId.id) return
723+
if (id === envLoadId?.id) return
659724

660725
if (code.includes('process.env')) {
661726
this.error(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`ssr minimal > basic response 1`] = `
4+
"
5+
<!doctype html>
6+
<html lang="">
7+
<head>
8+
<script type="module" src="/@vite/client"></script>
9+
10+
<meta charset="UTF-8" />
11+
12+
<title></title>
13+
14+
15+
16+
17+
</head>
18+
<body>
19+
<div id="app"><h1>Hello World</h1></div>
20+
<script type='module' src="/virtual:adex:client"></script></body>
21+
</html>
22+
"
23+
`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"compilerOptions":{
3+
"jsx":"react-jsx",
4+
"jsxImportSource": "preact"
5+
}
6+
}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "minimal",
3+
"type": "module",
4+
"dependencies": {
5+
"adex": "workspace:*",
6+
"preact": "catalog:",
7+
"@preact/preset-vite": "catalog:"
8+
},
9+
10+
"devDependencies": {
11+
"vite": "^5.4.8"
12+
}
13+
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function AboutPage() {
2+
return <h2>About</h2>
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return <h1>Hello World</h1>
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function workaroundPage() {
2+
return <></>
3+
}
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { defineConfig } from 'vite'
2+
import { adex } from "adex"
3+
import preact from "@preact/preset-vite"
4+
5+
export default defineConfig({
6+
plugins: [
7+
adex({
8+
islands: false,
9+
ssr: true,
10+
}),
11+
preact(),
12+
],
13+
})

adex/tests/index.spec.js

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { beforeAll, describe, expect, it } from 'vitest'
2+
3+
import { devServerURL, launchDemoDevServer } from './utils.js'
4+
5+
describe('ssr minimal', () => {
6+
beforeAll(async () => {
7+
await launchDemoDevServer('tests/fixtures/minimal')
8+
})
9+
10+
it('basic response', async () => {
11+
const response = await fetch(devServerURL).then(d => d.text())
12+
expect(response).toMatchSnapshot()
13+
})
14+
})

adex/tests/utils.js

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { spawn } from 'node:child_process'
2+
import path from 'node:path'
3+
import { fileURLToPath } from 'node:url'
4+
5+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
6+
export const dir = (...args) => path.join(__dirname, '..', ...args)
7+
8+
export const devServerURL = new URL('http://localhost:5173/')
9+
10+
/**
11+
* Wait for vite dev server to start
12+
* @param {import('node:child_process').ChildProcess} devServerProc
13+
* @returns {Promise<void>}
14+
*/
15+
function waitForServerStart(devServerProc) {
16+
return new Promise((resolve, reject) => {
17+
function onError(err) {
18+
cleanup()
19+
reject(err)
20+
}
21+
22+
/** @param {number | null} code */
23+
function onClose(code) {
24+
cleanup()
25+
reject(new Error(`Dev server closed unexpectedly with code "${code}"`))
26+
}
27+
28+
let serverReady = false
29+
let stdout = ''
30+
/** @param {Buffer | string} chunk */
31+
function onData(chunk) {
32+
try {
33+
/** @type {string} */
34+
const data = Buffer.isBuffer(chunk)
35+
? chunk.toString('utf-8')
36+
: chunk.toString()
37+
38+
stdout += data
39+
40+
if (stdout.match(/ready\sin\s[0-9]+\sms/g) != null) {
41+
serverReady = true
42+
}
43+
44+
if (stdout.match(/localhost:(\d+)/) != null) {
45+
const matchedPort = stdout.match(/localhost:(\d+)/)
46+
devServerURL.port = matchedPort[1]
47+
if (serverReady) {
48+
cleanup()
49+
resolve()
50+
}
51+
}
52+
} catch (e) {
53+
reject(e)
54+
}
55+
}
56+
57+
function cleanup() {
58+
try {
59+
devServerProc.stdout?.off('data', onData)
60+
devServerProc.off('error', onError)
61+
devServerProc.off('close', onClose)
62+
} catch (e) {
63+
reject(e)
64+
}
65+
}
66+
67+
devServerProc.stdout?.on('data', onData)
68+
devServerProc.on('error', onError)
69+
devServerProc.on('close', onClose)
70+
})
71+
}
72+
73+
/**
74+
* @param {string} fixturePath
75+
*/
76+
export async function launchDemoDevServer(fixturePath) {
77+
console.log(`launching on ${dir(fixturePath)}`)
78+
/** @type {import('node:child_process').ChildProcess} */
79+
const devServerProc = spawn(
80+
process.execPath,
81+
[dir('node_modules/vite/bin/vite.js')],
82+
{ cwd: dir(fixturePath), stdio: 'pipe' }
83+
)
84+
85+
await waitForServerStart(devServerProc)
86+
87+
// Ensure the server remains active until the test completes.
88+
process.once('exit', () => {
89+
devServerProc.kill()
90+
})
91+
92+
return devServerProc
93+
}

adex/vitest.config.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { defineConfig } from 'vite'
2+
3+
export default defineConfig({
4+
test: {
5+
testTimeout: 10_000,
6+
},
7+
})

playground/vite.config.js

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export default defineConfig({
88
plugins: [
99
adex({
1010
islands: false,
11-
ssr: false,
1211
fonts: {
1312
providers: [providers.google()],
1413
families: [

0 commit comments

Comments
 (0)