-
-
Notifications
You must be signed in to change notification settings - Fork 363
Expand file tree
/
Copy pathsmoke.ts
More file actions
182 lines (158 loc) · 4.41 KB
/
Copy pathsmoke.ts
File metadata and controls
182 lines (158 loc) · 4.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/**
* Lightweight smoke tests using fetch - no browser required
* Verifies pages return 200 and contain expected content
*
* Prerequisites:
* - Dev server running on :3000 (or will start one)
* - Library repos cloned as siblings (query, router, table, etc.)
* Dev mode reads docs from local filesystem: ../../../../{repo}
*
* Skipped in CI because:
* - No standalone production server (Netlify serverless deployment)
* - Library repos not available as siblings
*/
import { spawn, type ChildProcess } from 'child_process'
import { createServer } from 'net'
const DEFAULT_URL = 'http://localhost:3000'
// Skip in CI - this app deploys to Netlify serverless, no standalone server
if (process.env.CI === 'true') {
console.log('Skipping smoke tests in CI (Netlify serverless deployment)')
process.exit(0)
}
type TestCase = {
name: string
path: string
expectedContent: string[]
}
const tests: TestCase[] = [
{
name: 'home page',
path: '/',
expectedContent: ['TanStack', '<html', '</html>'],
},
{
name: 'query docs',
path: '/query/latest/docs/framework/react/overview',
expectedContent: ['<html', '</html>', '<h1'],
},
{
name: 'router docs',
path: '/router/latest/docs/framework/react/overview',
expectedContent: ['<html', '</html>', '<h1'],
},
{
name: 'table docs',
path: '/table/latest/docs/introduction',
expectedContent: ['<html', '</html>', '<h1'],
},
]
async function getAvailablePort(): Promise<number> {
return new Promise((resolve, reject) => {
const server = createServer()
server.listen(0, () => {
const address = server.address()
if (address && typeof address === 'object') {
const port = address.port
server.close(() => resolve(port))
} else {
reject(new Error('Failed to get port'))
}
})
server.on('error', reject)
})
}
async function runTest(
baseUrl: string,
test: TestCase,
): Promise<{ pass: boolean; error?: string }> {
try {
const url = `${baseUrl}${test.path}`
const response = await fetch(url, {
headers: { Accept: 'text/html' },
})
if (!response.ok) {
return { pass: false, error: `HTTP ${response.status}` }
}
const html = await response.text()
for (const content of test.expectedContent) {
if (!html.includes(content)) {
return { pass: false, error: `Missing expected content: "${content}"` }
}
}
return { pass: true }
} catch (err) {
return {
pass: false,
error: err instanceof Error ? err.message : String(err),
}
}
}
async function waitForServer(url: string, timeout = 60000): Promise<boolean> {
const start = Date.now()
while (Date.now() - start < timeout) {
try {
const res = await fetch(url, { method: 'HEAD' })
if (res.ok) return true
} catch {
// Server not ready yet
}
await new Promise((r) => setTimeout(r, 500))
}
return false
}
async function checkExistingServer(): Promise<boolean> {
try {
const res = await fetch(DEFAULT_URL)
if (!res.ok) return false
const html = await res.text()
return html.includes('TanStack')
} catch {
return false
}
}
async function main() {
let serverProcess: ChildProcess | null = null
let baseUrl = DEFAULT_URL
const existingServer = await checkExistingServer()
if (existingServer) {
console.log('Using existing server on :3000\n')
} else {
const port = await getAvailablePort()
baseUrl = `http://localhost:${port}`
console.log(`Starting dev server on port ${port}...`)
serverProcess = spawn('pnpm', ['dev'], {
stdio: 'ignore',
detached: true,
shell: true,
env: { ...process.env, PORT: String(port) },
})
const ready = await waitForServer(baseUrl)
if (!ready) {
console.error('Server failed to start within timeout')
serverProcess.kill()
process.exit(1)
}
console.log('Server ready\n')
}
console.log(`Running smoke tests against ${baseUrl}\n`)
let passed = 0
let failed = 0
for (const test of tests) {
const result = await runTest(baseUrl, test)
if (result.pass) {
console.log(` ✓ ${test.name}`)
passed++
} else {
console.log(` ✗ ${test.name}: ${result.error}`)
failed++
}
}
console.log(`\n${passed} passed, ${failed} failed\n`)
if (serverProcess) {
process.kill(-serverProcess.pid!, 'SIGTERM')
}
if (failed > 0) {
process.exit(1)
}
}
main()