Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upGitHub is where the world builds software
Millions of developers and companies build, ship, and maintain their software on GitHub — the largest and most advanced development platform in the world.
| 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' | |
| }, | |
| router: { | |
| base: '/foo/' | |
| }, | |
| render: { | |
| id: 'test-render', | |
| dist: { | |
| id: 'test-render-dist' | |
| }, | |
| static: { | |
| id: 'test-render-static', | |
| prefix: 'test-render-static-prefix' | |
| } | |
| }, | |
| server: {}, | |
| serverMiddleware: [] | |
| }, | |
| hook: jest.fn(), | |
| ready: 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.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 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(4, { | |
| 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, | |
| baseURL: '/foo/' | |
| }) | |
| 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, | |
| baseURL: '/foo/' | |
| }) | |
| }) | |
| 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() | |
| }) | |
| }) |