0
0
mirror of https://github.com/honojs/hono.git synced 2024-12-01 10:51:01 +00:00

feat: update visibility (#164)

This commit is contained in:
Yusuke Wada 2022-04-21 21:55:01 +09:00 committed by GitHub
parent 1a30e37dfa
commit 3d064d0559
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 75 additions and 101 deletions

View File

@ -25,7 +25,7 @@ export class Context<RequestParamKeyType = string> {
req: Request<RequestParamKeyType>, req: Request<RequestParamKeyType>,
opts?: { res: Response; env: Env; event: FetchEvent } opts?: { res: Response; env: Env; event: FetchEvent }
) { ) {
this.req = req this.req = this.initRequest(req)
if (opts) { if (opts) {
this.res = opts.res this.res = opts.res
this.env = opts.env this.env = opts.env
@ -34,14 +34,18 @@ export class Context<RequestParamKeyType = string> {
this._headers = {} this._headers = {}
} }
private initRequest<T>(req: Request<T>): Request<T> {
req.header = (name: string): string => {
return req.headers.get(name)
}
req.query = (key: string): string => {
const url = new URL(req.url)
return url.searchParams.get(key)
}
return req
}
header(name: string, value: string): void { header(name: string, value: string): void {
/*
XXX:
app.use('*', (c, next) => {
next()
c.header('foo', 'bar') // => c.res.headers.set(...)
})
*/
if (this.res) { if (this.res) {
this.res.headers.set(name, value) this.res.headers.set(name, value)
} }
@ -101,7 +105,7 @@ export class Context<RequestParamKeyType = string> {
json(object: object, status: StatusCode = this._status, headers: Headers = {}): Response { json(object: object, status: StatusCode = this._status, headers: Headers = {}): Response {
if (typeof object !== 'object') { if (typeof object !== 'object') {
throw new TypeError('json method arg must be a object!') throw new TypeError('json method arg must be an object!')
} }
const body = this._pretty const body = this._pretty
? JSON.stringify(object, null, this._prettySpace) ? JSON.stringify(object, null, this._prettySpace)

View File

@ -11,7 +11,6 @@ declare global {
param: (key: ParamKeyType) => string param: (key: ParamKeyType) => string
query: (key: string) => string query: (key: string) => string
header: (name: string) => string header: (name: string) => string
// TODO: do not use `any`
parsedBody: any parsedBody: any
} }
} }
@ -64,11 +63,11 @@ export class Hono extends defineDynamicClass(
'patch', 'patch',
'all' 'all'
) { ) {
routerClass: { new (): Router<any> } = TrieRouter readonly routerClass: { new (): Router<any> } = TrieRouter
strict: boolean = true // strict routing - default is true readonly strict: boolean = true // strict routing - default is true
router: Router<Handler> private router: Router<Handler>
middlewareRouters: Router<MiddlewareHandler>[] private middlewareRouters: Router<MiddlewareHandler>[]
tempPath: string private tempPath: string
constructor(init: Partial<Pick<Hono, 'routerClass' | 'strict'>> = {}) { constructor(init: Partial<Pick<Hono, 'routerClass' | 'strict'>> = {}) {
super() super()
@ -137,7 +136,7 @@ export class Hono extends defineDynamicClass(
return this.router.match(method, path) return this.router.match(method, path)
} }
async dispatch(request: Request, env?: Env, event?: FetchEvent): Promise<Response> { private async dispatch(request: Request, env?: Env, event?: FetchEvent): Promise<Response> {
const path = getPathFromURL(request.url, { strict: this.strict }) const path = getPathFromURL(request.url, { strict: this.strict })
const method = request.method const method = request.method
@ -147,13 +146,6 @@ export class Hono extends defineDynamicClass(
request.param = (key: string): string => { request.param = (key: string): string => {
if (result) return result.params[key] if (result) return result.params[key]
} }
request.header = (name: string): string => {
return request.headers.get(name)
}
request.query = (key: string): string => {
const url = new URL(c.req.url)
return url.searchParams.get(key)
}
const handler = result ? result.handler : this.notFoundHandler const handler = result ? result.handler : this.notFoundHandler
@ -192,12 +184,12 @@ export class Hono extends defineDynamicClass(
return this.dispatch(request, env, event) return this.dispatch(request, env, event)
} }
request(input: RequestInfo, requestInit?: RequestInit) { request(input: RequestInfo, requestInit?: RequestInit): Promise<Response> {
const req = new Request(input, requestInit) const req = input instanceof Request ? input : new Request(input, requestInit)
return this.dispatch(req) return this.dispatch(req)
} }
fire() { fire(): void {
addEventListener('fetch', (event: FetchEvent): void => { addEventListener('fetch', (event: FetchEvent): void => {
event.respondWith(this.handleEvent(event)) event.respondWith(this.handleEvent(event))
}) })

View File

@ -67,42 +67,42 @@ describe('Basic Auth by Middleware', () => {
app.get('/auth-multi/*', () => new Response('auth')) app.get('/auth-multi/*', () => new Response('auth'))
app.get('/auth-override-func/*', () => new Response('auth')) app.get('/auth-override-func/*', () => new Response('auth'))
it('Unauthorized', async () => { it('Should not authorize', async () => {
const req = new Request('http://localhost/auth/a') const req = new Request('http://localhost/auth/a')
const res = await app.dispatch(req) const res = await app.request(req)
expect(res).not.toBeNull() expect(res).not.toBeNull()
expect(res.status).toBe(401) expect(res.status).toBe(401)
expect(await res.text()).toBe('Unauthorized') expect(await res.text()).toBe('Unauthorized')
}) })
it('Authorizated', async () => { it('Should authorize', async () => {
const credential = Buffer.from(username + ':' + password).toString('base64') const credential = Buffer.from(username + ':' + password).toString('base64')
const req = new Request('http://localhost/auth/a') const req = new Request('http://localhost/auth/a')
req.headers.set('Authorization', `Basic ${credential}`) req.headers.set('Authorization', `Basic ${credential}`)
const res = await app.dispatch(req) const res = await app.request(req)
expect(res).not.toBeNull() expect(res).not.toBeNull()
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(await res.text()).toBe('auth') expect(await res.text()).toBe('auth')
}) })
it('Authorizated Unicode', async () => { it('Should authorize Unicode', async () => {
const credential = Buffer.from(username + ':' + unicodePassword).toString('base64') const credential = Buffer.from(username + ':' + unicodePassword).toString('base64')
const req = new Request('http://localhost/auth-unicode/a') const req = new Request('http://localhost/auth-unicode/a')
req.headers.set('Authorization', `Basic ${credential}`) req.headers.set('Authorization', `Basic ${credential}`)
const res = await app.dispatch(req) const res = await app.request(req)
expect(res).not.toBeNull() expect(res).not.toBeNull()
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(await res.text()).toBe('auth') expect(await res.text()).toBe('auth')
}) })
it('Authorizated multiple users', async () => { it('Should authorize multiple users', async () => {
let credential = Buffer.from(usernameB + ':' + passwordB).toString('base64') let credential = Buffer.from(usernameB + ':' + passwordB).toString('base64')
let req = new Request('http://localhost/auth-multi/b') let req = new Request('http://localhost/auth-multi/b')
req.headers.set('Authorization', `Basic ${credential}`) req.headers.set('Authorization', `Basic ${credential}`)
let res = await app.dispatch(req) let res = await app.request(req)
expect(res).not.toBeNull() expect(res).not.toBeNull()
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(await res.text()).toBe('auth') expect(await res.text()).toBe('auth')
@ -110,18 +110,18 @@ describe('Basic Auth by Middleware', () => {
credential = Buffer.from(usernameC + ':' + passwordC).toString('base64') credential = Buffer.from(usernameC + ':' + passwordC).toString('base64')
req = new Request('http://localhost/auth-multi/c') req = new Request('http://localhost/auth-multi/c')
req.headers.set('Authorization', `Basic ${credential}`) req.headers.set('Authorization', `Basic ${credential}`)
res = await app.dispatch(req) res = await app.request(req)
expect(res).not.toBeNull() expect(res).not.toBeNull()
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(await res.text()).toBe('auth') expect(await res.text()).toBe('auth')
}) })
it('should authorize with sha256 function override', async () => { it('Should authorize with sha256 function override', async () => {
const credential = Buffer.from(username + ':' + password).toString('base64') const credential = Buffer.from(username + ':' + password).toString('base64')
const req = new Request('http://localhost/auth-override-func/a') const req = new Request('http://localhost/auth-override-func/a')
req.headers.set('Authorization', `Basic ${credential}`) req.headers.set('Authorization', `Basic ${credential}`)
const res = await app.dispatch(req) const res = await app.request(req)
expect(res).not.toBeNull() expect(res).not.toBeNull()
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(await res.text()).toBe('auth') expect(await res.text()).toBe('auth')

View File

@ -22,7 +22,7 @@ describe('Parse Body Middleware', () => {
body: JSON.stringify(payload), body: JSON.stringify(payload),
headers: new Headers({ 'Content-Type': 'application/json' }), headers: new Headers({ 'Content-Type': 'application/json' }),
}) })
const res = await app.dispatch(req) const res = await app.request(req)
expect(res).not.toBeNull() expect(res).not.toBeNull()
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(req.parsedBody).toEqual(payload) expect(req.parsedBody).toEqual(payload)
@ -36,7 +36,7 @@ describe('Parse Body Middleware', () => {
body: 'hello', body: 'hello',
headers: new Headers({ 'Content-Type': 'application/text' }), headers: new Headers({ 'Content-Type': 'application/text' }),
}) })
const res = await app.dispatch(req) const res = await app.request(req)
expect(res).not.toBeNull() expect(res).not.toBeNull()
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(req.parsedBody).toEqual(payload) expect(req.parsedBody).toEqual(payload)
@ -53,7 +53,7 @@ describe('Parse Body Middleware', () => {
'Content-Type': 'application/x-www-form-urlencoded', 'Content-Type': 'application/x-www-form-urlencoded',
}, },
}) })
const res = await app.dispatch(req) const res = await app.request(req)
expect(res).not.toBeNull() expect(res).not.toBeNull()
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(req.parsedBody).toEqual({ message: 'hello' }) expect(req.parsedBody).toEqual({ message: 'hello' })

View File

@ -18,7 +18,7 @@ describe('Cookie Middleware', () => {
const req = new Request('http://localhost/cookie') const req = new Request('http://localhost/cookie')
const cookieString = 'yummy_cookie=choco; tasty_cookie = strawberry ' const cookieString = 'yummy_cookie=choco; tasty_cookie = strawberry '
req.headers.set('Cookie', cookieString) req.headers.set('Cookie', cookieString)
const res = await app.dispatch(req) const res = await app.request(req)
expect(res.headers.get('Yummy-Cookie')).toBe('choco') expect(res.headers.get('Yummy-Cookie')).toBe('choco')
expect(res.headers.get('Tasty-Cookie')).toBe('strawberry') expect(res.headers.get('Tasty-Cookie')).toBe('strawberry')
@ -32,8 +32,7 @@ describe('Cookie Middleware', () => {
}) })
it('Set cookie on c.cookie', async () => { it('Set cookie on c.cookie', async () => {
const req = new Request('http://localhost/set-cookie') const res = await app.request('http://localhost/set-cookie')
const res = await app.dispatch(req)
expect(res.status).toBe(200) expect(res.status).toBe(200)
const header = res.headers.get('Set-Cookie') const header = res.headers.get('Set-Cookie')
expect(header).toBe('delicious_cookie=macha') expect(header).toBe('delicious_cookie=macha')
@ -55,8 +54,7 @@ describe('Cookie Middleware', () => {
}) })
it('Complex pattern', async () => { it('Complex pattern', async () => {
const req = new Request('http://localhost/set-cookie-complex') const res = await app.request('http://localhost/set-cookie-complex')
const res = await app.dispatch(req)
expect(res.status).toBe(200) expect(res.status).toBe(200)
const header = res.headers.get('Set-Cookie') const header = res.headers.get('Set-Cookie')
expect(header).toBe( expect(header).toBe(

View File

@ -25,8 +25,7 @@ describe('CORS by Middleware', () => {
}) })
it('GET default', async () => { it('GET default', async () => {
const req = new Request('http://localhost/api/abc') const res = await app.request('http://localhost/api/abc')
const res = await app.dispatch(req)
expect(res.headers.get('Access-Control-Allow-Origin')).toBe('*') expect(res.headers.get('Access-Control-Allow-Origin')).toBe('*')
expect(res.headers.get('Vary')).toBeNull() expect(res.headers.get('Vary')).toBeNull()
@ -35,7 +34,7 @@ describe('CORS by Middleware', () => {
it('Preflight default', async () => { it('Preflight default', async () => {
const req = new Request('https://localhost/api/abc', { method: 'OPTIONS' }) const req = new Request('https://localhost/api/abc', { method: 'OPTIONS' })
req.headers.append('Access-Control-Request-Headers', 'X-PINGOTHER, Content-Type') req.headers.append('Access-Control-Request-Headers', 'X-PINGOTHER, Content-Type')
const res = await app.dispatch(req) const res = await app.request(req)
expect(res.status).toBe(204) expect(res.status).toBe(204)
expect(res.headers.get('Access-Control-Allow-Methods').split(',')[0]).toBe('GET') expect(res.headers.get('Access-Control-Allow-Methods').split(',')[0]).toBe('GET')
@ -47,7 +46,7 @@ describe('CORS by Middleware', () => {
it('Preflight with options', async () => { it('Preflight with options', async () => {
const req = new Request('https://localhost/api2/abc', { method: 'OPTIONS' }) const req = new Request('https://localhost/api2/abc', { method: 'OPTIONS' })
const res = await app.dispatch(req) const res = await app.request(req)
expect(res.headers.get('Access-Control-Allow-Origin')).toBe('http://example.com') expect(res.headers.get('Access-Control-Allow-Origin')).toBe('http://example.com')
expect(res.headers.get('Vary').split(/\s*,\s*/)).toEqual(expect.arrayContaining(['Origin'])) expect(res.headers.get('Vary').split(/\s*,\s*/)).toEqual(expect.arrayContaining(['Origin']))

View File

@ -17,43 +17,34 @@ describe('Etag Middleware', () => {
return c.text('Hono is cool') return c.text('Hono is cool')
}) })
it('should return etag header', async () => { it('Should return etag header', async () => {
let req = new Request('http://localhost/etag/abc') let res = await app.request('http://localhost/etag/abc')
let res = await app.dispatch(req)
expect(res.headers.get('ETag')).not.toBeFalsy() expect(res.headers.get('ETag')).not.toBeFalsy()
expect(res.headers.get('ETag')).toBe('"4e32298b1cb4edc595237405e5b696e105c2399a"') expect(res.headers.get('ETag')).toBe('"4e32298b1cb4edc595237405e5b696e105c2399a"')
req = new Request('http://localhost/etag/def') res = await app.request('http://localhost/etag/def')
res = await app.dispatch(req)
expect(res.headers.get('ETag')).not.toBeFalsy() expect(res.headers.get('ETag')).not.toBeFalsy()
expect(res.headers.get('ETag')).toBe('"c1d44ff03aff1372856c281854f454e2e1d15b7c"') expect(res.headers.get('ETag')).toBe('"c1d44ff03aff1372856c281854f454e2e1d15b7c"')
}) })
it('should return etag header - weak', async () => { it('Should return etag header - weak', async () => {
const req = new Request('http://localhost/etag-weak/abc') const res = await app.request('http://localhost/etag-weak/abc')
const res = await app.dispatch(req)
expect(res.headers.get('ETag')).not.toBeFalsy() expect(res.headers.get('ETag')).not.toBeFalsy()
expect(res.headers.get('ETag')).toBe('W/"4e32298b1cb4edc595237405e5b696e105c2399a"') expect(res.headers.get('ETag')).toBe('W/"4e32298b1cb4edc595237405e5b696e105c2399a"')
}) })
it('should return 304 response', async () => { it('Should return 304 response', async () => {
let req = new Request('http://localhost/etag/abc') let res = await app.request('http://localhost/etag/abc')
let res = await app.dispatch(req)
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(res.headers.get('ETag')).not.toBeFalsy() expect(res.headers.get('ETag')).not.toBeFalsy()
const etag = res.headers.get('Etag') const etag = res.headers.get('Etag')
req = new Request('http://localhost/etag/abc', { const req = new Request('http://localhost/etag/abc', {
headers: { headers: {
'If-None-Match': etag, 'If-None-Match': etag,
}, },
}) })
res = await app.dispatch(req) res = await app.request(req)
expect(res.status).toBe(304) expect(res.status).toBe(304)
expect(await res.text()).toBe('') expect(await res.text()).toBe('')
}) })

View File

@ -18,8 +18,7 @@ describe('Logger by Middleware', () => {
app.get('/empty', (c) => c.text('')) app.get('/empty', (c) => c.text(''))
it('Log status 200 with empty body', async () => { it('Log status 200 with empty body', async () => {
const req = new Request('http://localhost/empty') const res = await app.request('http://localhost/empty')
const res = await app.dispatch(req)
expect(res).not.toBeNull() expect(res).not.toBeNull()
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(log.startsWith(' --> GET /empty \x1b[32m200\x1b[0m')).toBe(true) expect(log.startsWith(' --> GET /empty \x1b[32m200\x1b[0m')).toBe(true)
@ -27,8 +26,7 @@ describe('Logger by Middleware', () => {
}) })
it('Log status 200 with small body', async () => { it('Log status 200 with small body', async () => {
const req = new Request('http://localhost/short') const res = await app.request('http://localhost/short')
const res = await app.dispatch(req)
expect(res).not.toBeNull() expect(res).not.toBeNull()
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(log.startsWith(' --> GET /short \x1b[32m200\x1b[0m')).toBe(true) expect(log.startsWith(' --> GET /short \x1b[32m200\x1b[0m')).toBe(true)
@ -36,8 +34,7 @@ describe('Logger by Middleware', () => {
}) })
it('Log status 200 with big body', async () => { it('Log status 200 with big body', async () => {
const req = new Request('http://localhost/long') const res = await app.request('http://localhost/long')
const res = await app.dispatch(req)
expect(res).not.toBeNull() expect(res).not.toBeNull()
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(log.startsWith(' --> GET /long \x1b[32m200\x1b[0m')).toBe(true) expect(log.startsWith(' --> GET /long \x1b[32m200\x1b[0m')).toBe(true)
@ -49,9 +46,7 @@ describe('Logger by Middleware', () => {
app.all('*', (c) => { app.all('*', (c) => {
return c.text(msg, 404) return c.text(msg, 404)
}) })
const res = await app.request('http://localhost/notfound')
const req = new Request('http://localhost/notfound')
const res = await app.dispatch(req)
expect(res).not.toBeNull() expect(res).not.toBeNull()
expect(res.status).toBe(404) expect(res.status).toBe(404)
expect(log.startsWith(' --> GET /notfound \x1b[33m404\x1b[0m')).toBe(true) expect(log.startsWith(' --> GET /notfound \x1b[33m404\x1b[0m')).toBe(true)

View File

@ -45,15 +45,13 @@ describe('Mustache by Middleware', () => {
}) })
it('Mustache template rendering', async () => { it('Mustache template rendering', async () => {
const req = new Request('http://localhost/foo') const res = await app.request('http://localhost/foo')
const res = await app.dispatch(req)
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(await res.text()).toBe('<html><body>Title: Hono!</body></html>') expect(await res.text()).toBe('<html><body>Title: Hono!</body></html>')
}) })
it('Mustache template rendering with root', async () => { it('Mustache template rendering with root', async () => {
const req = new Request('http://localhost/bar') const res = await app.request('http://localhost/bar')
const res = await app.dispatch(req)
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(await res.text()).toBe('<h1>With Root</h1>') expect(await res.text()).toBe('<h1>With Root</h1>')
}) })

View File

@ -5,11 +5,10 @@ describe('Powered by Middleware', () => {
const app = new Hono() const app = new Hono()
app.use('*', poweredBy()) app.use('*', poweredBy())
app.get('/', () => new Response('root')) app.get('/', (c) => c.text('root'))
it('Response headers include X-Powered-By', async () => { it('Should return with X-Powered-By header', async () => {
const req = new Request('http://localhost/') const res = await app.request('http://localhost/')
const res = await app.dispatch(req)
expect(res).not.toBeNull() expect(res).not.toBeNull()
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(res.headers.get('X-Powered-By')).toBe('Hono') expect(res.headers.get('X-Powered-By')).toBe('Hono')

View File

@ -10,8 +10,7 @@ describe('JSON pretty by Middleware', () => {
it('Should return pretty JSON output', async () => { it('Should return pretty JSON output', async () => {
app.use('*', prettyJSON()) app.use('*', prettyJSON())
const req = new Request('http://localhost/?pretty') const res = await app.request('http://localhost/?pretty')
const res = await app.dispatch(req)
expect(res).not.toBeNull() expect(res).not.toBeNull()
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(await res.text()).toBe(`{ expect(await res.text()).toBe(`{
@ -21,8 +20,7 @@ describe('JSON pretty by Middleware', () => {
it('Should return pretty JSON output with 4 spaces', async () => { it('Should return pretty JSON output with 4 spaces', async () => {
app.use('*', prettyJSON({ space: 4 })) app.use('*', prettyJSON({ space: 4 }))
const req = new Request('http://localhost/?pretty') const res = await app.request('http://localhost/?pretty')
const res = await app.dispatch(req)
expect(res).not.toBeNull() expect(res).not.toBeNull()
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(await res.text()).toBe(`{ expect(await res.text()).toBe(`{

View File

@ -26,40 +26,40 @@ Object.assign(global, {
describe('ServeStatic Middleware', () => { describe('ServeStatic Middleware', () => {
const app = new Hono() const app = new Hono()
app.use('/static/*', serveStatic({ root: './assets' })) app.use('/static/*', serveStatic({ root: './assets' }))
app.use('/static-no-root/*', serveStatic()) app.use('/static-no-root/*', serveStatic())
it('Serve static files', async () => { it('Should return plain.txt', async () => {
let req = new Request('http://localhost/static/plain.txt') const res = await app.request('http://localhost/static/plain.txt')
let res = await app.dispatch(req)
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(await res.text()).toBe('This is plain.txt') expect(await res.text()).toBe('This is plain.txt')
expect(res.headers.get('Content-Type')).toBe('text/plain; charset=utf-8') expect(res.headers.get('Content-Type')).toBe('text/plain; charset=utf-8')
expect(res.headers.get('Content-Length')).toBe('17') expect(res.headers.get('Content-Length')).toBe('17')
})
req = new Request('http://localhost/static/hono.html') it('Should return hono.html', async () => {
res = await app.dispatch(req) const res = await app.request('http://localhost/static/hono.html')
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(await res.text()).toBe('<h1>Hono!</h1>') expect(await res.text()).toBe('<h1>Hono!</h1>')
expect(res.headers.get('Content-Type')).toBe('text/html; charset=utf-8') expect(res.headers.get('Content-Type')).toBe('text/html; charset=utf-8')
expect(res.headers.get('Content-Length')).toBe('14') expect(res.headers.get('Content-Length')).toBe('14')
})
req = new Request('http://localhost/static/not-found.html') it('Should return 404 response', async () => {
res = await app.dispatch(req) const res = await app.request('http://localhost/static/not-found.html')
expect(res.status).toBe(404) expect(res.status).toBe(404)
})
req = new Request('http://localhost/static-no-root/plain.txt') it('Should return plan.txt', async () => {
res = await app.dispatch(req) const res = await app.request('http://localhost/static-no-root/plain.txt')
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(await res.text()).toBe('That is plain.txt') expect(await res.text()).toBe('That is plain.txt')
expect(res.headers.get('Content-Type')).toBe('text/plain; charset=utf-8') expect(res.headers.get('Content-Type')).toBe('text/plain; charset=utf-8')
expect(res.headers.get('Content-Length')).toBe('17') expect(res.headers.get('Content-Length')).toBe('17')
}) })
it('Serve index.html', async () => { it('Should return index.html', async () => {
const req = new Request('http://localhost/static/top') const res = await app.request('http://localhost/static/top')
const res = await app.dispatch(req)
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(await res.text()).toBe('<h1>Top</h1>') expect(await res.text()).toBe('<h1>Top</h1>')
expect(res.headers.get('Content-Type')).toBe('text/html; charset=utf-8') expect(res.headers.get('Content-Type')).toBe('text/html; charset=utf-8')