mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-11 08:33:53 +00:00
test: unit tests for server module (#5154)
This commit is contained in:
parent
e3991aacdf
commit
cc573a4925
@ -19,7 +19,8 @@ module.exports = {
|
|||||||
|
|
||||||
coveragePathIgnorePatterns: [
|
coveragePathIgnorePatterns: [
|
||||||
'node_modules/(?!(@nuxt|nuxt))',
|
'node_modules/(?!(@nuxt|nuxt))',
|
||||||
'packages/webpack/src/config/plugins/vue'
|
'packages/webpack/src/config/plugins/vue',
|
||||||
|
'packages/server/src/jsdom'
|
||||||
],
|
],
|
||||||
|
|
||||||
testPathIgnorePatterns: [
|
testPathIgnorePatterns: [
|
||||||
|
@ -11,6 +11,10 @@ describe('build', () => {
|
|||||||
jest.spyOn(utils, 'createLock').mockImplementation(() => () => {})
|
jest.spyOn(utils, 'createLock').mockImplementation(() => () => {})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
process.exit.mockRestore()
|
||||||
|
})
|
||||||
|
|
||||||
afterEach(() => jest.resetAllMocks())
|
afterEach(() => jest.resetAllMocks())
|
||||||
|
|
||||||
test('has run function', () => {
|
test('has run function', () => {
|
||||||
|
@ -11,6 +11,10 @@ describe('generate', () => {
|
|||||||
jest.spyOn(utils, 'createLock').mockImplementation(() => () => {})
|
jest.spyOn(utils, 'createLock').mockImplementation(() => () => {})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
process.exit.mockRestore()
|
||||||
|
})
|
||||||
|
|
||||||
afterEach(() => jest.resetAllMocks())
|
afterEach(() => jest.resetAllMocks())
|
||||||
|
|
||||||
test('has run function', () => {
|
test('has run function', () => {
|
||||||
|
@ -53,7 +53,6 @@ export default async function renderAndGetWindow(
|
|||||||
ssr ? `window.${globals.context}` : `<div id="${globals.id}">`
|
ssr ? `window.${globals.context}` : `<div id="${globals.id}">`
|
||||||
)
|
)
|
||||||
|
|
||||||
/* istanbul ignore if */
|
|
||||||
if (!nuxtExists) {
|
if (!nuxtExists) {
|
||||||
const error = new Error('Could not load the nuxt app')
|
const error = new Error('Could not load the nuxt app')
|
||||||
error.body = window.document.body.innerHTML
|
error.body = window.document.body.innerHTML
|
||||||
|
@ -12,15 +12,6 @@ export default ({ resources, options }) => async function errorMiddleware(err, r
|
|||||||
message: err.message || 'Nuxt Server Error',
|
message: err.message || 'Nuxt Server Error',
|
||||||
name: !err.name || err.name === 'Error' ? 'NuxtServerError' : err.name
|
name: !err.name || err.name === 'Error' ? 'NuxtServerError' : err.name
|
||||||
}
|
}
|
||||||
const errorFull = err instanceof Error
|
|
||||||
? err
|
|
||||||
: typeof err === 'string'
|
|
||||||
? new Error(err)
|
|
||||||
: new Error(err.message || JSON.stringify(err))
|
|
||||||
|
|
||||||
errorFull.name = error.name
|
|
||||||
errorFull.statusCode = error.statusCode
|
|
||||||
errorFull.stack = err.stack || undefined
|
|
||||||
|
|
||||||
const sendResponse = (content, type = 'text/html') => {
|
const sendResponse = (content, type = 'text/html') => {
|
||||||
// Set Headers
|
// Set Headers
|
||||||
@ -62,6 +53,16 @@ export default ({ resources, options }) => async function errorMiddleware(err, r
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const errorFull = err instanceof Error
|
||||||
|
? err
|
||||||
|
: typeof err === 'string'
|
||||||
|
? new Error(err)
|
||||||
|
: new Error(err.message || JSON.stringify(err))
|
||||||
|
|
||||||
|
errorFull.name = error.name
|
||||||
|
errorFull.statusCode = error.statusCode
|
||||||
|
errorFull.stack = err.stack || undefined
|
||||||
|
|
||||||
// Show stack trace
|
// Show stack trace
|
||||||
const youch = new Youch(
|
const youch = new Youch(
|
||||||
errorFull,
|
errorFull,
|
||||||
@ -90,7 +91,6 @@ const readSourceFactory = ({ srcDir, rootDir, buildDir }) => async function read
|
|||||||
frame.fileName = sanitizeName(frame.fileName)
|
frame.fileName = sanitizeName(frame.fileName)
|
||||||
|
|
||||||
// Return if fileName is unknown
|
// Return if fileName is unknown
|
||||||
/* istanbul ignore if */
|
|
||||||
if (!frame.fileName) {
|
if (!frame.fileName) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,6 @@ export default ({ options, nuxt, renderRoute, resources }) => async function nux
|
|||||||
await nuxt.callHook('render:routeDone', url, result, context)
|
await nuxt.callHook('render:routeDone', url, result, context)
|
||||||
return html
|
return html
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
/* istanbul ignore if */
|
|
||||||
if (context && context.redirected) {
|
if (context && context.redirected) {
|
||||||
consola.error(err)
|
consola.error(err)
|
||||||
return err
|
return err
|
||||||
@ -95,7 +94,6 @@ const defaultPushAssets = (preloadFiles, shouldPush, publicPath, options) => {
|
|||||||
const links = []
|
const links = []
|
||||||
preloadFiles.forEach(({ file, asType, fileWithoutQuery, modern }) => {
|
preloadFiles.forEach(({ file, asType, fileWithoutQuery, modern }) => {
|
||||||
// By default, we only preload scripts or css
|
// By default, we only preload scripts or css
|
||||||
/* istanbul ignore if */
|
|
||||||
if (!shouldPush && asType !== 'script' && asType !== 'style') {
|
if (!shouldPush && asType !== 'script' && asType !== 'style') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
17
packages/server/test/contest.test.js
Normal file
17
packages/server/test/contest.test.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import ServerContext from '../src/context'
|
||||||
|
|
||||||
|
describe('server: ServerContext', () => {
|
||||||
|
test('should construct context', () => {
|
||||||
|
const server = {
|
||||||
|
nuxt: { id: 'test-contest-nuxt' },
|
||||||
|
globals: { id: 'test-contest-globals' },
|
||||||
|
options: { id: 'test-contest-options' },
|
||||||
|
resources: { id: 'test-contest-resources' }
|
||||||
|
}
|
||||||
|
const context = new ServerContext(server)
|
||||||
|
expect(context.nuxt).toBe(server.nuxt)
|
||||||
|
expect(context.globals).toEqual(server.globals)
|
||||||
|
expect(context.options).toEqual(server.options)
|
||||||
|
expect(context.resources).toEqual(server.resources)
|
||||||
|
})
|
||||||
|
})
|
14
packages/server/test/index.test.js
Normal file
14
packages/server/test/index.test.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { Server, Listener } from '../src'
|
||||||
|
|
||||||
|
jest.mock('../src/server', () => ({
|
||||||
|
server: true
|
||||||
|
})).mock('../src/listener', () => ({
|
||||||
|
listener: true
|
||||||
|
}))
|
||||||
|
|
||||||
|
describe('server: entry', () => {
|
||||||
|
test('should export Server and Listener', () => {
|
||||||
|
expect(Server.server).toEqual(true)
|
||||||
|
expect(Listener.listener).toEqual(true)
|
||||||
|
})
|
||||||
|
})
|
395
packages/server/test/listener.test.js
Normal file
395
packages/server/test/listener.test.js
Normal file
@ -0,0 +1,395 @@
|
|||||||
|
import http from 'http'
|
||||||
|
import https from 'https'
|
||||||
|
import enableDestroy from 'server-destroy'
|
||||||
|
import ip from 'ip'
|
||||||
|
import consola from 'consola'
|
||||||
|
import pify from 'pify'
|
||||||
|
|
||||||
|
import Listener from '../src/listener'
|
||||||
|
|
||||||
|
jest.mock('http')
|
||||||
|
jest.mock('https')
|
||||||
|
jest.mock('server-destroy')
|
||||||
|
jest.mock('ip')
|
||||||
|
jest.mock('pify')
|
||||||
|
|
||||||
|
describe('server: listener', () => {
|
||||||
|
const mockServer = () => {
|
||||||
|
const server = {
|
||||||
|
address: jest.fn(),
|
||||||
|
on: jest.fn(),
|
||||||
|
listen: jest.fn((listenArgs, callback) => {
|
||||||
|
Promise.resolve().then(callback)
|
||||||
|
return server
|
||||||
|
}),
|
||||||
|
destroy: jest.fn()
|
||||||
|
}
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should construct listener', () => {
|
||||||
|
const options = {
|
||||||
|
port: 3000,
|
||||||
|
host: 'localhost',
|
||||||
|
socket: jest.fn(),
|
||||||
|
https: { id: 'test-listener-https' },
|
||||||
|
app: jest.fn(),
|
||||||
|
dev: false
|
||||||
|
}
|
||||||
|
const listener = new Listener(options)
|
||||||
|
|
||||||
|
expect(listener.port).toEqual(options.port)
|
||||||
|
expect(listener.host).toEqual(options.host)
|
||||||
|
expect(listener.socket).toEqual(options.socket)
|
||||||
|
expect(listener.https).toEqual(options.https)
|
||||||
|
expect(listener.app).toEqual(options.app)
|
||||||
|
expect(listener.dev).toEqual(options.dev)
|
||||||
|
expect(listener.listening).toEqual(false)
|
||||||
|
expect(listener._server).toBe(null)
|
||||||
|
expect(listener.server).toBe(null)
|
||||||
|
expect(listener.address).toBe(null)
|
||||||
|
expect(listener.url).toBe(null)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should listen http host and port', async () => {
|
||||||
|
const server = mockServer()
|
||||||
|
http.createServer.mockReturnValueOnce(server)
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
port: 3000,
|
||||||
|
host: 'localhost',
|
||||||
|
https: false,
|
||||||
|
app: jest.fn(),
|
||||||
|
dev: false
|
||||||
|
}
|
||||||
|
const listener = new Listener(options)
|
||||||
|
listener.computeURL = jest.fn()
|
||||||
|
|
||||||
|
await listener.listen()
|
||||||
|
|
||||||
|
expect(http.createServer).toBeCalledTimes(1)
|
||||||
|
expect(http.createServer).toBeCalledWith(options.app)
|
||||||
|
expect(server.on).toBeCalledTimes(1)
|
||||||
|
expect(server.on).toBeCalledWith('error', expect.any(Function))
|
||||||
|
expect(server.listen).toBeCalledTimes(1)
|
||||||
|
expect(server.listen).toBeCalledWith(
|
||||||
|
{
|
||||||
|
host: options.host,
|
||||||
|
port: options.port,
|
||||||
|
exclusive: false
|
||||||
|
},
|
||||||
|
expect.any(Function)
|
||||||
|
)
|
||||||
|
expect(listener.server).toBe(server)
|
||||||
|
expect(enableDestroy).toBeCalledTimes(1)
|
||||||
|
expect(enableDestroy).toBeCalledWith(listener.server)
|
||||||
|
expect(pify).toBeCalledTimes(1)
|
||||||
|
expect(pify).toBeCalledWith(listener.server.destroy)
|
||||||
|
expect(listener.computeURL).toBeCalledTimes(1)
|
||||||
|
expect(listener.listening).toEqual(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should listen https host and port', async () => {
|
||||||
|
const server = mockServer()
|
||||||
|
https.createServer.mockReturnValueOnce(server)
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
port: 3000,
|
||||||
|
host: 'localhost',
|
||||||
|
https: { key: 'test-listener' },
|
||||||
|
app: jest.fn(),
|
||||||
|
dev: false
|
||||||
|
}
|
||||||
|
const listener = new Listener(options)
|
||||||
|
listener.computeURL = jest.fn()
|
||||||
|
|
||||||
|
await listener.listen()
|
||||||
|
|
||||||
|
expect(https.createServer).toBeCalledTimes(1)
|
||||||
|
expect(https.createServer).toBeCalledWith(options.https, options.app)
|
||||||
|
expect(server.on).toBeCalledTimes(1)
|
||||||
|
expect(server.on).toBeCalledWith('error', expect.any(Function))
|
||||||
|
expect(server.listen).toBeCalledTimes(1)
|
||||||
|
expect(server.listen).toBeCalledWith(
|
||||||
|
{
|
||||||
|
host: options.host,
|
||||||
|
port: options.port,
|
||||||
|
exclusive: false
|
||||||
|
},
|
||||||
|
expect.any(Function)
|
||||||
|
)
|
||||||
|
expect(listener.server).toBe(server)
|
||||||
|
expect(enableDestroy).toBeCalledTimes(1)
|
||||||
|
expect(enableDestroy).toBeCalledWith(listener.server)
|
||||||
|
expect(pify).toBeCalledTimes(1)
|
||||||
|
expect(pify).toBeCalledWith(listener.server.destroy)
|
||||||
|
expect(listener.computeURL).toBeCalledTimes(1)
|
||||||
|
expect(listener.listening).toEqual(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should listen unix socket host and port', async () => {
|
||||||
|
const server = mockServer()
|
||||||
|
http.createServer.mockReturnValueOnce(server)
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
port: 3000,
|
||||||
|
host: 'localhost',
|
||||||
|
https: false,
|
||||||
|
socket: '/var/nuxt/unix.socket',
|
||||||
|
app: jest.fn(),
|
||||||
|
dev: false
|
||||||
|
}
|
||||||
|
const listener = new Listener(options)
|
||||||
|
listener.computeURL = jest.fn()
|
||||||
|
|
||||||
|
await listener.listen()
|
||||||
|
|
||||||
|
expect(http.createServer).toBeCalledTimes(1)
|
||||||
|
expect(http.createServer).toBeCalledWith(options.app)
|
||||||
|
expect(server.on).toBeCalledTimes(1)
|
||||||
|
expect(server.on).toBeCalledWith('error', expect.any(Function))
|
||||||
|
expect(server.listen).toBeCalledTimes(1)
|
||||||
|
expect(server.listen).toBeCalledWith(
|
||||||
|
{
|
||||||
|
path: options.socket,
|
||||||
|
exclusive: false
|
||||||
|
},
|
||||||
|
expect.any(Function)
|
||||||
|
)
|
||||||
|
expect(listener.server).toBe(server)
|
||||||
|
expect(enableDestroy).toBeCalledTimes(1)
|
||||||
|
expect(enableDestroy).toBeCalledWith(listener.server)
|
||||||
|
expect(pify).toBeCalledTimes(1)
|
||||||
|
expect(pify).toBeCalledWith(listener.server.destroy)
|
||||||
|
expect(listener.computeURL).toBeCalledTimes(1)
|
||||||
|
expect(listener.listening).toEqual(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should prevent listening multiple times', async () => {
|
||||||
|
const options = {
|
||||||
|
port: 3000,
|
||||||
|
host: 'localhost',
|
||||||
|
https: false,
|
||||||
|
app: jest.fn(),
|
||||||
|
dev: false
|
||||||
|
}
|
||||||
|
const listener = new Listener(options)
|
||||||
|
listener.computeURL = jest.fn()
|
||||||
|
|
||||||
|
listener.listening = true
|
||||||
|
await listener.listen()
|
||||||
|
|
||||||
|
expect(http.createServer).not.toBeCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should throw error if error occurred or listen failed', async () => {
|
||||||
|
const server = mockServer()
|
||||||
|
http.createServer.mockReturnValueOnce(server)
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
port: 3000,
|
||||||
|
host: 'localhost',
|
||||||
|
https: false,
|
||||||
|
app: jest.fn(),
|
||||||
|
dev: false
|
||||||
|
}
|
||||||
|
const listener = new Listener(options)
|
||||||
|
listener.computeURL = jest.fn()
|
||||||
|
listener.serverErrorHandler = jest.fn()
|
||||||
|
|
||||||
|
const serverError = new Error('error occurred')
|
||||||
|
server.listen.mockImplementationOnce((listenArgs, callback) => {
|
||||||
|
Promise.resolve().then(callback)
|
||||||
|
const errorListener = server.on.mock.calls[0][1]
|
||||||
|
errorListener(serverError)
|
||||||
|
return server
|
||||||
|
})
|
||||||
|
await listener.listen()
|
||||||
|
expect(listener.serverErrorHandler).toBeCalledTimes(1)
|
||||||
|
expect(listener.serverErrorHandler).toBeCalledWith(serverError)
|
||||||
|
|
||||||
|
http.createServer.mockReturnValueOnce(server)
|
||||||
|
listener.serverErrorHandler.mockClear()
|
||||||
|
|
||||||
|
const listenError = new Error('listen failed')
|
||||||
|
server.listen.mockImplementationOnce((listenArgs, callback) => {
|
||||||
|
Promise.resolve().then(() => callback(listenError))
|
||||||
|
return server
|
||||||
|
})
|
||||||
|
await listener.listen()
|
||||||
|
expect(listener.serverErrorHandler).toBeCalledTimes(1)
|
||||||
|
expect(listener.serverErrorHandler).toBeCalledWith(listenError)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should compute http url', () => {
|
||||||
|
const options = {
|
||||||
|
port: 3000,
|
||||||
|
host: 'localhost'
|
||||||
|
}
|
||||||
|
const listener = new Listener(options)
|
||||||
|
listener.server = mockServer()
|
||||||
|
|
||||||
|
listener.server.address.mockReturnValueOnce({
|
||||||
|
address: 'localhost',
|
||||||
|
port: 3000
|
||||||
|
})
|
||||||
|
listener.computeURL()
|
||||||
|
expect(listener.host).toEqual('localhost')
|
||||||
|
expect(listener.port).toEqual(3000)
|
||||||
|
expect(listener.url).toEqual('http://localhost:3000')
|
||||||
|
|
||||||
|
listener.server.address.mockReturnValueOnce({
|
||||||
|
address: '127.0.0.1',
|
||||||
|
port: 3001
|
||||||
|
})
|
||||||
|
listener.computeURL()
|
||||||
|
expect(listener.host).toEqual('localhost')
|
||||||
|
expect(listener.port).toEqual(3001)
|
||||||
|
expect(listener.url).toEqual('http://localhost:3001')
|
||||||
|
|
||||||
|
ip.address.mockReturnValueOnce('192.168.0.1')
|
||||||
|
listener.server.address.mockReturnValueOnce({
|
||||||
|
address: '0.0.0.0',
|
||||||
|
port: 3002
|
||||||
|
})
|
||||||
|
listener.computeURL()
|
||||||
|
expect(listener.host).toEqual('192.168.0.1')
|
||||||
|
expect(listener.port).toEqual(3002)
|
||||||
|
expect(listener.url).toEqual('http://192.168.0.1:3002')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should compute https url', () => {
|
||||||
|
const options = {
|
||||||
|
port: 3000,
|
||||||
|
host: 'localhost',
|
||||||
|
https: true
|
||||||
|
}
|
||||||
|
const listener = new Listener(options)
|
||||||
|
listener.server = mockServer()
|
||||||
|
|
||||||
|
listener.server.address.mockReturnValueOnce({
|
||||||
|
address: 'localhost',
|
||||||
|
port: 3000
|
||||||
|
})
|
||||||
|
listener.computeURL()
|
||||||
|
expect(listener.host).toEqual('localhost')
|
||||||
|
expect(listener.port).toEqual(3000)
|
||||||
|
expect(listener.url).toEqual('https://localhost:3000')
|
||||||
|
|
||||||
|
listener.server.address.mockReturnValueOnce({
|
||||||
|
address: '127.0.0.1',
|
||||||
|
port: 3001
|
||||||
|
})
|
||||||
|
listener.computeURL()
|
||||||
|
expect(listener.host).toEqual('localhost')
|
||||||
|
expect(listener.port).toEqual(3001)
|
||||||
|
expect(listener.url).toEqual('https://localhost:3001')
|
||||||
|
|
||||||
|
ip.address.mockReturnValueOnce('192.168.0.1')
|
||||||
|
listener.server.address.mockReturnValueOnce({
|
||||||
|
address: '0.0.0.0',
|
||||||
|
port: 3002
|
||||||
|
})
|
||||||
|
listener.computeURL()
|
||||||
|
expect(listener.host).toEqual('192.168.0.1')
|
||||||
|
expect(listener.port).toEqual(3002)
|
||||||
|
expect(listener.url).toEqual('https://192.168.0.1:3002')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should compute unix socket url', () => {
|
||||||
|
const options = {
|
||||||
|
socket: true
|
||||||
|
}
|
||||||
|
const listener = new Listener(options)
|
||||||
|
listener.server = mockServer()
|
||||||
|
|
||||||
|
listener.server.address.mockReturnValueOnce('/var/nuxt/unix.socket')
|
||||||
|
listener.computeURL()
|
||||||
|
|
||||||
|
expect(listener.url).toEqual('unix+http:///var/nuxt/unix.socket')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should throw error in serverErrorHandler', () => {
|
||||||
|
const listener = new Listener({})
|
||||||
|
|
||||||
|
const error = new Error('server error')
|
||||||
|
expect(() => listener.serverErrorHandler(error)).toThrow(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should throw address in use error', () => {
|
||||||
|
const listener = new Listener({})
|
||||||
|
listener.host = 'localhost'
|
||||||
|
listener.port = 3000
|
||||||
|
|
||||||
|
const addressInUse = new Error()
|
||||||
|
addressInUse.code = 'EADDRINUSE'
|
||||||
|
expect(() => listener.serverErrorHandler(addressInUse)).toThrow('Address `localhost:3000` is already in use.')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should fallback to a random port in address in use error', async () => {
|
||||||
|
const listener = new Listener({ dev: true })
|
||||||
|
listener.host = 'localhost'
|
||||||
|
listener.port = 3000
|
||||||
|
listener.close = jest.fn(() => Promise.resolve())
|
||||||
|
listener.listen = jest.fn()
|
||||||
|
|
||||||
|
const addressInUse = new Error()
|
||||||
|
addressInUse.code = 'EADDRINUSE'
|
||||||
|
|
||||||
|
await listener.serverErrorHandler(addressInUse)
|
||||||
|
|
||||||
|
expect(consola.warn).toBeCalledTimes(1)
|
||||||
|
expect(consola.warn).toBeCalledWith('Address `localhost:3000` is already in use.')
|
||||||
|
expect(consola.info).toBeCalledTimes(1)
|
||||||
|
expect(consola.info).toBeCalledWith('Trying a random port...')
|
||||||
|
expect(listener.port).toEqual('0')
|
||||||
|
expect(listener.close).toBeCalledTimes(1)
|
||||||
|
expect(listener.listen).toBeCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should close server', async () => {
|
||||||
|
const listener = new Listener({})
|
||||||
|
const server = mockServer()
|
||||||
|
listener.listening = true
|
||||||
|
listener._server = server
|
||||||
|
listener.server = server
|
||||||
|
listener.server.listening = true
|
||||||
|
listener.address = 'localhost'
|
||||||
|
listener.url = 'http://localhost:3000'
|
||||||
|
|
||||||
|
await listener.close()
|
||||||
|
|
||||||
|
expect(server.destroy).toBeCalledTimes(1)
|
||||||
|
expect(consola.debug).toBeCalledTimes(1)
|
||||||
|
expect(consola.debug).toBeCalledWith('server closed')
|
||||||
|
expect(listener.listening).toEqual(false)
|
||||||
|
expect(listener._server).toBe(null)
|
||||||
|
expect(listener.server).toBe(null)
|
||||||
|
expect(listener.address).toBe(null)
|
||||||
|
expect(listener.url).toBe(null)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should prevent destroying server if server is not listening', async () => {
|
||||||
|
const listener = new Listener({})
|
||||||
|
const server = mockServer()
|
||||||
|
listener.listening = true
|
||||||
|
listener._server = server
|
||||||
|
listener.server = server
|
||||||
|
listener.address = 'localhost'
|
||||||
|
listener.url = 'http://localhost:3000'
|
||||||
|
|
||||||
|
await listener.close()
|
||||||
|
|
||||||
|
expect(server.destroy).not.toBeCalled()
|
||||||
|
expect(consola.debug).not.toBeCalled()
|
||||||
|
expect(listener.listening).toEqual(false)
|
||||||
|
expect(listener._server).toBe(null)
|
||||||
|
expect(listener.server).toBe(null)
|
||||||
|
expect(listener.address).toBe(null)
|
||||||
|
expect(listener.url).toBe(null)
|
||||||
|
})
|
||||||
|
})
|
277
packages/server/test/middleware/error.test.js
Normal file
277
packages/server/test/middleware/error.test.js
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
import path from 'path'
|
||||||
|
import fs from 'fs-extra'
|
||||||
|
import consola from 'consola'
|
||||||
|
|
||||||
|
import Youch from '@nuxtjs/youch'
|
||||||
|
|
||||||
|
import createErrorMiddleware from '../../src/middleware/error'
|
||||||
|
|
||||||
|
jest.mock('path')
|
||||||
|
jest.mock('fs-extra')
|
||||||
|
jest.mock('@nuxtjs/youch', () => jest.fn(() => ({
|
||||||
|
toHTML: jest.fn(() => 'youch html'),
|
||||||
|
toJSON: jest.fn(() => 'youch json')
|
||||||
|
})))
|
||||||
|
|
||||||
|
const createParams = () => ({
|
||||||
|
resources: { errorTemplate: jest.fn(() => `error template`) },
|
||||||
|
options: {
|
||||||
|
srcDir: '/var/nuxt/src',
|
||||||
|
rootDir: '/var/nuxt',
|
||||||
|
buildDir: '/var/nuxt/dist',
|
||||||
|
router: { base: '/' }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const createServerContext = () => ({
|
||||||
|
req: { headers: {} },
|
||||||
|
res: { setHeader: jest.fn(), end: jest.fn() },
|
||||||
|
next: jest.fn()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('server: errorMiddleware', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
path.join.mockImplementation((...args) => `join(${args.join(', ')})`)
|
||||||
|
path.resolve.mockImplementation((...args) => `resolve(${args.join(', ')})`)
|
||||||
|
fs.readFile.mockImplementation(() => Promise.resolve())
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should return error middleware', () => {
|
||||||
|
const errorMiddleware = createErrorMiddleware({})
|
||||||
|
expect(errorMiddleware).toBeInstanceOf(Function)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should send html error response', async () => {
|
||||||
|
const params = createParams()
|
||||||
|
const errorMiddleware = createErrorMiddleware(params)
|
||||||
|
const error = new Error()
|
||||||
|
const ctx = createServerContext()
|
||||||
|
|
||||||
|
await errorMiddleware(error, ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
expect(consola.error).toBeCalledWith(error)
|
||||||
|
expect(ctx.res.statusCode).toEqual(500)
|
||||||
|
expect(ctx.res.statusMessage).toEqual('NuxtServerError')
|
||||||
|
expect(ctx.res.setHeader).toBeCalledTimes(3)
|
||||||
|
expect(ctx.res.setHeader).nthCalledWith(1, 'Content-Type', 'text/html; charset=utf-8')
|
||||||
|
expect(ctx.res.setHeader).nthCalledWith(2, 'Content-Length', Buffer.byteLength('error template'))
|
||||||
|
expect(ctx.res.setHeader).nthCalledWith(3, 'Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
|
||||||
|
expect(params.resources.errorTemplate).toBeCalledTimes(1)
|
||||||
|
expect(params.resources.errorTemplate).toBeCalledWith({
|
||||||
|
status: 500,
|
||||||
|
message: 'Nuxt Server Error',
|
||||||
|
name: 'NuxtServerError'
|
||||||
|
})
|
||||||
|
expect(ctx.res.end).toBeCalledTimes(1)
|
||||||
|
expect(ctx.res.end).toBeCalledWith('error template', 'utf-8')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should send json error response', async () => {
|
||||||
|
const params = createParams()
|
||||||
|
const errorMiddleware = createErrorMiddleware(params)
|
||||||
|
const error = {
|
||||||
|
statusCode: 404,
|
||||||
|
name: 'NuxtTestError',
|
||||||
|
message: 'test error'
|
||||||
|
}
|
||||||
|
const ctx = createServerContext()
|
||||||
|
ctx.req.headers.accept = 'application/json'
|
||||||
|
|
||||||
|
await errorMiddleware(error, ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
const errJson = JSON.stringify({
|
||||||
|
status: error.statusCode,
|
||||||
|
message: error.message,
|
||||||
|
name: error.name
|
||||||
|
}, undefined, 2)
|
||||||
|
expect(consola.error).not.toBeCalled()
|
||||||
|
expect(ctx.res.statusCode).toEqual(404)
|
||||||
|
expect(ctx.res.statusMessage).toEqual(error.name)
|
||||||
|
expect(ctx.res.setHeader).toBeCalledTimes(3)
|
||||||
|
expect(ctx.res.setHeader).nthCalledWith(1, 'Content-Type', 'text/json; charset=utf-8')
|
||||||
|
expect(ctx.res.setHeader).nthCalledWith(2, 'Content-Length', Buffer.byteLength(errJson))
|
||||||
|
expect(ctx.res.setHeader).nthCalledWith(3, 'Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
|
||||||
|
expect(params.resources.errorTemplate).not.toBeCalled()
|
||||||
|
expect(ctx.res.end).toBeCalledTimes(1)
|
||||||
|
expect(ctx.res.end).toBeCalledWith(errJson, 'utf-8')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should send html error response by youch in debug mode', async () => {
|
||||||
|
const params = createParams()
|
||||||
|
params.options.debug = true
|
||||||
|
const errorMiddleware = createErrorMiddleware(params)
|
||||||
|
const error = new Error('test error')
|
||||||
|
error.statusCode = 503
|
||||||
|
error.name = 'NuxtTestError'
|
||||||
|
const ctx = createServerContext()
|
||||||
|
|
||||||
|
await errorMiddleware(error, ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
const errHtml = 'youch html'
|
||||||
|
expect(Youch).toBeCalledTimes(1)
|
||||||
|
expect(Youch).toBeCalledWith(
|
||||||
|
error,
|
||||||
|
ctx.req,
|
||||||
|
expect.any(Function),
|
||||||
|
params.options.router.base,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
expect(ctx.res.statusCode).toEqual(503)
|
||||||
|
expect(ctx.res.statusMessage).toEqual(error.name)
|
||||||
|
expect(ctx.res.setHeader).toBeCalledTimes(3)
|
||||||
|
expect(ctx.res.setHeader).nthCalledWith(1, 'Content-Type', 'text/html; charset=utf-8')
|
||||||
|
expect(ctx.res.setHeader).nthCalledWith(2, 'Content-Length', Buffer.byteLength(errHtml))
|
||||||
|
expect(ctx.res.setHeader).nthCalledWith(3, 'Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
|
||||||
|
expect(params.resources.errorTemplate).not.toBeCalled()
|
||||||
|
expect(ctx.res.end).toBeCalledTimes(1)
|
||||||
|
expect(ctx.res.end).toBeCalledWith(errHtml, 'utf-8')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should send json error response by youch in debug mode', async () => {
|
||||||
|
const params = createParams()
|
||||||
|
params.options.debug = true
|
||||||
|
const errorMiddleware = createErrorMiddleware(params)
|
||||||
|
const error = {
|
||||||
|
statusCode: 404,
|
||||||
|
name: 'NuxtTestError',
|
||||||
|
message: 'test error'
|
||||||
|
}
|
||||||
|
const ctx = createServerContext()
|
||||||
|
ctx.req.headers.accept = 'application/json'
|
||||||
|
|
||||||
|
await errorMiddleware(error, ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
const errJson = JSON.stringify('youch json', undefined, 2)
|
||||||
|
const errorFull = new Error(error.message)
|
||||||
|
errorFull.name = error.name
|
||||||
|
errorFull.statusCode = error.statusCode
|
||||||
|
errorFull.stack = undefined
|
||||||
|
expect(Youch).toBeCalledTimes(1)
|
||||||
|
expect(Youch).toBeCalledWith(
|
||||||
|
errorFull,
|
||||||
|
ctx.req,
|
||||||
|
expect.any(Function),
|
||||||
|
params.options.router.base,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
expect(ctx.res.statusCode).toEqual(404)
|
||||||
|
expect(ctx.res.statusMessage).toEqual(error.name)
|
||||||
|
expect(ctx.res.setHeader).toBeCalledTimes(3)
|
||||||
|
expect(ctx.res.setHeader).nthCalledWith(1, 'Content-Type', 'text/json; charset=utf-8')
|
||||||
|
expect(ctx.res.setHeader).nthCalledWith(2, 'Content-Length', Buffer.byteLength(errJson))
|
||||||
|
expect(ctx.res.setHeader).nthCalledWith(3, 'Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
|
||||||
|
expect(params.resources.errorTemplate).not.toBeCalled()
|
||||||
|
expect(ctx.res.end).toBeCalledTimes(1)
|
||||||
|
expect(ctx.res.end).toBeCalledWith(errJson, 'utf-8')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should search all possible paths when read source', async () => {
|
||||||
|
const params = createParams()
|
||||||
|
params.options.debug = true
|
||||||
|
const errorMiddleware = createErrorMiddleware(params)
|
||||||
|
const error = {}
|
||||||
|
const ctx = createServerContext()
|
||||||
|
|
||||||
|
await errorMiddleware(error, ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
const frame = { fileName: 'webpack:///test-error.js?desc=test' }
|
||||||
|
const readSource = Youch.mock.calls[0][2]
|
||||||
|
await readSource(frame)
|
||||||
|
|
||||||
|
const fileName = 'test-error.js'
|
||||||
|
expect(frame).toEqual({ fileName })
|
||||||
|
expect(path.resolve).toBeCalledTimes(5)
|
||||||
|
expect(fs.readFile).toBeCalledTimes(5)
|
||||||
|
expect(fs.readFile).nthCalledWith(1, `resolve(${params.options.srcDir}, ${fileName})`, 'utf-8')
|
||||||
|
expect(fs.readFile).nthCalledWith(2, `resolve(${params.options.rootDir}, ${fileName})`, 'utf-8')
|
||||||
|
expect(fs.readFile).nthCalledWith(3, `resolve(join(${params.options.buildDir}, dist, server), ${fileName})`, 'utf-8')
|
||||||
|
expect(fs.readFile).nthCalledWith(4, `resolve(${params.options.buildDir}, ${fileName})`, 'utf-8')
|
||||||
|
expect(fs.readFile).nthCalledWith(5, `resolve(${process.cwd()}, ${fileName})`, 'utf-8')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should return source content after read source', async () => {
|
||||||
|
const params = createParams()
|
||||||
|
params.options.debug = true
|
||||||
|
const errorMiddleware = createErrorMiddleware(params)
|
||||||
|
const error = {}
|
||||||
|
const ctx = createServerContext()
|
||||||
|
|
||||||
|
await errorMiddleware(error, ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
const frame = { fileName: 'webpack:///test-error.js?desc=test' }
|
||||||
|
const readSource = Youch.mock.calls[0][2]
|
||||||
|
fs.readFile.mockImplementationOnce(() => Promise.resolve('source content'))
|
||||||
|
await readSource(frame)
|
||||||
|
|
||||||
|
const fileName = 'test-error.js'
|
||||||
|
expect(frame).toEqual({
|
||||||
|
fileName,
|
||||||
|
contents: 'source content',
|
||||||
|
fullPath: `resolve(${params.options.srcDir}, ${fileName})`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should return relative fileName if fileName is absolute path', async () => {
|
||||||
|
const params = createParams()
|
||||||
|
params.options.debug = true
|
||||||
|
const errorMiddleware = createErrorMiddleware(params)
|
||||||
|
const error = {}
|
||||||
|
const ctx = createServerContext()
|
||||||
|
|
||||||
|
await errorMiddleware(error, ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
const frame = { fileName: 'webpack:///test-error.js?desc=test' }
|
||||||
|
const readSource = Youch.mock.calls[0][2]
|
||||||
|
fs.readFile.mockImplementationOnce(() => Promise.resolve('source content'))
|
||||||
|
path.isAbsolute.mockReturnValueOnce(true)
|
||||||
|
path.relative.mockImplementationOnce((...args) => `relative(${args.join(', ')})`)
|
||||||
|
await readSource(frame)
|
||||||
|
|
||||||
|
const fullPath = `resolve(${params.options.srcDir}, test-error.js)`
|
||||||
|
expect(frame).toEqual({
|
||||||
|
fileName: `relative(${params.options.rootDir}, ${fullPath})`,
|
||||||
|
contents: 'source content',
|
||||||
|
fullPath
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should ignore error when reading source', async () => {
|
||||||
|
const params = createParams()
|
||||||
|
params.options.debug = true
|
||||||
|
const errorMiddleware = createErrorMiddleware(params)
|
||||||
|
const error = {}
|
||||||
|
const ctx = createServerContext()
|
||||||
|
|
||||||
|
await errorMiddleware(error, ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
const frame = { fileName: 'webpack:///test-error.js?desc=test' }
|
||||||
|
const readSource = Youch.mock.calls[0][2]
|
||||||
|
fs.readFile.mockReturnValueOnce(Promise.reject(new Error('read failed')))
|
||||||
|
await readSource(frame)
|
||||||
|
|
||||||
|
const fileName = 'test-error.js'
|
||||||
|
expect(frame).toEqual({ fileName })
|
||||||
|
expect(path.resolve).toBeCalledTimes(5)
|
||||||
|
expect(fs.readFile).toBeCalledTimes(5)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should return if fileName is unknown when read source', async () => {
|
||||||
|
const params = createParams()
|
||||||
|
params.options.debug = true
|
||||||
|
const errorMiddleware = createErrorMiddleware(params)
|
||||||
|
const error = {}
|
||||||
|
const ctx = createServerContext()
|
||||||
|
|
||||||
|
await errorMiddleware(error, ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
const frame = {}
|
||||||
|
const readSource = Youch.mock.calls[0][2]
|
||||||
|
await readSource(frame)
|
||||||
|
|
||||||
|
expect(frame.fileName).toBeNull()
|
||||||
|
})
|
||||||
|
})
|
142
packages/server/test/middleware/modern.test.js
Normal file
142
packages/server/test/middleware/modern.test.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import consola from 'consola'
|
||||||
|
|
||||||
|
jest.mock('chalk', () => ({
|
||||||
|
green: {
|
||||||
|
bold: modern => `greenBold(${modern})`
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
const createContext = () => ({
|
||||||
|
resources: {},
|
||||||
|
options: {
|
||||||
|
render: {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const createServerContext = () => ({
|
||||||
|
req: { headers: {} },
|
||||||
|
next: jest.fn()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('server: modernMiddleware', () => {
|
||||||
|
let createModernMiddleware
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.isolateModules(() => {
|
||||||
|
createModernMiddleware = require('../../src/middleware/modern').default
|
||||||
|
})
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should return modern middleware', () => {
|
||||||
|
const modernMiddleware = createModernMiddleware({})
|
||||||
|
expect(modernMiddleware).toBeInstanceOf(Function)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should not detect modern build if modern mode is specified', () => {
|
||||||
|
const context = createContext()
|
||||||
|
const modernMiddleware = createModernMiddleware({ context })
|
||||||
|
const ctx = createServerContext()
|
||||||
|
|
||||||
|
context.options.modern = false
|
||||||
|
modernMiddleware(ctx.req, ctx.res, ctx.next)
|
||||||
|
context.options.modern = 'client'
|
||||||
|
modernMiddleware(ctx.req, ctx.res, ctx.next)
|
||||||
|
context.options.modern = 'server'
|
||||||
|
modernMiddleware(ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
expect(ctx.req.modernMode).toEqual(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should detect client modern build and display message', () => {
|
||||||
|
const context = createContext()
|
||||||
|
const modernMiddleware = createModernMiddleware({ context })
|
||||||
|
const ctx = createServerContext()
|
||||||
|
|
||||||
|
context.resources.modernManifest = {}
|
||||||
|
modernMiddleware(ctx.req, ctx.res, ctx.next)
|
||||||
|
expect(context.options.modern).toEqual('client')
|
||||||
|
expect(consola.info).toBeCalledWith('Modern bundles are detected. Modern mode (greenBold(client)) is enabled now.')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should detect server modern build and display message', () => {
|
||||||
|
const context = createContext()
|
||||||
|
const modernMiddleware = createModernMiddleware({ context })
|
||||||
|
const ctx = createServerContext()
|
||||||
|
|
||||||
|
context.options.render.ssr = true
|
||||||
|
context.resources.modernManifest = {}
|
||||||
|
modernMiddleware(ctx.req, ctx.res, ctx.next)
|
||||||
|
expect(context.options.modern).toEqual('server')
|
||||||
|
expect(consola.info).toBeCalledWith('Modern bundles are detected. Modern mode (greenBold(server)) is enabled now.')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should not detect modern browser if modern build is not found', () => {
|
||||||
|
const context = createContext()
|
||||||
|
const modernMiddleware = createModernMiddleware({ context })
|
||||||
|
const ctx = createServerContext()
|
||||||
|
|
||||||
|
modernMiddleware(ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
expect(ctx.req.modernMode).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should not detect modern browser if connect has been detected', () => {
|
||||||
|
const context = createContext()
|
||||||
|
const modernMiddleware = createModernMiddleware({ context })
|
||||||
|
const ctx = createServerContext()
|
||||||
|
ctx.req.socket = { isModernBrowser: true }
|
||||||
|
|
||||||
|
context.options.dev = true
|
||||||
|
context.options.modern = 'server'
|
||||||
|
modernMiddleware(ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
expect(ctx.req.modernMode).toEqual(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should detect modern browser based on user-agent', () => {
|
||||||
|
const context = createContext()
|
||||||
|
const modernMiddleware = createModernMiddleware({ context })
|
||||||
|
const ctx = createServerContext()
|
||||||
|
const ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'
|
||||||
|
ctx.req.headers['user-agent'] = ua
|
||||||
|
ctx.req.socket = {}
|
||||||
|
|
||||||
|
context.options.dev = true
|
||||||
|
context.options.modern = 'server'
|
||||||
|
modernMiddleware(ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
expect(ctx.req.socket.isModernBrowser).toEqual(true)
|
||||||
|
expect(ctx.req.modernMode).toEqual(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should detect legacy browser based on user-agent', () => {
|
||||||
|
const context = createContext()
|
||||||
|
const modernMiddleware = createModernMiddleware({ context })
|
||||||
|
const ctx = createServerContext()
|
||||||
|
const ua = 'Mozilla/5.0 (Windows; U; MSIE 9.0; WIndows NT 9.0; en-US))'
|
||||||
|
ctx.req.headers['user-agent'] = ua
|
||||||
|
ctx.req.socket = {}
|
||||||
|
|
||||||
|
context.options.dev = true
|
||||||
|
context.options.modern = 'client'
|
||||||
|
modernMiddleware(ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
expect(ctx.req.socket.isModernBrowser).toEqual(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should ignore illegal user-agent', () => {
|
||||||
|
const context = createContext()
|
||||||
|
const modernMiddleware = createModernMiddleware({ context })
|
||||||
|
const ctx = createServerContext()
|
||||||
|
const ua = 'illegal user agent'
|
||||||
|
ctx.req.headers['user-agent'] = ua
|
||||||
|
ctx.req.socket = {}
|
||||||
|
|
||||||
|
context.options.dev = true
|
||||||
|
context.options.modern = 'client'
|
||||||
|
modernMiddleware(ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
expect(ctx.req.socket.isModernBrowser).toEqual(false)
|
||||||
|
})
|
||||||
|
})
|
309
packages/server/test/middleware/nuxt.test.js
Normal file
309
packages/server/test/middleware/nuxt.test.js
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
import generateETag from 'etag'
|
||||||
|
import fresh from 'fresh'
|
||||||
|
import consola from 'consola'
|
||||||
|
|
||||||
|
import createNuxtMiddleware from '../../src/middleware/nuxt'
|
||||||
|
|
||||||
|
jest.mock('etag', () => jest.fn(() => 'etag-hash'))
|
||||||
|
jest.mock('fresh')
|
||||||
|
|
||||||
|
const createContext = () => ({
|
||||||
|
options: {
|
||||||
|
render: { http2: false },
|
||||||
|
build: {}
|
||||||
|
},
|
||||||
|
nuxt: {
|
||||||
|
callHook: jest.fn()
|
||||||
|
},
|
||||||
|
renderRoute: jest.fn(),
|
||||||
|
resources: {}
|
||||||
|
})
|
||||||
|
|
||||||
|
const createServerContext = () => ({
|
||||||
|
req: { headers: {}, url: 'http://localhost/test/server' },
|
||||||
|
res: { headers: {}, setHeader: jest.fn(), end: jest.fn() },
|
||||||
|
next: jest.fn()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('server: nuxtMiddleware', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should return nuxt middleware', () => {
|
||||||
|
const nuxtMiddleware = createNuxtMiddleware({})
|
||||||
|
expect(nuxtMiddleware).toBeInstanceOf(Function)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should render route in nuxt middleware', async () => {
|
||||||
|
const context = createContext()
|
||||||
|
const result = { html: 'rendered html' }
|
||||||
|
context.renderRoute.mockReturnValue(result)
|
||||||
|
const nuxtMiddleware = createNuxtMiddleware(context)
|
||||||
|
const { req, res, next } = createServerContext()
|
||||||
|
|
||||||
|
const html = await nuxtMiddleware(req, res, next)
|
||||||
|
|
||||||
|
expect(context.renderRoute).toBeCalledTimes(1)
|
||||||
|
expect(context.renderRoute).toBeCalledWith(req.url, { req, res })
|
||||||
|
|
||||||
|
expect(context.nuxt.callHook).toBeCalledTimes(2)
|
||||||
|
expect(context.nuxt.callHook).nthCalledWith(1, 'render:route', req.url, result, { req, res })
|
||||||
|
expect(context.nuxt.callHook).nthCalledWith(2, 'render:routeDone', req.url, result, { req, res })
|
||||||
|
|
||||||
|
expect(res.setHeader).toBeCalledTimes(3)
|
||||||
|
expect(res.setHeader).nthCalledWith(1, 'Content-Type', 'text/html; charset=utf-8')
|
||||||
|
expect(res.setHeader).nthCalledWith(2, 'Accept-Ranges', 'none')
|
||||||
|
expect(res.setHeader).nthCalledWith(3, 'Content-Length', Buffer.byteLength(result.html))
|
||||||
|
|
||||||
|
expect(res.end).toBeCalledTimes(1)
|
||||||
|
expect(res.end).toBeCalledWith(result.html, 'utf8')
|
||||||
|
expect(res.statusCode).toEqual(200)
|
||||||
|
expect(html).toEqual(result.html)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should early return if route is redirected', async () => {
|
||||||
|
const context = createContext()
|
||||||
|
const result = { html: 'rendered html', redirected: true }
|
||||||
|
context.renderRoute.mockReturnValue(result)
|
||||||
|
const nuxtMiddleware = createNuxtMiddleware(context)
|
||||||
|
const { req, res, next } = createServerContext()
|
||||||
|
|
||||||
|
const html = await nuxtMiddleware(req, res, next)
|
||||||
|
|
||||||
|
expect(context.nuxt.callHook).toBeCalledTimes(2)
|
||||||
|
expect(context.nuxt.callHook).nthCalledWith(1, 'render:route', req.url, result, { req, res })
|
||||||
|
expect(context.nuxt.callHook).nthCalledWith(2, 'render:routeDone', req.url, result, { req, res })
|
||||||
|
|
||||||
|
expect(res.setHeader).not.toBeCalled()
|
||||||
|
expect(res.end).not.toBeCalled()
|
||||||
|
expect(res.statusCode).toEqual(200)
|
||||||
|
expect(html).toEqual(result.html)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should set error status code when error occurred', async () => {
|
||||||
|
const context = createContext()
|
||||||
|
const result = { html: 'rendered html', error: new Error('render error') }
|
||||||
|
const nuxt = { error: { statusCode: 404 } }
|
||||||
|
context.renderRoute.mockImplementation((url, ctx) => {
|
||||||
|
ctx.nuxt = nuxt
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
const nuxtMiddleware = createNuxtMiddleware(context)
|
||||||
|
const { req, res, next } = createServerContext()
|
||||||
|
|
||||||
|
const html = await nuxtMiddleware(req, res, next)
|
||||||
|
|
||||||
|
expect(context.nuxt.callHook).toBeCalledTimes(2)
|
||||||
|
expect(context.nuxt.callHook).nthCalledWith(1, 'render:route', req.url, result, { req, res, nuxt })
|
||||||
|
expect(context.nuxt.callHook).nthCalledWith(2, 'render:routeDone', req.url, result, { req, res, nuxt })
|
||||||
|
|
||||||
|
expect(res.statusCode).toEqual(404)
|
||||||
|
expect(html).toEqual(result.html)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should add etag after rendering', async () => {
|
||||||
|
const context = createContext()
|
||||||
|
const result = { html: 'rendered html' }
|
||||||
|
context.renderRoute.mockReturnValue(result)
|
||||||
|
context.options.render.etag = true
|
||||||
|
const nuxtMiddleware = createNuxtMiddleware(context)
|
||||||
|
const { req, res, next } = createServerContext()
|
||||||
|
|
||||||
|
await nuxtMiddleware(req, res, next)
|
||||||
|
|
||||||
|
expect(generateETag).toBeCalledTimes(1)
|
||||||
|
expect(generateETag).toBeCalledWith('rendered html', true)
|
||||||
|
expect(res.setHeader).nthCalledWith(1, 'ETag', 'etag-hash')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should return 304 if request is fresh', async () => {
|
||||||
|
const context = createContext()
|
||||||
|
const result = { html: 'rendered html' }
|
||||||
|
context.renderRoute.mockReturnValue(result)
|
||||||
|
context.options.render.etag = true
|
||||||
|
const nuxtMiddleware = createNuxtMiddleware(context)
|
||||||
|
const { req, res, next } = createServerContext()
|
||||||
|
fresh.mockReturnValue(true)
|
||||||
|
|
||||||
|
await nuxtMiddleware(req, res, next)
|
||||||
|
|
||||||
|
expect(res.statusCode).toEqual(304)
|
||||||
|
expect(res.end).toBeCalledTimes(1)
|
||||||
|
expect(res.end).toBeCalledWith()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should add http2 links header if http2 push is enabled', async () => {
|
||||||
|
const context = createContext()
|
||||||
|
const result = {
|
||||||
|
html: 'rendered html',
|
||||||
|
getPreloadFiles: jest.fn(() => ['/nuxt/preload1.js', '/nuxt/preload2.js'])
|
||||||
|
}
|
||||||
|
context.renderRoute.mockReturnValue(result)
|
||||||
|
const pushAssets = jest.fn((req, res, publicPath, preloadFiles) => preloadFiles)
|
||||||
|
context.options.render.http2 = { push: true, pushAssets }
|
||||||
|
context.resources = { clientManifest: { publicPath: '/nuxt' } }
|
||||||
|
|
||||||
|
const nuxtMiddleware = createNuxtMiddleware(context)
|
||||||
|
const { req, res, next } = createServerContext()
|
||||||
|
fresh.mockReturnValue(true)
|
||||||
|
|
||||||
|
await nuxtMiddleware(req, res, next)
|
||||||
|
|
||||||
|
expect(pushAssets).toBeCalledWith(req, res, '/nuxt', ['/nuxt/preload1.js', '/nuxt/preload2.js'])
|
||||||
|
expect(res.setHeader).nthCalledWith(1, 'Link', '/nuxt/preload1.js, /nuxt/preload2.js')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should only include script and style in http2 push by default', async () => {
|
||||||
|
const context = createContext()
|
||||||
|
const result = {
|
||||||
|
html: 'rendered html',
|
||||||
|
getPreloadFiles: jest.fn(() => [
|
||||||
|
{ file: '/nuxt/preload1.js', asType: 'script' },
|
||||||
|
{ file: '/nuxt/preload2.js', asType: 'script' },
|
||||||
|
{ file: '/nuxt/style.css', asType: 'style' },
|
||||||
|
{ file: '/nuxt/font.woff', asType: 'font' }
|
||||||
|
])
|
||||||
|
}
|
||||||
|
context.renderRoute.mockReturnValue(result)
|
||||||
|
context.options.render.http2 = { push: true }
|
||||||
|
context.resources = { clientManifest: { publicPath: '/nuxt' } }
|
||||||
|
|
||||||
|
const nuxtMiddleware = createNuxtMiddleware(context)
|
||||||
|
const { req, res, next } = createServerContext()
|
||||||
|
fresh.mockReturnValue(true)
|
||||||
|
|
||||||
|
await nuxtMiddleware(req, res, next)
|
||||||
|
|
||||||
|
expect(res.setHeader).nthCalledWith(1, 'Link', '</nuxt/nuxt/preload1.js>; rel=preload; as=script, </nuxt/nuxt/preload2.js>; rel=preload; as=script, </nuxt/nuxt/style.css>; rel=preload; as=style')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should ignore preload files which are excluded by shouldPush', async () => {
|
||||||
|
const context = createContext()
|
||||||
|
const result = {
|
||||||
|
html: 'rendered html',
|
||||||
|
getPreloadFiles: jest.fn(() => [
|
||||||
|
{ file: '/nuxt/preload1.js', asType: 'script' },
|
||||||
|
{ file: '/nuxt/preload2.js', asType: 'script', modern: true },
|
||||||
|
{ file: '/nuxt/style.css', asType: 'style' },
|
||||||
|
{ file: '/nuxt/font.woff', asType: 'font' }
|
||||||
|
])
|
||||||
|
}
|
||||||
|
context.renderRoute.mockReturnValue(result)
|
||||||
|
context.options.dev = true
|
||||||
|
context.options.build.crossorigin = 'use-credentials'
|
||||||
|
context.options.render.http2 = {
|
||||||
|
push: true,
|
||||||
|
shouldPush: jest.fn((fileWithoutQuery, asType) => asType === 'script')
|
||||||
|
}
|
||||||
|
context.resources = { clientManifest: { publicPath: '/nuxt' } }
|
||||||
|
|
||||||
|
const nuxtMiddleware = createNuxtMiddleware(context)
|
||||||
|
const { req, res, next } = createServerContext()
|
||||||
|
fresh.mockReturnValue(true)
|
||||||
|
|
||||||
|
await nuxtMiddleware(req, res, next)
|
||||||
|
|
||||||
|
expect(consola.warn).toBeCalledWith('http2.shouldPush is deprecated. Use http2.pushAssets function')
|
||||||
|
expect(context.options.render.http2.shouldPush).toBeCalledTimes(4)
|
||||||
|
expect(res.setHeader).nthCalledWith(1, 'Link', '</nuxt/nuxt/preload1.js>; rel=preload; crossorigin=use-credentials; as=script, </nuxt/nuxt/preload2.js>; rel=modulepreload; crossorigin=use-credentials; as=script')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should add csp header if csp is enabled', async () => {
|
||||||
|
const context = createContext()
|
||||||
|
const result = { html: 'rendered html', cspScriptSrcHashes: ['sha256-hashes'] }
|
||||||
|
context.renderRoute.mockReturnValue(result)
|
||||||
|
context.options.render.csp = true
|
||||||
|
|
||||||
|
const nuxtMiddleware = createNuxtMiddleware(context)
|
||||||
|
const { req, res, next } = createServerContext()
|
||||||
|
fresh.mockReturnValue(true)
|
||||||
|
|
||||||
|
await nuxtMiddleware(req, res, next)
|
||||||
|
|
||||||
|
expect(res.setHeader).nthCalledWith(1, 'Content-Security-Policy', "script-src 'self' sha256-hashes")
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should support allowedSources for setting csp header', async () => {
|
||||||
|
const context = createContext()
|
||||||
|
const result = { html: 'rendered html', cspScriptSrcHashes: ['sha256-hashes'] }
|
||||||
|
context.renderRoute.mockReturnValue(result)
|
||||||
|
context.options.dev = true
|
||||||
|
context.options.render.csp = {
|
||||||
|
reportOnly: true,
|
||||||
|
allowedSources: ['/nuxt/*.js', '/nuxt/images/*']
|
||||||
|
}
|
||||||
|
|
||||||
|
const nuxtMiddleware = createNuxtMiddleware(context)
|
||||||
|
const { req, res, next } = createServerContext()
|
||||||
|
fresh.mockReturnValue(true)
|
||||||
|
|
||||||
|
await nuxtMiddleware(req, res, next)
|
||||||
|
|
||||||
|
expect(res.setHeader).nthCalledWith(
|
||||||
|
1,
|
||||||
|
'Content-Security-Policy-Report-Only',
|
||||||
|
"script-src 'self' 'unsafe-eval' sha256-hashes /nuxt/*.js /nuxt/images/*"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should support policies for setting csp header', async () => {
|
||||||
|
const context = createContext()
|
||||||
|
const result = { html: 'rendered html', cspScriptSrcHashes: ['sha256-hashes'] }
|
||||||
|
context.renderRoute.mockReturnValue(result)
|
||||||
|
context.options.dev = true
|
||||||
|
context.options.render.csp = {
|
||||||
|
policies: {
|
||||||
|
'script-src': [
|
||||||
|
'/nuxt.js',
|
||||||
|
'/test.js'
|
||||||
|
],
|
||||||
|
'report-uri': [
|
||||||
|
'/report'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const nuxtMiddleware = createNuxtMiddleware(context)
|
||||||
|
const { req, res, next } = createServerContext()
|
||||||
|
fresh.mockReturnValue(true)
|
||||||
|
|
||||||
|
await nuxtMiddleware(req, res, next)
|
||||||
|
|
||||||
|
expect(res.setHeader).nthCalledWith(
|
||||||
|
1,
|
||||||
|
'Content-Security-Policy',
|
||||||
|
"script-src sha256-hashes 'self' /nuxt.js /test.js; report-uri /report"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should catch error during running nuxt middleware', async () => {
|
||||||
|
const context = createContext()
|
||||||
|
const err = Error('render error')
|
||||||
|
context.renderRoute.mockImplementation(() => {
|
||||||
|
throw err
|
||||||
|
})
|
||||||
|
const nuxtMiddleware = createNuxtMiddleware(context)
|
||||||
|
const { req, res, next } = createServerContext()
|
||||||
|
fresh.mockReturnValue(true)
|
||||||
|
|
||||||
|
await nuxtMiddleware(req, res, next)
|
||||||
|
|
||||||
|
expect(next).toBeCalledWith(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should log and return error during redirecting in nuxt middleware', async () => {
|
||||||
|
const context = createContext()
|
||||||
|
const err = Error('render error')
|
||||||
|
context.renderRoute.mockImplementation((url, ctx) => {
|
||||||
|
ctx.redirected = true
|
||||||
|
throw err
|
||||||
|
})
|
||||||
|
const nuxtMiddleware = createNuxtMiddleware(context)
|
||||||
|
const { req, res, next } = createServerContext()
|
||||||
|
fresh.mockReturnValue(true)
|
||||||
|
|
||||||
|
expect(await nuxtMiddleware(req, res, next)).toBe(err)
|
||||||
|
expect(consola.error).toBeCalledWith(err)
|
||||||
|
})
|
||||||
|
})
|
103
packages/server/test/middleware/timing.test.js
Normal file
103
packages/server/test/middleware/timing.test.js
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import consola from 'consola'
|
||||||
|
import onHeaders from 'on-headers'
|
||||||
|
import { Timer } from '@nuxt/utils'
|
||||||
|
|
||||||
|
import createTimingMiddleware from '../../src/middleware/timing'
|
||||||
|
|
||||||
|
jest.mock('on-headers')
|
||||||
|
jest.mock('@nuxt/utils')
|
||||||
|
|
||||||
|
const createServerContext = () => ({
|
||||||
|
req: {},
|
||||||
|
res: {
|
||||||
|
getHeader: jest.fn(),
|
||||||
|
setHeader: jest.fn()
|
||||||
|
},
|
||||||
|
next: jest.fn()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('server: timingMiddleware', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should return timing middleware', () => {
|
||||||
|
const timingMiddleware = createTimingMiddleware({})
|
||||||
|
expect(timingMiddleware).toBeInstanceOf(Function)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should warn duplicate registration', () => {
|
||||||
|
const timingMiddleware = createTimingMiddleware({})
|
||||||
|
const ctx = createServerContext()
|
||||||
|
|
||||||
|
ctx.res.timing = true
|
||||||
|
timingMiddleware(ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
expect(consola.warn).toBeCalledWith('server-timing is already registered.')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should register timer for recording timing', () => {
|
||||||
|
const timingMiddleware = createTimingMiddleware({ total: true })
|
||||||
|
const ctx = createServerContext()
|
||||||
|
|
||||||
|
timingMiddleware(ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
expect(ctx.res.timing).toBeDefined()
|
||||||
|
expect(Timer.prototype.start).toBeCalledTimes(1)
|
||||||
|
expect(Timer.prototype.start).toBeCalledWith('total', 'Nuxt Server Time')
|
||||||
|
expect(onHeaders).toBeCalledTimes(1)
|
||||||
|
expect(onHeaders).toBeCalledWith(ctx.res, expect.any(Function))
|
||||||
|
expect(ctx.next).toBeCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should add Server-Timing header before sending header', () => {
|
||||||
|
const timingMiddleware = createTimingMiddleware({ total: true })
|
||||||
|
const ctx = createServerContext()
|
||||||
|
|
||||||
|
timingMiddleware(ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
const headerCallback = onHeaders.mock.calls[0][1]
|
||||||
|
Timer.prototype.end.mockReturnValueOnce({
|
||||||
|
name: 'total',
|
||||||
|
duration: 300,
|
||||||
|
description: 'Nuxt Server Time'
|
||||||
|
})
|
||||||
|
headerCallback()
|
||||||
|
|
||||||
|
expect(ctx.res.timing.end).toBeCalledTimes(1)
|
||||||
|
expect(ctx.res.timing.end).toBeCalledWith('total')
|
||||||
|
expect(ctx.res.getHeader).toBeCalledTimes(1)
|
||||||
|
expect(ctx.res.getHeader).toBeCalledWith('Server-Timing')
|
||||||
|
expect(ctx.res.setHeader).toBeCalledTimes(1)
|
||||||
|
expect(ctx.res.setHeader).toBeCalledWith('Server-Timing', 'total;dur=300;desc="Nuxt Server Time"')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should ignore desc if empty', () => {
|
||||||
|
const timingMiddleware = createTimingMiddleware({})
|
||||||
|
const ctx = createServerContext()
|
||||||
|
|
||||||
|
timingMiddleware(ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
ctx.res.timing.formatHeader({
|
||||||
|
name: 'timing-test',
|
||||||
|
duration: 300
|
||||||
|
})
|
||||||
|
).toEqual('timing-test;dur=300')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should not send Server-Timing header if empty', () => {
|
||||||
|
const timingMiddleware = createTimingMiddleware({ total: true })
|
||||||
|
const ctx = createServerContext()
|
||||||
|
|
||||||
|
timingMiddleware(ctx.req, ctx.res, ctx.next)
|
||||||
|
|
||||||
|
const headerCallback = onHeaders.mock.calls[0][1]
|
||||||
|
headerCallback()
|
||||||
|
|
||||||
|
expect(ctx.res.timing.end).toBeCalledTimes(1)
|
||||||
|
expect(ctx.res.timing.end).toBeCalledWith('total')
|
||||||
|
expect(ctx.res.getHeader).not.toBeCalled()
|
||||||
|
expect(ctx.res.setHeader).not.toBeCalled()
|
||||||
|
})
|
||||||
|
})
|
598
packages/server/test/server.test.js
Normal file
598
packages/server/test/server.test.js
Normal file
@ -0,0 +1,598 @@
|
|||||||
|
import path from 'path'
|
||||||
|
import connect from 'connect'
|
||||||
|
import consola from 'consola'
|
||||||
|
import serveStatic from 'serve-static'
|
||||||
|
import servePlaceholder from 'serve-placeholder'
|
||||||
|
import launchMiddleware from 'launch-editor-middleware'
|
||||||
|
import { determineGlobals, isUrl } from '@nuxt/utils'
|
||||||
|
import { VueRenderer } from '@nuxt/vue-renderer'
|
||||||
|
|
||||||
|
import Server from '../src/server'
|
||||||
|
import Listener from '../src/listener'
|
||||||
|
import ServerContext from '../src/context'
|
||||||
|
import renderAndGetWindow from '../src/jsdom'
|
||||||
|
import nuxtMiddleware from '../src/middleware/nuxt'
|
||||||
|
import errorMiddleware from '../src/middleware/error'
|
||||||
|
import createModernMiddleware from '../src/middleware/modern'
|
||||||
|
import createTimingMiddleware from '../src/middleware/timing'
|
||||||
|
|
||||||
|
jest.mock('connect')
|
||||||
|
jest.mock('serve-static')
|
||||||
|
jest.mock('serve-placeholder')
|
||||||
|
jest.mock('launch-editor-middleware')
|
||||||
|
jest.mock('@nuxt/utils')
|
||||||
|
jest.mock('@nuxt/vue-renderer')
|
||||||
|
jest.mock('../src/listener')
|
||||||
|
jest.mock('../src/context')
|
||||||
|
jest.mock('../src/jsdom')
|
||||||
|
jest.mock('../src/middleware/nuxt')
|
||||||
|
jest.mock('../src/middleware/error')
|
||||||
|
jest.mock('../src/middleware/modern')
|
||||||
|
jest.mock('../src/middleware/timing')
|
||||||
|
|
||||||
|
describe('server: server', () => {
|
||||||
|
const createNuxt = () => ({
|
||||||
|
options: {
|
||||||
|
dir: {
|
||||||
|
static: 'var/nuxt/static'
|
||||||
|
},
|
||||||
|
srcDir: '/var/nuxt/src',
|
||||||
|
buildDir: '/var/nuxt/build',
|
||||||
|
globalName: 'test-global-name',
|
||||||
|
globals: { id: 'test-globals' },
|
||||||
|
build: {
|
||||||
|
publicPath: '__nuxt_test'
|
||||||
|
},
|
||||||
|
render: {
|
||||||
|
id: 'test-render',
|
||||||
|
dist: {
|
||||||
|
id: 'test-render-dist'
|
||||||
|
},
|
||||||
|
static: {
|
||||||
|
id: 'test-render-static',
|
||||||
|
prefix: 'test-render-static-prefix'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
server: {},
|
||||||
|
serverMiddleware: []
|
||||||
|
},
|
||||||
|
hook: jest.fn(),
|
||||||
|
callHook: jest.fn(),
|
||||||
|
resolver: {
|
||||||
|
requireModule: jest.fn()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
jest.spyOn(path, 'join').mockImplementation((...args) => `join(${args.join(', ')})`)
|
||||||
|
jest.spyOn(path, 'resolve').mockImplementation((...args) => `resolve(${args.join(', ')})`)
|
||||||
|
connect.mockReturnValue({ use: jest.fn() })
|
||||||
|
serveStatic.mockImplementation(dir => ({ id: 'test-serve-static', dir }))
|
||||||
|
createModernMiddleware.mockImplementation(options => ({
|
||||||
|
id: 'test-modern-middleware',
|
||||||
|
...options
|
||||||
|
}))
|
||||||
|
nuxtMiddleware.mockImplementation(options => ({
|
||||||
|
id: 'test-nuxt-middleware',
|
||||||
|
...options
|
||||||
|
}))
|
||||||
|
errorMiddleware.mockImplementation(options => ({
|
||||||
|
id: 'test-error-middleware',
|
||||||
|
...options
|
||||||
|
}))
|
||||||
|
createTimingMiddleware.mockImplementation(options => ({
|
||||||
|
id: 'test-timing-middleware',
|
||||||
|
...options
|
||||||
|
}))
|
||||||
|
launchMiddleware.mockImplementation(options => ({
|
||||||
|
id: 'test-open-in-editor-middleware',
|
||||||
|
...options
|
||||||
|
}))
|
||||||
|
servePlaceholder.mockImplementation(options => ({
|
||||||
|
key: 'test-serve-placeholder',
|
||||||
|
...options
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
path.join.mockRestore()
|
||||||
|
path.resolve.mockRestore()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should construct server', () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
determineGlobals.mockReturnValueOnce({
|
||||||
|
...nuxt.options.globals,
|
||||||
|
name: nuxt.options.globalName
|
||||||
|
})
|
||||||
|
let server = new Server(nuxt)
|
||||||
|
|
||||||
|
expect(server.nuxt).toBe(nuxt)
|
||||||
|
expect(server.options).toBe(nuxt.options)
|
||||||
|
expect(server.publicPath).toBe('__nuxt_test')
|
||||||
|
expect(server.resources).toEqual({})
|
||||||
|
expect(server.devMiddleware).toBeNull()
|
||||||
|
expect(server.hotMiddleware).toBeNull()
|
||||||
|
expect(server.listeners).toEqual([])
|
||||||
|
expect(connect).toBeCalledTimes(1)
|
||||||
|
expect(server.nuxt.hook).toBeCalledTimes(1)
|
||||||
|
expect(server.nuxt.hook).toBeCalledWith('close', expect.any(Function))
|
||||||
|
|
||||||
|
const closeHook = server.nuxt.hook.mock.calls[0][1]
|
||||||
|
server.close = jest.fn()
|
||||||
|
expect(server.close).not.toBeCalled()
|
||||||
|
closeHook()
|
||||||
|
expect(server.close).toBeCalledTimes(1)
|
||||||
|
|
||||||
|
nuxt.options.build._publicPath = 'http://localhost:3000/test'
|
||||||
|
isUrl.mockReturnValueOnce(true)
|
||||||
|
server = new Server(nuxt)
|
||||||
|
expect(server.publicPath).toBe(nuxt.options.build._publicPath)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should be ready for listening', async () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
const renderer = {
|
||||||
|
ready: jest.fn()
|
||||||
|
}
|
||||||
|
const context = jest.fn()
|
||||||
|
VueRenderer.mockImplementationOnce(() => renderer)
|
||||||
|
ServerContext.mockImplementationOnce(() => context)
|
||||||
|
server.setupMiddleware = jest.fn()
|
||||||
|
path.join.mockRestore()
|
||||||
|
path.resolve.mockRestore()
|
||||||
|
|
||||||
|
await server.ready()
|
||||||
|
|
||||||
|
expect(server.nuxt.callHook).toBeCalledTimes(2)
|
||||||
|
expect(server.nuxt.callHook).nthCalledWith(1, 'render:before', server, server.options.render)
|
||||||
|
expect(server.nuxt.callHook).nthCalledWith(2, 'render:done', server)
|
||||||
|
expect(ServerContext).toBeCalledTimes(1)
|
||||||
|
expect(ServerContext).toBeCalledWith(server)
|
||||||
|
expect(VueRenderer).toBeCalledTimes(1)
|
||||||
|
expect(VueRenderer).toBeCalledWith(context)
|
||||||
|
expect(server.renderer).toBe(renderer)
|
||||||
|
expect(renderer.ready).toBeCalledTimes(1)
|
||||||
|
expect(server.setupMiddleware).toBeCalledTimes(1)
|
||||||
|
|
||||||
|
jest.spyOn(path, 'join').mockImplementation((...args) => `join(${args.join(', ')})`)
|
||||||
|
jest.spyOn(path, 'resolve').mockImplementation((...args) => `resolve(${args.join(', ')})`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should setup middleware', async () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
server.useMiddleware = jest.fn()
|
||||||
|
server.renderer = {
|
||||||
|
context: { id: 'test-server-context' }
|
||||||
|
}
|
||||||
|
|
||||||
|
await server.setupMiddleware()
|
||||||
|
|
||||||
|
expect(server.nuxt.callHook).toBeCalledTimes(2)
|
||||||
|
expect(server.nuxt.callHook).nthCalledWith(1, 'render:setupMiddleware', server.app)
|
||||||
|
expect(server.nuxt.callHook).nthCalledWith(2, 'render:errorMiddleware', server.app)
|
||||||
|
|
||||||
|
expect(server.useMiddleware).toBeCalledTimes(5)
|
||||||
|
expect(serveStatic).toBeCalledTimes(2)
|
||||||
|
expect(serveStatic).nthCalledWith(1, 'resolve(/var/nuxt/src, var/nuxt/static)', server.options.render.static)
|
||||||
|
expect(server.useMiddleware).nthCalledWith(1, {
|
||||||
|
dir: 'resolve(/var/nuxt/src, var/nuxt/static)',
|
||||||
|
id: 'test-serve-static',
|
||||||
|
prefix: 'test-render-static-prefix'
|
||||||
|
})
|
||||||
|
expect(serveStatic).nthCalledWith(2, 'resolve(/var/nuxt/build, dist, client)', server.options.render.dist)
|
||||||
|
expect(server.useMiddleware).nthCalledWith(2, {
|
||||||
|
handler: {
|
||||||
|
dir: 'resolve(/var/nuxt/build, dist, client)',
|
||||||
|
id: 'test-serve-static'
|
||||||
|
},
|
||||||
|
path: '__nuxt_test'
|
||||||
|
})
|
||||||
|
|
||||||
|
const modernMiddleware = {
|
||||||
|
context: server.renderer.context
|
||||||
|
}
|
||||||
|
expect(createModernMiddleware).toBeCalledTimes(1)
|
||||||
|
expect(createModernMiddleware).toBeCalledWith(modernMiddleware)
|
||||||
|
expect(server.useMiddleware).nthCalledWith(3, {
|
||||||
|
id: 'test-modern-middleware',
|
||||||
|
...modernMiddleware
|
||||||
|
})
|
||||||
|
|
||||||
|
const nuxtMiddlewareOpts = {
|
||||||
|
options: server.options,
|
||||||
|
nuxt: server.nuxt,
|
||||||
|
renderRoute: expect.any(Function),
|
||||||
|
resources: server.resources
|
||||||
|
}
|
||||||
|
expect(nuxtMiddleware).toBeCalledTimes(1)
|
||||||
|
expect(nuxtMiddleware).toBeCalledWith(nuxtMiddlewareOpts)
|
||||||
|
expect(server.useMiddleware).nthCalledWith(4, {
|
||||||
|
id: 'test-nuxt-middleware',
|
||||||
|
...nuxtMiddlewareOpts
|
||||||
|
})
|
||||||
|
|
||||||
|
const errorMiddlewareOpts = {
|
||||||
|
resources: server.resources,
|
||||||
|
options: server.options
|
||||||
|
}
|
||||||
|
expect(errorMiddleware).toBeCalledTimes(1)
|
||||||
|
expect(errorMiddleware).toBeCalledWith(errorMiddlewareOpts)
|
||||||
|
expect(server.useMiddleware).nthCalledWith(5, {
|
||||||
|
id: 'test-error-middleware',
|
||||||
|
...errorMiddlewareOpts
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should setup compressor middleware', async () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.render.compressor = jest.fn()
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
server.useMiddleware = jest.fn()
|
||||||
|
server.renderer = {
|
||||||
|
context: { id: 'test-server-context' }
|
||||||
|
}
|
||||||
|
|
||||||
|
await server.setupMiddleware()
|
||||||
|
expect(server.useMiddleware).nthCalledWith(1, nuxt.options.render.compressor)
|
||||||
|
|
||||||
|
server.useMiddleware.mockClear()
|
||||||
|
nuxt.options.render.compressor = { id: 'test-render-compressor' }
|
||||||
|
nuxt.resolver.requireModule.mockImplementationOnce(name => jest.fn(options => ({
|
||||||
|
name,
|
||||||
|
...options
|
||||||
|
})))
|
||||||
|
await server.setupMiddleware()
|
||||||
|
expect(nuxt.resolver.requireModule).nthCalledWith(1, 'compression')
|
||||||
|
expect(server.useMiddleware).nthCalledWith(1, {
|
||||||
|
id: 'test-render-compressor',
|
||||||
|
name: 'compression'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should setup timing middleware', async () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.server.timing = { id: 'test-server-timing' }
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
server.useMiddleware = jest.fn()
|
||||||
|
server.renderer = {
|
||||||
|
context: { id: 'test-server-context' }
|
||||||
|
}
|
||||||
|
|
||||||
|
await server.setupMiddleware()
|
||||||
|
|
||||||
|
expect(createTimingMiddleware).nthCalledWith(1, { id: 'test-server-timing' })
|
||||||
|
expect(server.useMiddleware).nthCalledWith(1, { id: 'test-server-timing' })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should setup dev middleware', async () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.dev = true
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
server.useMiddleware = jest.fn()
|
||||||
|
server.renderer = {
|
||||||
|
context: { id: 'test-server-context' }
|
||||||
|
}
|
||||||
|
|
||||||
|
await server.setupMiddleware()
|
||||||
|
|
||||||
|
expect(server.useMiddleware).nthCalledWith(1, {
|
||||||
|
id: 'test-modern-middleware',
|
||||||
|
context: server.renderer.context
|
||||||
|
})
|
||||||
|
|
||||||
|
const devMiddleware = server.useMiddleware.mock.calls[1][0]
|
||||||
|
|
||||||
|
const req = { id: 'req' }
|
||||||
|
const res = { id: 'res' }
|
||||||
|
const next = jest.fn()
|
||||||
|
await devMiddleware(req, res, next)
|
||||||
|
expect(next).toBeCalledTimes(1)
|
||||||
|
|
||||||
|
next.mockClear()
|
||||||
|
server.devMiddleware = { client: jest.fn() }
|
||||||
|
server.hotMiddleware = { client: jest.fn() }
|
||||||
|
await devMiddleware(req, res, next)
|
||||||
|
expect(server.devMiddleware.client).nthCalledWith(1, req, res)
|
||||||
|
expect(server.hotMiddleware.client).nthCalledWith(1, req, res)
|
||||||
|
expect(next).toBeCalledTimes(1)
|
||||||
|
|
||||||
|
next.mockClear()
|
||||||
|
req.modernMode = true
|
||||||
|
server.devMiddleware = { modern: jest.fn() }
|
||||||
|
server.hotMiddleware = { modern: jest.fn() }
|
||||||
|
await devMiddleware(req, res, next)
|
||||||
|
expect(server.devMiddleware.modern).nthCalledWith(1, req, res)
|
||||||
|
expect(server.hotMiddleware.modern).nthCalledWith(1, req, res)
|
||||||
|
expect(next).toBeCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should setup open-in-editor middleware', async () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.dev = true
|
||||||
|
nuxt.options.debug = true
|
||||||
|
nuxt.options.editor = { id: 'test-editor' }
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
server.useMiddleware = jest.fn()
|
||||||
|
server.renderer = {
|
||||||
|
context: { id: 'test-server-context' }
|
||||||
|
}
|
||||||
|
|
||||||
|
await server.setupMiddleware()
|
||||||
|
|
||||||
|
expect(launchMiddleware).toBeCalledTimes(1)
|
||||||
|
expect(launchMiddleware).toBeCalledWith({ id: 'test-editor' })
|
||||||
|
|
||||||
|
expect(server.useMiddleware).nthCalledWith(3, {
|
||||||
|
handler: { id: 'test-editor' },
|
||||||
|
path: '__open-in-editor'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should setup server middleware', async () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.serverMiddleware = [
|
||||||
|
{ id: 'test-server-middleware-1' },
|
||||||
|
{ id: 'test-server-middleware-2' }
|
||||||
|
]
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
server.useMiddleware = jest.fn()
|
||||||
|
server.renderer = {
|
||||||
|
context: { id: 'test-server-context' }
|
||||||
|
}
|
||||||
|
|
||||||
|
await server.setupMiddleware()
|
||||||
|
|
||||||
|
expect(server.useMiddleware).nthCalledWith(4, { id: 'test-server-middleware-1' })
|
||||||
|
expect(server.useMiddleware).nthCalledWith(5, { id: 'test-server-middleware-2' })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should setup fallback middleware', async () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.render.fallback = {
|
||||||
|
dist: { id: 'test-render-fallback-dist' },
|
||||||
|
static: { id: 'test-render-fallback-static' }
|
||||||
|
}
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
server.useMiddleware = jest.fn()
|
||||||
|
server.renderer = {
|
||||||
|
context: { id: 'test-server-context' }
|
||||||
|
}
|
||||||
|
|
||||||
|
await server.setupMiddleware()
|
||||||
|
expect(servePlaceholder).toBeCalledTimes(2)
|
||||||
|
expect(server.useMiddleware).nthCalledWith(4, {
|
||||||
|
handler: {
|
||||||
|
id: 'test-render-fallback-dist',
|
||||||
|
key: 'test-serve-placeholder'
|
||||||
|
},
|
||||||
|
path: '__nuxt_test'
|
||||||
|
})
|
||||||
|
expect(server.useMiddleware).nthCalledWith(5, {
|
||||||
|
handler: {
|
||||||
|
id: 'test-render-fallback-static',
|
||||||
|
key: 'test-serve-placeholder'
|
||||||
|
},
|
||||||
|
path: '/'
|
||||||
|
})
|
||||||
|
|
||||||
|
servePlaceholder.mockClear()
|
||||||
|
nuxt.options.render.fallback = {}
|
||||||
|
await server.setupMiddleware()
|
||||||
|
expect(servePlaceholder).not.toBeCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should use object middleware', () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.router = { base: '/' }
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
const handler = jest.fn()
|
||||||
|
|
||||||
|
server.useMiddleware({
|
||||||
|
handler
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(nuxt.resolver.requireModule).not.toBeCalled()
|
||||||
|
expect(server.app.use).toBeCalledTimes(1)
|
||||||
|
expect(server.app.use).toBeCalledWith(nuxt.options.router.base, handler)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should use function module middleware', () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.router = { base: '/' }
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
const handler = jest.fn()
|
||||||
|
nuxt.resolver.requireModule.mockReturnValueOnce(handler)
|
||||||
|
|
||||||
|
server.useMiddleware('test-middleware')
|
||||||
|
|
||||||
|
expect(nuxt.resolver.requireModule).toBeCalledTimes(1)
|
||||||
|
expect(nuxt.resolver.requireModule).toBeCalledWith('test-middleware')
|
||||||
|
expect(server.app.use).toBeCalledTimes(1)
|
||||||
|
expect(server.app.use).toBeCalledWith(nuxt.options.router.base, handler)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should use object module middleware', () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.router = { base: '/' }
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
const handler = jest.fn()
|
||||||
|
nuxt.resolver.requireModule.mockReturnValueOnce({
|
||||||
|
handler,
|
||||||
|
prefix: false,
|
||||||
|
path: '//middleware'
|
||||||
|
})
|
||||||
|
|
||||||
|
server.useMiddleware('test-middleware')
|
||||||
|
|
||||||
|
expect(nuxt.resolver.requireModule).toBeCalledTimes(1)
|
||||||
|
expect(nuxt.resolver.requireModule).toBeCalledWith('test-middleware')
|
||||||
|
expect(server.app.use).toBeCalledTimes(1)
|
||||||
|
expect(server.app.use).toBeCalledWith('/middleware', handler)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should throw error when module resolves failed', () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.router = { base: '/' }
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
const error = Error('middleware resolves failed')
|
||||||
|
nuxt.resolver.requireModule.mockImplementationOnce(() => {
|
||||||
|
throw error
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(() => server.useMiddleware('test-middleware')).toThrow(error)
|
||||||
|
expect(consola.error).toBeCalledTimes(1)
|
||||||
|
expect(consola.error).toBeCalledWith(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should only log error when module resolves failed in dev mode', () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.dev = true
|
||||||
|
nuxt.options.router = { base: '/' }
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
const error = Error('middleware resolves failed')
|
||||||
|
nuxt.resolver.requireModule.mockImplementationOnce(() => {
|
||||||
|
throw error
|
||||||
|
})
|
||||||
|
|
||||||
|
server.useMiddleware('test-middleware')
|
||||||
|
|
||||||
|
expect(consola.error).toBeCalledTimes(1)
|
||||||
|
expect(consola.error).toBeCalledWith(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should render route via renderer', () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
server.renderer = { renderRoute: jest.fn() }
|
||||||
|
|
||||||
|
server.renderRoute('test-render-route')
|
||||||
|
|
||||||
|
expect(server.renderer.renderRoute).toBeCalledTimes(1)
|
||||||
|
expect(server.renderer.renderRoute).toBeCalledWith('test-render-route')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should load resources via renderer', () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
server.renderer = { loadResources: jest.fn() }
|
||||||
|
|
||||||
|
server.loadResources('test-load-resources')
|
||||||
|
|
||||||
|
expect(server.renderer.loadResources).toBeCalledTimes(1)
|
||||||
|
expect(server.renderer.loadResources).toBeCalledWith('test-load-resources')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should render and get window', () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
const globals = {
|
||||||
|
...nuxt.options.globals,
|
||||||
|
name: nuxt.options.globalName,
|
||||||
|
loadedCallback: jest.fn()
|
||||||
|
}
|
||||||
|
determineGlobals.mockReturnValueOnce(globals)
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
|
||||||
|
server.renderAndGetWindow('/render/window')
|
||||||
|
|
||||||
|
expect(renderAndGetWindow).toBeCalledTimes(1)
|
||||||
|
expect(renderAndGetWindow).toBeCalledWith('/render/window', {}, {
|
||||||
|
loadedCallback: globals.loadedCallback,
|
||||||
|
ssr: nuxt.options.render.ssr,
|
||||||
|
globals: globals
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should listen server', async () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
const listener = {
|
||||||
|
listen: jest.fn(),
|
||||||
|
server: jest.fn()
|
||||||
|
}
|
||||||
|
Listener.mockImplementationOnce(() => {
|
||||||
|
return listener
|
||||||
|
})
|
||||||
|
|
||||||
|
await server.listen(3000, 'localhost', '/var/nuxt/unix.socket')
|
||||||
|
|
||||||
|
expect(Listener).toBeCalledWith({
|
||||||
|
port: 3000,
|
||||||
|
host: 'localhost',
|
||||||
|
socket: '/var/nuxt/unix.socket',
|
||||||
|
https: undefined,
|
||||||
|
app: server.app,
|
||||||
|
dev: server.options.dev
|
||||||
|
})
|
||||||
|
expect(listener.listen).toBeCalledTimes(1)
|
||||||
|
expect(server.listeners).toEqual([ listener ])
|
||||||
|
expect(server.nuxt.callHook).toBeCalledTimes(1)
|
||||||
|
expect(server.nuxt.callHook).toBeCalledWith('listen', listener.server, listener)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should listen server via options.server', async () => {
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
nuxt.options.server = {
|
||||||
|
host: 'localhost',
|
||||||
|
port: '3000',
|
||||||
|
socket: '/var/nuxt/unix.socket',
|
||||||
|
https: true
|
||||||
|
}
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
|
||||||
|
await server.listen()
|
||||||
|
|
||||||
|
expect(Listener).toBeCalledWith({
|
||||||
|
...nuxt.options.server,
|
||||||
|
app: server.app,
|
||||||
|
dev: server.options.dev
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should close server', async () => {
|
||||||
|
const removeAllListeners = jest.fn()
|
||||||
|
connect.mockReturnValueOnce({ use: jest.fn(), removeAllListeners })
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
const listener = { close: jest.fn() }
|
||||||
|
server.listeners = [ listener ]
|
||||||
|
server.renderer = { close: jest.fn() }
|
||||||
|
server.resources = { id: 'test-resources' }
|
||||||
|
|
||||||
|
await server.close()
|
||||||
|
|
||||||
|
expect(server.__closed).toEqual(true)
|
||||||
|
expect(listener.close).toBeCalledTimes(1)
|
||||||
|
expect(server.listeners).toEqual([])
|
||||||
|
expect(server.renderer.close).toBeCalledTimes(1)
|
||||||
|
expect(removeAllListeners).toBeCalledTimes(1)
|
||||||
|
expect(server.app).toBeNull()
|
||||||
|
expect(server.resources).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should prevent closing server multiple times', async () => {
|
||||||
|
const removeAllListeners = jest.fn()
|
||||||
|
connect.mockReturnValueOnce({ use: jest.fn(), removeAllListeners })
|
||||||
|
const nuxt = createNuxt()
|
||||||
|
const server = new Server(nuxt)
|
||||||
|
server.renderer = {}
|
||||||
|
|
||||||
|
await server.close()
|
||||||
|
|
||||||
|
expect(server.__closed).toEqual(true)
|
||||||
|
expect(removeAllListeners).toBeCalledTimes(1)
|
||||||
|
|
||||||
|
removeAllListeners.mockClear()
|
||||||
|
|
||||||
|
await server.close()
|
||||||
|
|
||||||
|
expect(server.__closed).toEqual(true)
|
||||||
|
expect(removeAllListeners).not.toBeCalled()
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user