@goa/goa
Version:
The Goa Source Code For Compilation Into @Goa/Koa That Includes Modules, Tests, Types And Dependencies.
910 lines (853 loc) • 26.8 kB
JavaScript
import { throws } from 'assert'
import { deepEqual, assert, equal } from '@zoroaster/assert'
import Context from '../../context'
import Stream, { PassThrough } from 'stream'
import { inspect } from 'util'
import { createReadStream } from 'fs'
import { request } from 'http'
export { Context as context }
/** @type {TestSuite} */
export const append = {
'appends multiple headers'({ ctx }) {
ctx.append('x-foo', 'bar1')
ctx.append('x-foo', 'bar2')
deepEqual(ctx.response.header['x-foo'], ['bar1', 'bar2'])
},
'accepts array of values'({ ctx }) {
ctx.append('Set-Cookie', ['foo=bar', 'fizz=buzz'])
ctx.append('Set-Cookie', 'hi=again')
deepEqual(ctx.response.header['set-cookie'], ['foo=bar', 'fizz=buzz', 'hi=again'])
},
'gets reset by res.set(field, val)'({ ctx }) {
ctx.append('Link', '<http://localhost/>')
ctx.append('Link', '<http://localhost:80/>')
ctx.set('Link', '<http://127.0.0.1/>')
equal(ctx.response.header.link, '<http://127.0.0.1/>')
},
'works with res.set(field, val) first'({ ctx }) {
ctx.set('Link', '<http://localhost/>')
ctx.append('Link', '<http://localhost:80/>')
deepEqual(ctx.response.header.link, ['<http://localhost/>', '<http://localhost:80/>'])
},
}
/** @type {TestSuite} */
export const attachment = {
'when given a filename': {
'sets the filename param'({ ctx }) {
ctx.attachment('path/to/tobi.png')
const str = 'attachment; filename="tobi.png"'
equal(ctx.response.header['content-disposition'], str)
},
},
'when omitting filename': {
'does not set filename param'({ ctx }) {
ctx.attachment()
equal(ctx.response.header['content-disposition'], 'attachment')
},
},
'when given a no-ascii filename': {
'sets the encodeURI filename param'({ ctx }) {
ctx.attachment('path/to/include-no-ascii-char-中文名-ok.png')
const str = 'attachment; filename="include-no-ascii-char-???-ok.png"; filename*=UTF-8\'\'include-no-ascii-char-%E4%B8%AD%E6%96%87%E5%90%8D-ok.png'
equal(ctx.response.header['content-disposition'], str)
},
async 'works with http client'({ app, startApp }) {
app.use((ctx) => {
ctx.attachment('path/to/include-no-ascii-char-中文名-ok.json')
ctx.body = { foo: 'bar' }
})
await startApp()
.get('/')
.assert('content-disposition', 'attachment; filename="include-no-ascii-char-???-ok.json"; filename*=UTF-8\'\'include-no-ascii-char-%E4%B8%AD%E6%96%87%E5%90%8D-ok.json')
.assert(200, { foo: 'bar' })
},
},
}
/** @type {TestSuite} */
export const contentDisposition = {
'with "fallback" option': {
'requires a string or Boolean'({ ctx }) {
throws(() => { ctx.attachment('plans.pdf', { fallback: 42 }) },
/fallback.*string/)
},
'defaults to true'({ ctx }) {
ctx.attachment('€ rates.pdf')
equal(ctx.response.header['content-disposition'],
'attachment; filename="? rates.pdf"; filename*=UTF-8\'\'%E2%82%AC%20rates.pdf')
},
'when "false"': {
'does not generate ISO-8859-1 fallback'({ ctx }) {
ctx.attachment('£ and € rates.pdf', { fallback: false })
equal(ctx.response.header['content-disposition'],
'attachment; filename*=UTF-8\'\'%C2%A3%20and%20%E2%82%AC%20rates.pdf')
},
'keeps ISO-8859-1 filename'({ ctx }) {
ctx.attachment('£ rates.pdf', { fallback: false })
equal(ctx.response.header['content-disposition'],
'attachment; filename="£ rates.pdf"')
},
},
'when "true"': {
'generates ISO-8859-1 fallback'({ ctx }) {
ctx.attachment('£ and € rates.pdf', { fallback: true })
equal(ctx.response.header['content-disposition'],
'attachment; filename="£ and ? rates.pdf"; filename*=UTF-8\'\'%C2%A3%20and%20%E2%82%AC%20rates.pdf')
},
'passes through ISO-8859-1 filename'({ ctx }) {
ctx.attachment('£ rates.pdf', { fallback: true })
equal(ctx.response.header['content-disposition'],
'attachment; filename="£ rates.pdf"')
},
},
'when a string': {
'requires an ISO-8859-1 string'({ ctx }) {
throws(() => { ctx.attachment('€ rates.pdf', { fallback: '€ rates.pdf' }) },
/fallback.*iso-8859-1/i)
},
'uses as ISO-8859-1 fallback'({ ctx }) {
ctx.attachment('£ and € rates.pdf', { fallback: '£ and EURO rates.pdf' })
equal(ctx.response.header['content-disposition'],
'attachment; filename="£ and EURO rates.pdf"; filename*=UTF-8\'\'%C2%A3%20and%20%E2%82%AC%20rates.pdf')
},
'uses as fallback even when filename is ISO-8859-1'({ ctx }) {
ctx.attachment('"£ rates".pdf', { fallback: '£ rates.pdf' })
equal(ctx.response.header['content-disposition'],
'attachment; filename="£ rates.pdf"; filename*=UTF-8\'\'%22%C2%A3%20rates%22.pdf')
},
'does nothing if equal to filename'({ ctx }) {
ctx.attachment('plans.pdf', { fallback: 'plans.pdf' })
equal(ctx.response.header['content-disposition'],
'attachment; filename="plans.pdf"')
},
'uses the basename of the string'({ ctx }) {
ctx.attachment('€ rates.pdf', { fallback: '/path/to/EURO rates.pdf' })
equal(ctx.response.header['content-disposition'],
'attachment; filename="EURO rates.pdf"; filename*=UTF-8\'\'%E2%82%AC%20rates.pdf')
},
'does nothing without filename option'({ ctx }) {
ctx.attachment(undefined, { fallback: 'plans.pdf' })
equal(ctx.response.header['content-disposition'],
'attachment')
},
},
},
'with "type" option': {
'defaults to attachment'({ ctx }) {
ctx.attachment()
equal(ctx.response.header['content-disposition'],
'attachment')
},
'requires a string'({ ctx }) {
throws(() => { ctx.attachment(undefined, { type: 42 }) },
/invalid type/)
},
'requires a valid type'({ ctx }) {
throws(() => { ctx.attachment(undefined, { type: 'invlaid;type' }) },
/invalid type/)
},
'creates a header with inline type'({ ctx }) {
ctx.attachment(undefined, { type: 'inline' })
equal(ctx.response.header['content-disposition'],
'inline')
},
'creates a header with inline type & filename'({ ctx }) {
ctx.attachment('plans.pdf', { type: 'inline' })
equal(ctx.response.header['content-disposition'],
'inline; filename="plans.pdf"')
},
'normalizes type'({ ctx }) {
ctx.attachment(undefined, { type: 'INLINE' })
equal(ctx.response.header['content-disposition'],
'inline')
},
},
}
/** @type {TestSuite} */
export const body = {
'when Content-Type is set': {
'does not override'({ res }) {
res.type = 'png'
res.body = Buffer.from('something')
equal('image/png', res.header['content-type'])
},
'when body is an object': {
'overrides as json'({ res }) {
res.body = '<em>hey</em>'
equal('text/html; charset=utf-8', res.header['content-type'])
res.body = { foo: 'bar' }
equal('application/json; charset=utf-8', res.header['content-type'])
},
},
'overrides length'({ res }) {
res.type = 'html'
res.body = 'something'
equal(res.length, 9)
},
},
'when a string is given': {
'defaults to text'({ res }) {
res.body = 'Tobi'
equal('text/plain; charset=utf-8', res.header['content-type'])
},
'sets length'({ res }) {
res.body = 'Tobi'
equal('4', res.header['content-length'])
},
'and contains a non-leading <': {
'defaults to text'({ res }) {
res.body = 'aklsdjf < klajsdlfjasd'
equal('text/plain; charset=utf-8', res.header['content-type'])
},
},
},
'when an html string is given': {
'defaults to html'({ res }) {
res.body = '<h1>Tobi</h1>'
equal('text/html; charset=utf-8', res.header['content-type'])
},
'sets length'({ res }) {
const string = '<h1>Tobi</h1>'
res.body = string
equal(res.length, Buffer.byteLength(string))
},
'sets length when body is overridden'({ res }) {
const string = '<h1>Tobi</h1>'
res.body = string
res.body = string + string
equal(res.length, 2 * Buffer.byteLength(string))
},
'when it contains leading whitespace': {
'defaults to html'({ res }) {
res.body = ' <h1>Tobi</h1>'
equal('text/html; charset=utf-8', res.header['content-type'])
},
},
},
'when an xml string is given': {
'defaults to html'({ res }) {
/**
* ctx test is to show that we're not going
* to be stricter with the html sniff
* or that we will sniff other string types.
* You should `.type=` if ctx simple test fails.
*/
res.body = '<?xml version="1.0" encoding="UTF-8"?>\n<俄语>данные</俄语>'
equal('text/html; charset=utf-8', res.header['content-type'])
},
},
'when a stream is given': {
'defaults to an octet stream'({ res }) {
res.body = createReadStream('LICENSE')
equal('application/octet-stream', res.header['content-type'])
},
},
'when a buffer is given': {
'defaults to an octet stream'({ res }) {
res.body = Buffer.from('hey')
equal('application/octet-stream', res.header['content-type'])
},
'sets length'({ res }) {
res.body = Buffer.from('Tobi')
equal('4', res.header['content-length'])
},
},
'when an object is given': {
'defaults to json'({ res }) {
res.body = { foo: 'bar' }
equal('application/json; charset=utf-8', res.header['content-type'])
},
},
}
/** @type {TestSuite} */
export const etag = {
'should not modify an etag with quotes'({ res }) {
res.etag = '"asdf"'
equal(res.header.etag, '"asdf"')
},
'should not modify a weak etag'({ res }) {
res.etag = 'W/"asdf"'
equal(res.header.etag, 'W/"asdf"')
},
'should add quotes around an etag if necessary'({ res }) {
res.etag = 'asdf'
equal(res.header.etag, '"asdf"')
},
'returns etag'({ res }) {
res.etag = '"asdf"'
equal(res.etag, '"asdf"')
},
}
/** @type {TestSuite} */
export const flushHeaders = {
async 'sets headersSent'({ app, startApp }) {
app.use((ctx) => {
ctx.body = 'Body'
ctx.status = 200
ctx.flushHeaders()
equal(ctx.res.headersSent, true)
})
await startApp()
.get('/')
.assert(200, 'Body')
},
async 'allows a response afterwards'({ app, startApp }) {
app.use((ctx) => {
ctx.status = 200
ctx.res.setHeader('Content-Type', 'text/plain')
ctx.flushHeaders()
ctx.body = 'Body'
})
await startApp()
.get('/')
.assert(200, 'Body')
.assert('Content-Type', 'text/plain')
},
async 'should send the correct status code'({ app, startApp }) {
app.use((ctx) => {
ctx.status = 401
ctx.res.setHeader('Content-Type', 'text/plain')
ctx.flushHeaders()
ctx.body = 'Body'
})
await startApp()
.get('/')
.assert(401, 'Body')
.assert('Content-Type', 'text/plain')
},
async 'should ignore set header after flushHeaders'({ app, startApp }) {
app.use((ctx) => {
ctx.status = 401
ctx.res.setHeader('Content-Type', 'text/plain')
ctx.flushHeaders()
ctx.body = 'foo'
ctx.set('X-Shouldnt-Work', 'Value')
ctx.remove('Content-Type')
ctx.vary('Content-Type')
})
await startApp()
.get('/')
.assert(401)
.assert('Content-Type', 'text/plain')
.assert('x-shouldnt-work', null)
.assert('vary', null)
},
async 'flushes headers first and delay to send data'({ app }) {
let t, s
app.use(ctx => {
ctx.type = 'json'
ctx.status = 200
ctx.headers['Link'] = '</css/mycss.css>; as=style; rel=preload, <https://img.craftflair.com>; rel=preconnect; crossorigin'
s = ctx.body = new PassThrough()
ctx.flushHeaders()
t = setTimeout(() => {
s.end(JSON.stringify({ message: 'hello!' }))
}, 100)
})
await new Promise((r, j) => {
app.listen(function () {
const port = this.address().port
request({
port,
})
.on('response', res => {
const onData = () => j(new Error('boom'))
res.on('data', onData)
// shouldn't receive any data for a while
setTimeout(() => {
res.removeListener('data', onData)
this.close()
clearTimeout(t)
s.end()
r()
}, 50)
})
.on('error', j)
.end()
})
})
},
async 'catches stream error'({ app, expectError, startApp }) {
const p = expectError({ message: 'mock error' })
app.use(ctx => {
ctx.type = 'json'
ctx.status = 200
ctx.headers['Link'] = '</css/mycss.css>; as=style; rel=preload, <https://img.craftflair.com>; rel=preconnect; crossorigin'
ctx.length = 20
ctx.flushHeaders()
const stream = ctx.body = new PassThrough()
setTimeout(() => {
stream.emit('error', new Error('mock error'))
stream.end()
}, 10)
})
await startApp()
.get('/')
await p
},
}
/** @type {TestSuite} */
export const header = {
'returns the response header object'({ res }) {
res.set('X-Foo', 'bar')
res.set('X-Number', 200)
deepEqual(res.header, { 'x-foo': 'bar', 'x-number': '200' })
},
'uses res.getHeaders() accessor when available'({ res }) {
res.res.getHeaders = () => ({ 'x-foo': 'baz' })
deepEqual(res.header, { 'x-foo': 'baz' })
},
async 'returns the response header object when no mocks are in use'({ app, startApp }) {
let h
app.use(ctx => {
ctx.set('x-foo', '42')
h = Object.assign({}, ctx.response.header)
})
await startApp()
.get('/')
deepEqual(h, { 'x-foo': '42' })
},
// 'when res._headers not present': {
// '!returns empty object'({ res }) {
// res.res._headers = null
// deepEqual(res.header, {})
// },
// },
}
/** @type {TestSuite} */
export const headers = {
'returns the response header object'({ res }) {
res.set('X-Foo', 'bar')
deepEqual(res.headers, { 'x-foo': 'bar' })
},
// 'when res._headers not present': {
// 'returns empty object'({ res }) {
// res.res._headers = null
// deepEqual(res.headers, {})
// },
// },
}
/** @type {TestSuite} */
export const Inspect = {
'with no response.res present': {
'returns null'({ res }) {
res.body = 'hello'
delete res.res
equal(res.inspect(), null)
equal(inspect(res), 'undefined')
},
},
'returns a json representation'({ res }) {
res.body = 'hello'
const expected = {
status: 200,
message: 'OK',
header: {
'content-type': 'text/plain; charset=utf-8',
'content-length': '5',
},
body: 'hello',
}
deepEqual(res.inspect(), expected)
deepEqual(inspect(res), inspect(expected))
},
}
/** @type {TestSuite} */
export const is = {
'ignores params'({ res }) {
res.type = 'text/html; charset=utf-8'
equal(res.is('text/*'), 'text/html')
},
'when no type is set': {
'returns false'({ res }) {
equal(res.is(), false)
equal(res.is('html'), false)
},
},
'when given no types': {
'returns the type'({ res }) {
res.type = 'text/html; charset=utf-8'
equal(res.is(), 'text/html')
},
},
'given one type': {
'returns the type or false'({ res }) {
res.type = 'image/png'
equal(res.is('png'), 'png')
equal(res.is('.png'), '.png')
equal(res.is('image/png'), 'image/png')
equal(res.is('image/*'), 'image/png')
equal(res.is('*/png'), 'image/png')
equal(res.is('jpeg'), false)
equal(res.is('.jpeg'), false)
equal(res.is('image/jpeg'), false)
equal(res.is('text/*'), false)
equal(res.is('*/jpeg'), false)
},
},
'given multiple types': {
'returns the first match or false'({ res }) {
res.type = 'image/png'
equal(res.is('png'), 'png')
equal(res.is('.png'), '.png')
equal(res.is('text/*', 'image/*'), 'image/png')
equal(res.is('image/*', 'text/*'), 'image/png')
equal(res.is('image/*', 'image/png'), 'image/png')
equal(res.is('image/png', 'image/*'), 'image/png')
equal(res.is(['text/*', 'image/*']), 'image/png')
equal(res.is(['image/*', 'text/*']), 'image/png')
equal(res.is(['image/*', 'image/png']), 'image/png')
equal(res.is(['image/png', 'image/*']), 'image/png')
equal(res.is('jpeg'), false)
equal(res.is('.jpeg'), false)
equal(res.is('text/*', 'application/*'), false)
equal(res.is('text/html', 'text/plain', 'application/json; charset=utf-8'), false)
},
},
'when Content-Type: application/x-www-form-urlencoded': {
'should match "urlencoded"'({ res }) {
res.type = 'application/x-www-form-urlencoded'
equal(res.is('urlencoded'), 'urlencoded')
equal(res.is('json', 'urlencoded'), 'urlencoded')
equal(res.is('urlencoded', 'json'), 'urlencoded')
},
},
}
/** @type {TestSuite} */
export const lastModified = {
'sets the header as a UTCString'({ res }) {
const date = new Date()
res.lastModified = date
equal(res.header['last-modified'], date.toUTCString())
},
'works with date strings'({ res }) {
const date = new Date()
res.lastModified = date.toString()
equal(res.header['last-modified'], date.toUTCString())
},
'gets the header as a Date'({ res }) {
// Note: Date() removes milliseconds, but it's practically important.
const date = new Date()
res.lastModified = date
equal((res.lastModified.getTime() / 1000), Math.floor(date.getTime() / 1000))
},
'when lastModified not set': {
'gets undefined'({ res }) {
equal(res.lastModified, undefined)
},
},
}
/** @type {TestSuite} */
export const length = {
'when Content-Length is defined': {
'returns a number'({ res }) {
res.header['content-length'] = '120'
equal(res.length, 120)
},
},
'when content-length is defined': {
'returns a number'({ res }) {
res.set('Content-Length', '1024')
equal(res.length, 1024)
},
},
'when Content-Length is not defined': {
'and a .body is set': {
'returns a number'({ res }) {
res.body = 'foo'
res.remove('Content-Length')
equal(res.length, 3)
res.body = 'foo'
equal(res.length, 3)
res.body = Buffer.from('foo bar')
res.remove('Content-Length')
equal(res.length, 7)
res.body = Buffer.from('foo bar')
equal(res.length, 7)
res.body = { hello: 'world' }
res.remove('Content-Length')
equal(res.length, 17)
res.body = { hello: 'world' }
equal(res.length, 17)
res.body = createReadStream('package.json')
equal(res.length, undefined)
res.body = null
equal(res.length, undefined)
},
},
'and .body is not': {
'returns undefined'({ res }) {
equal(res.length, undefined)
},
},
},
}
/** @type {TestSuite} */
export const message = {
'returns the response status message'({ res }) {
res.status = 200
equal(res.message, 'OK')
},
'when res.message not present': {
'should look up in statuses'({ res }) {
res.res.statusCode = 200
equal(res.message, 'OK')
},
},
'=': {
'sets response status message'({ res }) {
res.status = 200
res.message = 'ok'
equal(res.res.statusMessage, 'ok')
equal(res.inspect().message, 'ok')
},
},
}
/** @type {TestSuite} */
export const redirect = {
'should redirect to the given url'({ ctx }) {
ctx.redirect('http://google.com')
equal(ctx.response.header.location, 'http://google.com')
equal(ctx.status, 302)
},
'with "back"': {
'should redirect to Referrer'({ ctx }) {
ctx.req.headers.referrer = '/login'
ctx.redirect('back')
equal(ctx.response.header.location, '/login')
},
'should redirect to Referer'({ ctx }) {
ctx.req.headers.referer = '/login'
ctx.redirect('back')
equal(ctx.response.header.location, '/login')
},
'defaults to alt'({ ctx }) {
ctx.redirect('back', '/index.html')
equal(ctx.response.header.location, '/index.html')
},
'defaults redirect to /'({ ctx }) {
ctx.redirect('back')
equal(ctx.response.header.location, '/')
},
},
'when html is accepted': {
'responds with html'({ ctx }) {
const url = 'http://google.com'
ctx.header.accept = 'text/html'
ctx.redirect(url)
equal(ctx.response.header['content-type'], 'text/html; charset=utf-8')
equal(ctx.body, `Redirecting to <a href="${url}">${url}</a>.`)
},
'escapes the url'({ ctx, escape }) {
let url = '<script>'
ctx.header.accept = 'text/html'
ctx.redirect(url)
url = escape(url)
equal(ctx.response.header['content-type'], 'text/html; charset=utf-8')
equal(ctx.body, `Redirecting to <a href="${url}">${url}</a>.`)
},
},
'when text is accepted': {
'responds with text'({ ctx }) {
const url = 'http://google.com'
ctx.header.accept = 'text/plain'
ctx.redirect(url)
equal(ctx.body, `Redirecting to ${url}.`)
},
},
'when status is 301': {
'does not change the status code'({ ctx }) {
const url = 'http://google.com'
ctx.status = 301
ctx.header.accept = 'text/plain'
ctx.redirect('http://google.com')
equal(ctx.status, 301)
equal(ctx.body, `Redirecting to ${url}.`)
},
},
'when status is 304': {
'changes the status code'({ ctx }) {
const url = 'http://google.com'
ctx.status = 304
ctx.header.accept = 'text/plain'
ctx.redirect('http://google.com')
equal(ctx.status, 302)
equal(ctx.body, `Redirecting to ${url}.`)
},
},
'when content-type was present': {
'overwrites content-type'({ ctx }) {
ctx.body = {}
const url = 'http://google.com'
ctx.header.accept = 'text/plain'
ctx.redirect('http://google.com')
equal(ctx.status, 302)
equal(ctx.body, `Redirecting to ${url}.`)
equal(ctx.type, 'text/plain')
},
},
}
/** @type {TestSuite} */
export const remove = {
'removes a field'({ ctx }) {
ctx.set('x-foo', 'bar')
ctx.remove('x-foo')
deepEqual(ctx.response.header, {})
},
}
/** @type {TestSuite} */
export const set = {
'sets a field value'({ ctx }) {
ctx.set('x-foo', 'bar')
equal(ctx.response.header['x-foo'], 'bar')
},
'coerces number to string'({ ctx }) {
ctx.set('x-foo', 5)
equal(ctx.response.header['x-foo'], '5')
},
'coerces undefined to string'({ ctx }) {
ctx.set('x-foo', undefined)
equal(ctx.response.header['x-foo'], 'undefined')
},
'sets a field value of array'({ ctx }) {
ctx.set('x-foo', ['foo', 'bar'])
deepEqual(ctx.response.header['x-foo'], [ 'foo', 'bar' ])
},
'sets multiple fields'({ ctx }) {
ctx.set({
foo: '1',
bar: '2',
})
equal(ctx.response.header.foo, '1')
equal(ctx.response.header.bar, '2')
},
}
/** @type {TestSuite} */
export const socket = {
'returns the request socket object'({ res }) {
equal(res.socket instanceof Stream, true)
},
}
/** @type {TestSuite} */
export const status = {
'when a status code': {
'and valid': {
'sets the status'({ res }) {
res.status = 403
equal(res.status, 403)
},
'should not throw'({ Response }) {
Response().status = 403
},
},
'and invalid': {
'should throw'({ Response }) {
throws(() => {
Response().status = 99
}, /invalid status code: 99/)
},
},
'and custom status': {
'sets the status'({ res }) {
res.status = 700
equal(res.status, 700)
},
'should not throw'({ Response }) {
Response().status = 700
},
},
'and HTTP/2': {
'does not set the status message'({ Response }) {
const res = Response({
'httpVersionMajor': 2,
'httpVersion': '2.0',
})
res.status = 200
assert(!res.res.statusMessage)
},
},
},
'when a status string': {
'should throw'({ Response }) {
throws(() => Response().status = 'forbidden', /status code must be a number/)
},
},
}
/** @type {TestSuite} */
export const type = {
'=': {
'with a mime': {
'sets the Content-Type'({ ctx }) {
ctx.type = 'text/plain'
equal(ctx.type, 'text/plain')
equal(ctx.response.header['content-type'], 'text/plain; charset=utf-8')
},
},
'with an extension': {
'should lookup the mime'({ ctx }) {
ctx.type = 'json'
equal(ctx.type, 'application/json')
equal(ctx.response.header['content-type'], 'application/json; charset=utf-8')
},
},
'without a charset': {
'defaults the charset'({ ctx }) {
ctx.type = 'text/html'
equal(ctx.type, 'text/html')
equal(ctx.response.header['content-type'], 'text/html; charset=utf-8')
},
},
'with a charset': {
'should not default the charset'({ ctx }) {
ctx.type = 'text/html; charset=foo'
equal(ctx.type, 'text/html')
equal(ctx.response.header['content-type'], 'text/html; charset=foo')
},
},
'with an unknown extension': {
'does not set a content-type'({ ctx }) {
ctx.type = 'asdf'
assert(!ctx.type)
assert(!ctx.response.header['content-type'])
},
},
},
'with no Content-Type': {
'returns ""'({ ctx }) {
assert(!ctx.type)
},
},
'with a Content-Type': {
'returns the mime'({ ctx }) {
ctx.type = 'json'
equal(ctx.type, 'application/json')
},
},
}
/** @type {TestSuite} */
export const vary = {
'when Vary is not set': {
'sets it'({ ctx }) {
ctx.vary('Accept')
equal(ctx.response.header.vary, 'Accept')
},
},
'when Vary is set': {
'appends'({ ctx }) {
ctx.vary('Accept')
ctx.vary('Accept-Encoding')
equal(ctx.response.header.vary, 'Accept, Accept-Encoding')
},
},
'when Vary already contains the value': {
'should not append'({ ctx }) {
ctx.vary('Accept')
ctx.vary('Accept-Encoding')
ctx.vary('Accept')
ctx.vary('Accept-Encoding')
equal(ctx.response.header.vary, 'Accept, Accept-Encoding')
},
},
}
/**
* @typedef {import('../../context').TestSuite} TestSuite
*/