2023-12-16 09:28:19 +08:00
import { Hono } from 'hono'
2024-03-17 08:08:43 +08:00
2025-03-28 17:50:19 +08:00
import { reactRenderer , useRequestContext } from '.'
2023-12-16 09:28:19 +08:00
const RequestUrl = ( ) = > {
const c = useRequestContext ( )
return < > { c . req . url } < / >
}
describe ( 'Basic' , ( ) = > {
const app = new Hono ( )
app . use (
// @ts-expect-error - `title` is not defined
reactRenderer ( ( { children , title } ) = > {
return (
< html >
< head > { title } < / head >
< body > { children } < / body >
< / html >
)
} )
)
app . get ( '/' , ( c ) = > {
return c . render (
< h1 >
< RequestUrl / >
< / h1 > ,
{
title : 'Title' ,
}
)
} )
it ( 'Should return HTML with layout' , async ( ) = > {
const res = await app . request ( 'http://localhost/' )
expect ( res ) . not . toBeNull ( )
expect ( res . status ) . toBe ( 200 )
expect ( await res . text ( ) ) . toBe (
2025-02-17 09:40:35 +08:00
'<!DOCTYPE html><html><head>Title</head><body><h1>http://localhost/</h1></body></html>'
2023-12-16 09:28:19 +08:00
)
} )
it ( 'Should return HTML without layout' , async ( ) = > {
const app = new Hono ( )
app . use ( '*' , reactRenderer ( ) )
app . get ( '/' , ( c ) = >
c . render (
< h1 >
< RequestUrl / >
< / h1 > ,
{ title : 'Title' }
)
)
const res = await app . request ( 'http://localhost/' )
expect ( res ) . not . toBeNull ( )
expect ( res . status ) . toBe ( 200 )
2025-02-17 09:40:35 +08:00
expect ( await res . text ( ) ) . toBe ( '<!DOCTYPE html><h1>http://localhost/</h1>' )
2023-12-16 09:28:19 +08:00
} )
2024-04-16 19:58:39 +08:00
it ( 'nested layout with Layout' , async ( ) = > {
const app = new Hono ( )
app . use (
'*' ,
// @ts-expect-error - `title` is not defined
reactRenderer ( ( { children , title , Layout } ) = > {
return (
< Layout >
< html >
< head > { title } < / head >
< body > { children } < / body >
< / html >
< / Layout >
)
} )
)
const app2 = new Hono ( )
app2 . use (
'*' ,
// @ts-expect-error - `title` is not defined
reactRenderer ( ( { children , Layout , title } ) = > {
return (
< Layout title = { title } >
< div className = 'nested' > { children } < / div >
< / Layout >
)
} )
)
app2 . get ( '/' , ( c ) = > c . render ( < h1 > http : //localhost/nested</h1>, { title: 'Nested' }))
const app3 = new Hono ( )
app3 . use (
'*' ,
// @ts-expect-error - `title` is not defined
reactRenderer ( ( { children , Layout , title } ) = > {
return (
< Layout title = { title } >
< div className = 'nested2' > { children } < / div >
< / Layout >
)
} )
)
app3 . get ( '/' , ( c ) = > c . render ( < h1 > http : //localhost/nested</h1>, { title: 'Nested2' }))
app2 . route ( '/nested2' , app3 )
app . route ( '/nested' , app2 )
let res = await app . request ( 'http://localhost/nested' )
expect ( res ) . not . toBeNull ( )
expect ( res . status ) . toBe ( 200 )
expect ( await res . text ( ) ) . toBe (
2025-02-17 09:40:35 +08:00
'<!DOCTYPE html><html><head>Nested</head><body><div class="nested"><h1>http://localhost/nested</h1></div></body></html>'
2024-04-16 19:58:39 +08:00
)
res = await app . request ( 'http://localhost/nested/nested2' )
expect ( res ) . not . toBeNull ( )
expect ( res . status ) . toBe ( 200 )
expect ( await res . text ( ) ) . toBe (
2025-02-17 09:40:35 +08:00
'<!DOCTYPE html><html><head>Nested2</head><body><div class="nested"><div class="nested2"><h1>http://localhost/nested</h1></div></div></body></html>'
2024-04-16 19:58:39 +08:00
)
} )
2023-12-16 09:28:19 +08:00
it ( 'Should return a default doctype' , async ( ) = > {
const app = new Hono ( )
app . use (
'*' ,
reactRenderer (
( { children } ) = > {
return (
< html >
2025-04-11 06:43:03 +08:00
< head > < / head >
2023-12-16 09:28:19 +08:00
< body > { children } < / body >
< / html >
)
} ,
{ docType : true }
)
)
app . get ( '/' , ( c ) = > c . render ( < h1 > Hello < / h1 > , { title : 'Title' } ) )
const res = await app . request ( '/' )
expect ( res ) . not . toBeNull ( )
expect ( res . status ) . toBe ( 200 )
2025-04-11 06:43:03 +08:00
expect ( await res . text ( ) ) . toBe (
'<!DOCTYPE html><html><head></head><body><h1>Hello</h1></body></html>'
)
2023-12-16 09:28:19 +08:00
} )
2025-02-17 09:40:35 +08:00
it ( 'Should return a content without a doctype' , async ( ) = > {
const app = new Hono ( )
app . use (
'*' ,
reactRenderer (
( { children } ) = > {
return (
< html >
< body > { children } < / body >
< / html >
)
} ,
{ docType : false }
)
)
app . get ( '/' , ( c ) = > c . render ( < h1 > Hello < / h1 > , { title : 'Title' } ) )
const res = await app . request ( '/' )
expect ( res ) . not . toBeNull ( )
expect ( res . status ) . toBe ( 200 )
2025-04-11 06:43:03 +08:00
expect ( await res . text ( ) ) . toBe ( '<html><head></head><body><h1>Hello</h1></body></html>' )
2025-02-17 09:40:35 +08:00
} )
2023-12-16 09:28:19 +08:00
it ( 'Should return a custom doctype' , async ( ) = > {
const app = new Hono ( )
app . use (
'*' ,
reactRenderer (
( { children } ) = > {
return (
< html >
< body > { children } < / body >
< / html >
)
} ,
{
docType :
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' ,
}
)
)
app . get ( '/' , ( c ) = > c . render ( < h1 > Hello < / h1 > , { title : 'Title' } ) )
const res = await app . request ( '/' )
expect ( res ) . not . toBeNull ( )
expect ( res . status ) . toBe ( 200 )
expect ( await res . text ( ) ) . toBe (
2025-04-11 06:43:03 +08:00
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html><head></head><body><h1>Hello</h1></body></html>'
2023-12-16 09:28:19 +08:00
)
} )
2024-03-17 09:40:58 +08:00
it ( 'Should return as streaming content with headers added in a handler' , async ( ) = > {
const app = new Hono ( )
app . use ( reactRenderer ( ( { children } ) = > < > { children } < / > , { stream : true } ) )
app . get ( '/' , ( c ) = > {
c . header ( 'X-Message-Set' , 'Hello' )
c . header ( 'X-Message-Append' , 'Hello' , { append : true } )
return c . render ( < h1 > Hi < / h1 > , { title : 'Hi' } )
} )
const res = await app . request ( '/' )
expect ( res . status ) . toBe ( 200 )
expect ( res . headers . get ( 'Transfer-Encoding' ) ) . toBe ( 'chunked' )
expect ( res . headers . get ( 'Content-Type' ) ) . toBe ( 'text/html; charset=UTF-8' )
expect ( res . headers . get ( 'X-Message-Set' ) ) . toBe ( 'Hello' )
expect ( res . headers . get ( 'X-Message-Append' ) ) . toBe ( 'Hello' )
expect ( await res . text ( ) ) . toBe ( '<h1>Hi</h1>' )
} )
2023-12-16 09:28:19 +08:00
} )
describe ( 'Streaming' , ( ) = > {
2024-03-17 08:08:43 +08:00
it ( 'Should return a stream response' , async ( ) = > {
const app = new Hono ( )
app . use (
'*' ,
reactRenderer (
( { children } ) = > {
return (
< html >
< body > { children } < / body >
< / html >
)
} ,
{ stream : true }
)
)
app . get ( '/' , ( c ) = > c . render ( < h1 > Hello < / h1 > , { title : 'Title' } ) )
const res = await app . request ( '/' )
expect ( res ) . not . toBeNull ( )
expect ( res . status ) . toBe ( 200 )
expect ( res . headers . get ( 'Transfer-Encoding' ) ) . toBe ( 'chunked' )
expect ( res . headers . get ( 'Content-Type' ) ) . toBe ( 'text/html; charset=UTF-8' )
2025-04-11 06:43:03 +08:00
expect ( await res . text ( ) ) . toBe (
'<!DOCTYPE html><html><head></head><body><h1>Hello</h1></body></html>'
)
2024-03-17 08:08:43 +08:00
} )
2023-12-16 09:28:19 +08:00
} )