import { Elysia, t } from 'elysia'
import SwaggerParser from '@apidevtools/swagger-parser'
import { swagger } from '../src'
import { describe, expect, it } from 'bun:test'
import { fail } from 'assert'
const req = (path: string) => new Request(`http://localhost${path}`)
describe('Swagger', () => {
it('show Swagger page', async () => {
const app = new Elysia().use(swagger())
await app.modules
const res = await app.handle(req('/swagger'))
expect(res.status).toBe(200)
})
it('returns a valid Swagger/OpenAPI json config', async () => {
const app = new Elysia().use(swagger())
await app.modules
const res = await app.handle(req('/swagger/json')).then((x) => x.json())
expect(res.openapi).toBe('3.0.3')
await SwaggerParser.validate(res).catch((err) => fail(err))
})
it('use custom Swagger version', async () => {
const app = new Elysia().use(
swagger({
provider: 'swagger-ui',
version: '4.5.0'
})
)
await app.modules
const res = await app.handle(req('/swagger')).then((x) => x.text())
expect(
res.includes(
'https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui-bundle.js'
)
).toBe(true)
})
it('follow title and description with Swagger-UI provider', async () => {
const app = new Elysia().use(
swagger({
version: '4.5.0',
provider: 'swagger-ui',
documentation: {
info: {
title: 'Elysia Documentation',
description: 'Herrscher of Human',
version: '1.0.0'
}
}
})
)
await app.modules
const res = await app.handle(req('/swagger')).then((x) => x.text())
expect(res.includes('
Elysia Documentation')).toBe(true)
expect(
res.includes(
``
)
).toBe(true)
})
it('follow title and description with Scalar provider', async () => {
const app = new Elysia().use(
swagger({
version: '4.5.0',
provider: 'scalar',
documentation: {
info: {
title: 'Elysia Documentation',
description: 'Herrscher of Human',
version: '1.0.0'
}
}
})
)
await app.modules
const res = await app.handle(req('/swagger')).then((x) => x.text())
expect(res.includes('Elysia Documentation')).toBe(true)
expect(
res.includes(
``
)
).toBe(true)
})
it('use custom path', async () => {
const app = new Elysia().use(
swagger({
path: '/v2/swagger'
})
)
await app.modules
const res = await app.handle(req('/v2/swagger'))
expect(res.status).toBe(200)
const resJson = await app.handle(req('/v2/swagger/json'))
expect(resJson.status).toBe(200)
})
it('Swagger UI options', async () => {
const app = new Elysia().use(
swagger({
provider: 'swagger-ui',
swaggerOptions: {
persistAuthorization: true
}
})
)
await app.modules
const res = await app.handle(req('/swagger')).then((x) => x.text())
const expected = `"persistAuthorization":true`
expect(res.trim().includes(expected.trim())).toBe(true)
})
it('should not return content response when using Void type', async () => {
const app = new Elysia().use(swagger()).get('/void', () => {}, {
response: {
204: t.Void({
description: 'Void response'
})
}
})
await app.modules
const res = await app.handle(req('/swagger/json'))
expect(res.status).toBe(200)
const response = await res.json()
expect(response.paths['/void'].get.responses['204'].description).toBe(
'Void response'
)
expect(
response.paths['/void'].get.responses['204'].content
).toBeUndefined()
})
it('should not return content response when using Undefined type', async () => {
const app = new Elysia()
.use(swagger())
.get('/undefined', () => undefined, {
response: {
204: t.Undefined({
description: 'Undefined response'
})
}
})
await app.modules
const res = await app.handle(req('/swagger/json'))
expect(res.status).toBe(200)
const response = await res.json()
expect(
response.paths['/undefined'].get.responses['204'].description
).toBe('Undefined response')
expect(
response.paths['/undefined'].get.responses['204'].content
).toBeUndefined()
})
it('should not return content response when using Null type', async () => {
const app = new Elysia().use(swagger()).get('/null', () => null, {
response: {
204: t.Null({
description: 'Null response'
})
}
})
await app.modules
const res = await app.handle(req('/swagger/json'))
expect(res.status).toBe(200)
const response = await res.json()
expect(response.paths['/null'].get.responses['204'].description).toBe(
'Null response'
)
expect(
response.paths['/null'].get.responses['204'].content
).toBeUndefined()
})
it('should set the required field to true when a request body is present', async () => {
const app = new Elysia().use(swagger()).post('/post', () => {}, {
body: t.Object({ name: t.String() })
})
await app.modules
const res = await app.handle(req('/swagger/json'))
expect(res.status).toBe(200)
const response = await res.json()
expect(response.paths['/post'].post.requestBody.required).toBe(true)
})
it('resolve optional param to param', async () => {
const app = new Elysia().use(swagger()).get('/id/:id?', () => {})
await app.modules
const res = await app.handle(req('/swagger/json'))
expect(res.status).toBe(200)
const response = await res.json()
expect(response.paths).toContainKey('/id/{id}')
})
it('should hide routes with hide = true from paths', async () => {
const app = new Elysia().use(swagger())
.get("/public", "omg")
.guard({
detail: {
hide: true
}
})
.get("/hidden", "ok")
await app.modules
const res = await app.handle(req('/swagger/json'))
expect(res.status).toBe(200)
const response = await res.json()
expect(response.paths['/public']).not.toBeUndefined();
expect(response.paths['/hidden']).toBeUndefined();
})
it('should expand .all routes', async () => {
const app = new Elysia().use(swagger())
.all("/all", "woah")
await app.modules
const res = await app.handle(req('/swagger/json'))
expect(res.status).toBe(200)
const response = await res.json()
expect(Object.keys(response.paths['/all'])).toBeArrayOfSize(8)
})
it('should hide routes that are invalid', async () => {
const app = new Elysia().use(swagger())
.get("/valid", "ok")
.route("LOCK", "/invalid", "nope")
await app.modules
const res = await app.handle(req('/swagger/json'))
expect(res.status).toBe(200)
const response = await res.json()
expect(response.paths['/valid']).not.toBeUndefined();
expect(response.paths['/invalid']).toBeUndefined();
})
})