mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-27 08:02:01 +00:00
feat(test): unit tests for core/config module (#4760)
* feat(test): enable tests in packages * fix: wait error hook * test: entry and hookable in core * fix(test): options snapshot in windows * refactor(test): simpilify jest.fn * test: module in core * test: complete module test * test: add test for resolver in core * test: update config snapshot * test: nuxt in core module
This commit is contained in:
parent
deadc48f19
commit
a616c09b22
@ -19,7 +19,7 @@
|
|||||||
"test:fixtures": "jest test/fixtures --forceExit",
|
"test:fixtures": "jest test/fixtures --forceExit",
|
||||||
"test:e2e": "jest -i test/e2e --forceExit",
|
"test:e2e": "jest -i test/e2e --forceExit",
|
||||||
"test:lint": "yarn lint",
|
"test:lint": "yarn lint",
|
||||||
"test:unit": "jest test/unit --forceExit",
|
"test:unit": "jest test/unit packages --forceExit",
|
||||||
"test:types": "tsc -p test/types",
|
"test:types": "tsc -p test/types",
|
||||||
"postinstall": "lerna link && yarn dev",
|
"postinstall": "lerna link && yarn dev",
|
||||||
"release": "./scripts/release"
|
"release": "./scripts/release"
|
||||||
|
@ -8,7 +8,7 @@ describe('cli', () => {
|
|||||||
|
|
||||||
test('calls expected method', async () => {
|
test('calls expected method', async () => {
|
||||||
const mockedCommand = {
|
const mockedCommand = {
|
||||||
run: jest.fn().mockImplementation(() => Promise.resolve({}))
|
run: jest.fn(() => Promise.resolve({}))
|
||||||
}
|
}
|
||||||
getCommand.mockImplementationOnce(() => Promise.resolve(mockedCommand))
|
getCommand.mockImplementationOnce(() => Promise.resolve(mockedCommand))
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import listCommands from '../../src/list'
|
|||||||
import getCommand from '../../src/commands'
|
import getCommand from '../../src/commands'
|
||||||
import { indent, foldLines, colorize } from '../../src/utils/formatting'
|
import { indent, foldLines, colorize } from '../../src/utils/formatting'
|
||||||
|
|
||||||
jest.mock('chalk', () => ({ green: jest.fn().mockImplementation(text => text) }))
|
jest.mock('chalk', () => ({ green: jest.fn(text => text) }))
|
||||||
jest.mock('../../src/commands')
|
jest.mock('../../src/commands')
|
||||||
jest.mock('../../src/utils/formatting')
|
jest.mock('../../src/utils/formatting')
|
||||||
|
|
||||||
|
@ -138,7 +138,6 @@ export function getNuxtConfig(_options) {
|
|||||||
options.build._publicPath = options.build._publicPath.replace(/([^/])$/, '$1/')
|
options.build._publicPath = options.build._publicPath.replace(/([^/])$/, '$1/')
|
||||||
|
|
||||||
// Ignore publicPath on dev
|
// Ignore publicPath on dev
|
||||||
/* istanbul ignore if */
|
|
||||||
if (options.dev && isUrl(options.build.publicPath)) {
|
if (options.dev && isUrl(options.build.publicPath)) {
|
||||||
options.build.publicPath = options.build._publicPath
|
options.build.publicPath = options.build._publicPath
|
||||||
}
|
}
|
||||||
@ -263,7 +262,6 @@ export function getNuxtConfig(_options) {
|
|||||||
defaultsDeep(options, modePreset || options.modes.universal)
|
defaultsDeep(options, modePreset || options.modes.universal)
|
||||||
|
|
||||||
// If no server-side rendering, add appear true transition
|
// If no server-side rendering, add appear true transition
|
||||||
/* istanbul ignore if */
|
|
||||||
if (options.render.ssr === false && options.transition) {
|
if (options.render.ssr === false && options.transition) {
|
||||||
options.transition.appear = true
|
options.transition.appear = true
|
||||||
}
|
}
|
||||||
|
@ -159,6 +159,7 @@ Object {
|
|||||||
"generate": Object {
|
"generate": Object {
|
||||||
"concurrency": 500,
|
"concurrency": 500,
|
||||||
"dir": "/var/nuxt/test/dist",
|
"dir": "/var/nuxt/test/dist",
|
||||||
|
"exclude": Array [],
|
||||||
"fallback": "200.html",
|
"fallback": "200.html",
|
||||||
"interval": 0,
|
"interval": 0,
|
||||||
"routes": Array [],
|
"routes": Array [],
|
||||||
|
@ -145,6 +145,7 @@ Object {
|
|||||||
"generate": Object {
|
"generate": Object {
|
||||||
"concurrency": 500,
|
"concurrency": 500,
|
||||||
"dir": "dist",
|
"dir": "dist",
|
||||||
|
"exclude": Array [],
|
||||||
"fallback": "200.html",
|
"fallback": "200.html",
|
||||||
"interval": 0,
|
"interval": 0,
|
||||||
"routes": Array [],
|
"routes": Array [],
|
||||||
@ -467,6 +468,7 @@ Object {
|
|||||||
"generate": Object {
|
"generate": Object {
|
||||||
"concurrency": 500,
|
"concurrency": 500,
|
||||||
"dir": "dist",
|
"dir": "dist",
|
||||||
|
"exclude": Array [],
|
||||||
"fallback": "200.html",
|
"fallback": "200.html",
|
||||||
"interval": 0,
|
"interval": 0,
|
||||||
"routes": Array [],
|
"routes": Array [],
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { getDefaultNuxtConfig, getNuxtConfig } from '../src'
|
import { getDefaultNuxtConfig, getNuxtConfig } from '../src'
|
||||||
|
|
||||||
jest.mock('../src/options', () => ({
|
jest.mock('../src/options', () => ({
|
||||||
getNuxtConfig: jest.fn().mockReturnValue('nuxt config')
|
getNuxtConfig: jest.fn(() => 'nuxt config')
|
||||||
}))
|
}))
|
||||||
|
|
||||||
jest.mock('../src/config', () => ({
|
jest.mock('../src/config', () => ({
|
||||||
getDefaultNuxtConfig: jest.fn().mockReturnValue('default nuxt config')
|
getDefaultNuxtConfig: jest.fn(() => 'default nuxt config')
|
||||||
}))
|
}))
|
||||||
|
|
||||||
describe('config: entry', () => {
|
describe('config: entry', () => {
|
||||||
|
@ -22,10 +22,12 @@ jest.mock('@nuxt/utils', () => ({
|
|||||||
describe('config: options', () => {
|
describe('config: options', () => {
|
||||||
test('should return default nuxt config', () => {
|
test('should return default nuxt config', () => {
|
||||||
jest.spyOn(process, 'cwd').mockReturnValue('/var/nuxt/test')
|
jest.spyOn(process, 'cwd').mockReturnValue('/var/nuxt/test')
|
||||||
|
jest.spyOn(path, 'resolve').mockImplementation((...args) => args.join('/').replace(/\\+/, '/'))
|
||||||
|
|
||||||
expect(getNuxtConfig({})).toMatchSnapshot()
|
expect(getNuxtConfig({})).toMatchSnapshot()
|
||||||
|
|
||||||
process.cwd.mockRestore()
|
process.cwd.mockRestore()
|
||||||
|
path.resolve.mockRestore()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should prevent duplicate calls with same options', () => {
|
test('should prevent duplicate calls with same options', () => {
|
||||||
@ -93,6 +95,11 @@ describe('config: options', () => {
|
|||||||
expect(render.ssr).toEqual(true)
|
expect(render.ssr).toEqual(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should add appear true in transition when no ssr', () => {
|
||||||
|
const { transition } = getNuxtConfig({ render: { ssr: false } })
|
||||||
|
expect(transition.appear).toEqual(true)
|
||||||
|
})
|
||||||
|
|
||||||
test('should return 404.html as default generate.fallback', () => {
|
test('should return 404.html as default generate.fallback', () => {
|
||||||
const { generate: { fallback } } = getNuxtConfig({ generate: { fallback: true } })
|
const { generate: { fallback } } = getNuxtConfig({ generate: { fallback: true } })
|
||||||
expect(fallback).toEqual('404.html')
|
expect(fallback).toEqual('404.html')
|
||||||
|
@ -28,17 +28,16 @@ export default class ModuleContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addTemplate(template) {
|
addTemplate(template) {
|
||||||
/* istanbul ignore if */
|
|
||||||
if (!template) {
|
if (!template) {
|
||||||
throw new Error('Invalid template:' + JSON.stringify(template))
|
throw new Error('Invalid template: ' + JSON.stringify(template))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate & parse source
|
// Validate & parse source
|
||||||
const src = template.src || template
|
const src = template.src || template
|
||||||
const srcPath = path.parse(src)
|
const srcPath = path.parse(src)
|
||||||
/* istanbul ignore if */
|
|
||||||
if (typeof src !== 'string' || !fs.existsSync(src)) {
|
if (typeof src !== 'string' || !fs.existsSync(src)) {
|
||||||
throw new Error('Template src not found:' + src)
|
throw new Error('Template src not found: ' + src)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate unique and human readable dst filename
|
// Generate unique and human readable dst filename
|
||||||
@ -133,7 +132,6 @@ export default class ModuleContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate handler
|
// Validate handler
|
||||||
/* istanbul ignore if */
|
|
||||||
if (typeof handler !== 'function') {
|
if (typeof handler !== 'function') {
|
||||||
throw new Error('Module should export a function: ' + src)
|
throw new Error('Module should export a function: ' + src)
|
||||||
}
|
}
|
||||||
@ -153,7 +151,6 @@ export default class ModuleContainer {
|
|||||||
if (options === undefined) {
|
if (options === undefined) {
|
||||||
options = {}
|
options = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await handler.call(this, options)
|
const result = await handler.call(this, options)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,6 @@ export default class Nuxt extends Hookable {
|
|||||||
async close(callback) {
|
async close(callback) {
|
||||||
await this.callHook('close', this)
|
await this.callHook('close', this)
|
||||||
|
|
||||||
/* istanbul ignore if */
|
|
||||||
if (typeof callback === 'function') {
|
if (typeof callback === 'function') {
|
||||||
await callback()
|
await callback()
|
||||||
}
|
}
|
||||||
|
162
packages/core/test/hookable.test.js
Normal file
162
packages/core/test/hookable.test.js
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
import consola from 'consola'
|
||||||
|
import Hookable from '../src/hookable'
|
||||||
|
|
||||||
|
describe('core: hookable', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
consola.debug.mockClear()
|
||||||
|
consola.log.mockClear()
|
||||||
|
consola.error.mockClear()
|
||||||
|
consola.fatal.mockClear()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should construct hook object', () => {
|
||||||
|
const hook = new Hookable()
|
||||||
|
|
||||||
|
expect(hook._hooks).toEqual({})
|
||||||
|
expect(hook._deprecatedHooks).toEqual({})
|
||||||
|
expect(hook.hook).toBeInstanceOf(Function)
|
||||||
|
expect(hook.callHook).toBeInstanceOf(Function)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should register hook successfully', () => {
|
||||||
|
const hook = new Hookable()
|
||||||
|
hook.hook('test:hook', () => {})
|
||||||
|
hook.hook('test:hook', () => {})
|
||||||
|
|
||||||
|
expect(hook._hooks['test:hook']).toHaveLength(2)
|
||||||
|
expect(hook._hooks['test:hook']).toBeInstanceOf(Array)
|
||||||
|
expect(hook._hooks['test:hook']).toEqual([expect.any(Function), expect.any(Function)])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should ignore empty hook name', () => {
|
||||||
|
const hook = new Hookable()
|
||||||
|
hook.hook(0, () => {})
|
||||||
|
hook.hook('', () => {})
|
||||||
|
hook.hook(undefined, () => {})
|
||||||
|
|
||||||
|
expect(hook._hooks[0]).toBeUndefined()
|
||||||
|
expect(hook._hooks['']).toBeUndefined()
|
||||||
|
expect(hook._hooks[undefined]).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should ignore non-function hook', () => {
|
||||||
|
const hook = new Hookable()
|
||||||
|
hook.hook('test:hook', '')
|
||||||
|
hook.hook('test:hook', undefined)
|
||||||
|
|
||||||
|
expect(hook._hooks['test:hook']).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should convert and display deprecated hook', () => {
|
||||||
|
const hook = new Hookable()
|
||||||
|
hook._deprecatedHooks['test:hook'] = 'test:before'
|
||||||
|
|
||||||
|
hook.hook('test:hook', () => {})
|
||||||
|
|
||||||
|
expect(consola.warn).toBeCalledWith('test:hook hook has been deprecated, please use test:before')
|
||||||
|
expect(hook._hooks['test:hook']).toBeUndefined()
|
||||||
|
expect(hook._hooks['test:before']).toEqual([expect.any(Function)])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should call registered hook', async () => {
|
||||||
|
const hook = new Hookable()
|
||||||
|
hook.hook('test:hook', () => consola.log('test:hook called'))
|
||||||
|
|
||||||
|
await hook.callHook('test:hook')
|
||||||
|
|
||||||
|
expect(consola.debug).toBeCalledWith('Call test:hook hooks (1)')
|
||||||
|
expect(consola.log).toBeCalledWith('test:hook called')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should ignore unregistered hook', async () => {
|
||||||
|
const hook = new Hookable()
|
||||||
|
|
||||||
|
await hook.callHook('test:hook')
|
||||||
|
|
||||||
|
expect(consola.debug).not.toBeCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should report hook error', async () => {
|
||||||
|
const hook = new Hookable()
|
||||||
|
const error = new Error('Hook Error')
|
||||||
|
hook.hook('test:hook', () => { throw error })
|
||||||
|
|
||||||
|
await hook.callHook('test:hook')
|
||||||
|
|
||||||
|
expect(consola.fatal).toBeCalledWith(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should call error hook', async () => {
|
||||||
|
const hook = new Hookable()
|
||||||
|
const error = new Error('Hook Error')
|
||||||
|
hook.hook('error', jest.fn())
|
||||||
|
hook.hook('test:hook', () => { throw error })
|
||||||
|
|
||||||
|
await hook.callHook('test:hook')
|
||||||
|
|
||||||
|
expect(hook._hooks.error[0]).toBeCalledWith(error)
|
||||||
|
expect(consola.fatal).toBeCalledWith(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should clear registered hooks', () => {
|
||||||
|
const hook = new Hookable()
|
||||||
|
hook.hook('test:hook', () => {})
|
||||||
|
|
||||||
|
expect(hook._hooks['test:hook']).toHaveLength(1)
|
||||||
|
expect(hook._hooks['test:before']).toBeUndefined()
|
||||||
|
|
||||||
|
hook.clearHook('test:hook')
|
||||||
|
hook.clearHook('test:before')
|
||||||
|
|
||||||
|
expect(hook._hooks['test:hook']).toBeUndefined()
|
||||||
|
expect(hook._hooks['test:before']).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should clear all registered hooks', () => {
|
||||||
|
const hook = new Hookable()
|
||||||
|
hook.hook('test:hook', () => {})
|
||||||
|
|
||||||
|
expect(hook._hooks['test:hook']).toHaveLength(1)
|
||||||
|
expect(hook._hooks['test:before']).toBeUndefined()
|
||||||
|
|
||||||
|
hook.clearHooks()
|
||||||
|
|
||||||
|
expect(hook._hooks['test:hook']).toBeUndefined()
|
||||||
|
expect(hook._hooks['test:before']).toBeUndefined()
|
||||||
|
expect(hook._hooks).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should return flat hooks', () => {
|
||||||
|
const hook = new Hookable()
|
||||||
|
const hooks = hook.flatHooks({
|
||||||
|
test: {
|
||||||
|
hook: () => {},
|
||||||
|
before: () => {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(hooks).toEqual({
|
||||||
|
'test:hook': expect.any(Function),
|
||||||
|
'test:before': expect.any(Function)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should add object hooks', () => {
|
||||||
|
const hook = new Hookable()
|
||||||
|
hook.addHooks({
|
||||||
|
test: {
|
||||||
|
hook: () => {},
|
||||||
|
before: () => {},
|
||||||
|
after: null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(hook._hooks).toEqual({
|
||||||
|
'test:hook': expect.any(Array),
|
||||||
|
'test:before': expect.any(Array),
|
||||||
|
'test:after': undefined
|
||||||
|
})
|
||||||
|
expect(hook._hooks['test:hook']).toHaveLength(1)
|
||||||
|
expect(hook._hooks['test:before']).toHaveLength(1)
|
||||||
|
})
|
||||||
|
})
|
17
packages/core/test/index.test.js
Normal file
17
packages/core/test/index.test.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Module, Nuxt, Resolver } from '../src'
|
||||||
|
|
||||||
|
jest.mock('../src/module', () => ({
|
||||||
|
module: true
|
||||||
|
})).mock('../src/nuxt', () => ({
|
||||||
|
nuxt: true
|
||||||
|
})).mock('../src/resolver', () => ({
|
||||||
|
resolver: true
|
||||||
|
}))
|
||||||
|
|
||||||
|
describe('core: entry', () => {
|
||||||
|
test('should export Module, Nuxt and Resolver', () => {
|
||||||
|
expect(Module.module).toEqual(true)
|
||||||
|
expect(Nuxt.nuxt).toEqual(true)
|
||||||
|
expect(Resolver.resolver).toEqual(true)
|
||||||
|
})
|
||||||
|
})
|
389
packages/core/test/module.test.js
Normal file
389
packages/core/test/module.test.js
Normal file
@ -0,0 +1,389 @@
|
|||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
import consola from 'consola'
|
||||||
|
import { chainFn } from '@nuxt/utils'
|
||||||
|
import ModuleContainer from '../src/module'
|
||||||
|
|
||||||
|
jest.mock('fs', () => ({
|
||||||
|
existsSync: Boolean
|
||||||
|
}))
|
||||||
|
|
||||||
|
jest.mock('hash-sum', () => src => `hash(${src})`)
|
||||||
|
|
||||||
|
jest.mock('@nuxt/utils', () => ({
|
||||||
|
...jest.requireActual('@nuxt/utils'),
|
||||||
|
chainFn: jest.fn(() => 'chainedFn')
|
||||||
|
}))
|
||||||
|
|
||||||
|
describe('core: module', () => {
|
||||||
|
const requireModule = jest.fn(src => options => Promise.resolve({ src, options }))
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
consola.debug.mockClear()
|
||||||
|
consola.log.mockClear()
|
||||||
|
consola.warn.mockClear()
|
||||||
|
consola.error.mockClear()
|
||||||
|
consola.fatal.mockClear()
|
||||||
|
chainFn.mockClear()
|
||||||
|
requireModule.mockClear()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should construct module container', () => {
|
||||||
|
const nuxt = jest.fn()
|
||||||
|
nuxt.options = jest.fn()
|
||||||
|
const module = new ModuleContainer(nuxt)
|
||||||
|
|
||||||
|
expect(module.nuxt).toBe(nuxt)
|
||||||
|
expect(module.options).toBe(nuxt.options)
|
||||||
|
expect(module.requiredModules).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should call hooks and addModule when ready', async () => {
|
||||||
|
const nuxt = {
|
||||||
|
options: {
|
||||||
|
modules: [jest.fn(), jest.fn()]
|
||||||
|
},
|
||||||
|
callHook: jest.fn()
|
||||||
|
}
|
||||||
|
const module = new ModuleContainer(nuxt)
|
||||||
|
module.addModule = jest.fn()
|
||||||
|
|
||||||
|
await module.ready()
|
||||||
|
|
||||||
|
expect(nuxt.callHook).toBeCalledTimes(2)
|
||||||
|
expect(nuxt.callHook).nthCalledWith(1, 'modules:before', module, module.options.modules)
|
||||||
|
expect(nuxt.callHook).nthCalledWith(2, 'modules:done', module)
|
||||||
|
|
||||||
|
expect(module.addModule).toBeCalledTimes(2)
|
||||||
|
expect(module.addModule).nthCalledWith(1, module.options.modules[0])
|
||||||
|
expect(module.addModule).nthCalledWith(2, module.options.modules[1])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should display deprecated message for addVendor', () => {
|
||||||
|
new ModuleContainer({}).addVendor()
|
||||||
|
|
||||||
|
expect(consola.warn).toBeCalledTimes(1)
|
||||||
|
expect(consola.warn).toBeCalledWith('addVendor has been deprecated due to webpack4 optimization')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should add string template', () => {
|
||||||
|
const module = new ModuleContainer({
|
||||||
|
options: {
|
||||||
|
build: {
|
||||||
|
templates: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const template = module.addTemplate('/var/nuxt/test')
|
||||||
|
const expected = {
|
||||||
|
'dst': 'nuxt.test.hash(/var/nuxt/test)',
|
||||||
|
'options': undefined,
|
||||||
|
'src': '/var/nuxt/test'
|
||||||
|
}
|
||||||
|
expect(template).toEqual(expected)
|
||||||
|
expect(module.options.build.templates).toEqual([expected])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should add object template', () => {
|
||||||
|
const module = new ModuleContainer({
|
||||||
|
options: {
|
||||||
|
build: {
|
||||||
|
templates: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const template = module.addTemplate({
|
||||||
|
src: '/var/nuxt/test',
|
||||||
|
options: { test: true }
|
||||||
|
})
|
||||||
|
const expected = {
|
||||||
|
'dst': 'nuxt.test.hash(/var/nuxt/test)',
|
||||||
|
'options': { test: true },
|
||||||
|
'src': '/var/nuxt/test'
|
||||||
|
}
|
||||||
|
expect(template).toEqual(expected)
|
||||||
|
expect(module.options.build.templates).toEqual([expected])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should use filename in preference to calculation', () => {
|
||||||
|
const module = new ModuleContainer({
|
||||||
|
options: {
|
||||||
|
build: {
|
||||||
|
templates: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const template = module.addTemplate({
|
||||||
|
src: '/var/nuxt/test',
|
||||||
|
fileName: '/var/nuxt/dist/test'
|
||||||
|
})
|
||||||
|
const expected = {
|
||||||
|
'dst': '/var/nuxt/dist/test',
|
||||||
|
'options': undefined,
|
||||||
|
'src': '/var/nuxt/test'
|
||||||
|
}
|
||||||
|
expect(template).toEqual(expected)
|
||||||
|
expect(module.options.build.templates).toEqual([expected])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should throw error when template invalid', () => {
|
||||||
|
const module = new ModuleContainer({})
|
||||||
|
|
||||||
|
expect(() => module.addTemplate()).toThrow('Invalid template: undefined')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should throw error when template not found', () => {
|
||||||
|
const module = new ModuleContainer({})
|
||||||
|
fs.existsSync = jest.fn(() => false)
|
||||||
|
|
||||||
|
expect(() => module.addTemplate('/var/nuxt/test')).toThrow('Template src not found: /var/nuxt/test')
|
||||||
|
|
||||||
|
fs.existsSync = jest.fn(Boolean)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should add plugin into module', () => {
|
||||||
|
const module = new ModuleContainer({
|
||||||
|
options: {
|
||||||
|
buildDir: '/var/nuxt/build',
|
||||||
|
plugins: []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
module.addTemplate = jest.fn(() => ({ dst: 'nuxt.test.template' }))
|
||||||
|
|
||||||
|
module.addPlugin({ ssr: false, mode: 'client' })
|
||||||
|
|
||||||
|
expect(module.options.plugins).toEqual([{
|
||||||
|
src: path.join('/var/nuxt/build', 'nuxt.test.template'),
|
||||||
|
ssr: false,
|
||||||
|
mode: 'client'
|
||||||
|
}])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should add layout into module', () => {
|
||||||
|
const module = new ModuleContainer({
|
||||||
|
options: {
|
||||||
|
layouts: {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
module.addTemplate = jest.fn(() => ({
|
||||||
|
dst: 'nuxt.test.template',
|
||||||
|
src: '/var/nuxt/src'
|
||||||
|
}))
|
||||||
|
|
||||||
|
module.addLayout({}, 'test-layout')
|
||||||
|
|
||||||
|
expect(module.options.layouts).toEqual({ 'test-layout': './nuxt.test.template' })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should display deprecated message when registration is duplicate', () => {
|
||||||
|
const module = new ModuleContainer({
|
||||||
|
options: {
|
||||||
|
layouts: {
|
||||||
|
'test-layout': 'test.template'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
module.addTemplate = jest.fn(() => ({
|
||||||
|
dst: 'nuxt.test.template',
|
||||||
|
src: '/var/nuxt/src'
|
||||||
|
}))
|
||||||
|
|
||||||
|
module.addLayout({}, 'test-layout')
|
||||||
|
|
||||||
|
expect(consola.warn).toBeCalledTimes(1)
|
||||||
|
expect(consola.warn).toBeCalledWith('Duplicate layout registration, "test-layout" has been registered as "test.template"')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should register error layout at same time', () => {
|
||||||
|
const module = new ModuleContainer({
|
||||||
|
options: {
|
||||||
|
layouts: {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
module.addErrorLayout = jest.fn()
|
||||||
|
module.addTemplate = jest.fn(() => ({
|
||||||
|
dst: 'nuxt.test.template',
|
||||||
|
src: '/var/nuxt/src'
|
||||||
|
}))
|
||||||
|
|
||||||
|
module.addLayout({}, 'error')
|
||||||
|
|
||||||
|
expect(module.options.layouts).toEqual({ 'error': './nuxt.test.template' })
|
||||||
|
expect(module.addErrorLayout).toBeCalledTimes(1)
|
||||||
|
expect(module.addErrorLayout).toBeCalledWith('nuxt.test.template')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should add error layout', () => {
|
||||||
|
const module = new ModuleContainer({
|
||||||
|
options: {
|
||||||
|
rootDir: '/var/nuxt',
|
||||||
|
buildDir: '/var/nuxt/build',
|
||||||
|
layouts: {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
module.addErrorLayout('error.template')
|
||||||
|
|
||||||
|
expect(module.options.ErrorPage).toEqual('~/build/error.template')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should add server middleware', () => {
|
||||||
|
const module = new ModuleContainer({
|
||||||
|
options: {
|
||||||
|
serverMiddleware: []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
module.addServerMiddleware(() => {})
|
||||||
|
module.addServerMiddleware(() => {})
|
||||||
|
|
||||||
|
expect(module.options.serverMiddleware).toHaveLength(2)
|
||||||
|
expect(module.options.serverMiddleware).toEqual([expect.any(Function), expect.any(Function)])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should chain build.extend', () => {
|
||||||
|
const extend = () => {}
|
||||||
|
const module = new ModuleContainer({
|
||||||
|
options: {
|
||||||
|
build: { extend }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const newExtend = () => {}
|
||||||
|
module.extendBuild(newExtend)
|
||||||
|
|
||||||
|
expect(chainFn).toBeCalledTimes(1)
|
||||||
|
expect(chainFn).toBeCalledWith(extend, newExtend)
|
||||||
|
expect(module.options.build.extend).toEqual('chainedFn')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should chain router.extendRoutes', () => {
|
||||||
|
const extendRoutes = () => {}
|
||||||
|
const module = new ModuleContainer({
|
||||||
|
options: {
|
||||||
|
router: { extendRoutes }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const newExtendRoutes = () => {}
|
||||||
|
module.extendRoutes(newExtendRoutes)
|
||||||
|
|
||||||
|
expect(chainFn).toBeCalledTimes(1)
|
||||||
|
expect(chainFn).toBeCalledWith(extendRoutes, newExtendRoutes)
|
||||||
|
expect(module.options.router.extendRoutes).toEqual('chainedFn')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should call addModule when require module', () => {
|
||||||
|
const module = new ModuleContainer({
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
module.addModule = jest.fn()
|
||||||
|
|
||||||
|
const moduleOpts = {}
|
||||||
|
module.requireModule(moduleOpts)
|
||||||
|
|
||||||
|
expect(module.addModule).toBeCalledTimes(1)
|
||||||
|
expect(module.addModule).toBeCalledWith(moduleOpts, true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should add string module', async () => {
|
||||||
|
const module = new ModuleContainer({
|
||||||
|
resolver: { requireModule },
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await module.addModule('moduleTest')
|
||||||
|
|
||||||
|
expect(requireModule).toBeCalledTimes(1)
|
||||||
|
expect(requireModule).toBeCalledWith('moduleTest')
|
||||||
|
expect(module.requiredModules).toEqual({
|
||||||
|
moduleTest: {
|
||||||
|
handler: expect.any(Function),
|
||||||
|
options: undefined,
|
||||||
|
src: 'moduleTest'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
expect(result).toEqual({ src: 'moduleTest', options: {} })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should add array module', async () => {
|
||||||
|
const module = new ModuleContainer({
|
||||||
|
resolver: { requireModule },
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await module.addModule(['moduleTest', { test: true }])
|
||||||
|
|
||||||
|
expect(requireModule).toBeCalledTimes(1)
|
||||||
|
expect(requireModule).toBeCalledWith('moduleTest')
|
||||||
|
expect(module.requiredModules).toEqual({
|
||||||
|
moduleTest: {
|
||||||
|
handler: expect.any(Function),
|
||||||
|
options: {
|
||||||
|
test: true
|
||||||
|
},
|
||||||
|
src: 'moduleTest'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
expect(result).toEqual({ src: 'moduleTest', options: { test: true } })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should add object module', async () => {
|
||||||
|
const module = new ModuleContainer({
|
||||||
|
resolver: { requireModule },
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await module.addModule({
|
||||||
|
src: 'moduleTest',
|
||||||
|
options: { test: true },
|
||||||
|
handler: function objectModule(options) {
|
||||||
|
return Promise.resolve(options)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(requireModule).not.toBeCalled()
|
||||||
|
expect(module.requiredModules).toEqual({
|
||||||
|
objectModule: {
|
||||||
|
handler: expect.any(Function),
|
||||||
|
options: {
|
||||||
|
test: true
|
||||||
|
},
|
||||||
|
src: 'moduleTest'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
expect(result).toEqual({ test: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should throw error when handler is not function', () => {
|
||||||
|
const module = new ModuleContainer({
|
||||||
|
resolver: { requireModule: () => false },
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(module.addModule('moduleTest')).rejects.toThrow('Module should export a function: moduleTest')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should prevent multiple adding when requireOnce is enabled', async () => {
|
||||||
|
const module = new ModuleContainer({
|
||||||
|
resolver: { requireModule },
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
|
||||||
|
const handler = jest.fn(() => true)
|
||||||
|
handler.meta = {
|
||||||
|
name: 'moduleTest'
|
||||||
|
}
|
||||||
|
|
||||||
|
const first = await module.addModule({ handler }, true)
|
||||||
|
const second = await module.addModule({ handler }, true)
|
||||||
|
|
||||||
|
expect(first).toEqual(true)
|
||||||
|
expect(second).toBeUndefined()
|
||||||
|
expect(handler).toBeCalledTimes(1)
|
||||||
|
expect(module.requiredModules.moduleTest).toBeDefined()
|
||||||
|
})
|
||||||
|
})
|
203
packages/core/test/nuxt.test.js
Normal file
203
packages/core/test/nuxt.test.js
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
import consola from 'consola'
|
||||||
|
import { defineAlias } from '@nuxt/utils'
|
||||||
|
import { getNuxtConfig } from '@nuxt/config'
|
||||||
|
import { Server } from '@nuxt/server'
|
||||||
|
|
||||||
|
import Nuxt from '../src/nuxt'
|
||||||
|
import ModuleContainer from '../src/module'
|
||||||
|
import Hookable from '../src/hookable'
|
||||||
|
import Resolver from '../src/resolver'
|
||||||
|
import { version } from '../package.json'
|
||||||
|
|
||||||
|
jest.mock('@nuxt/utils')
|
||||||
|
jest.mock('@nuxt/config', () => ({
|
||||||
|
getNuxtConfig: jest.fn(() => ({}))
|
||||||
|
}))
|
||||||
|
jest.mock('@nuxt/server')
|
||||||
|
|
||||||
|
describe('core: nuxt', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
jest.spyOn(Nuxt.prototype, 'ready').mockImplementation(() => Promise.resolve())
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
if (Nuxt.prototype.ready.mockRestore) {
|
||||||
|
Nuxt.prototype.ready.mockRestore()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should construct nuxt with options', () => {
|
||||||
|
const options = {}
|
||||||
|
const nuxt = new Nuxt(options)
|
||||||
|
|
||||||
|
expect(nuxt).toBeInstanceOf(Hookable)
|
||||||
|
expect(getNuxtConfig).toBeCalledTimes(1)
|
||||||
|
expect(getNuxtConfig).toBeCalledWith(options)
|
||||||
|
|
||||||
|
expect(nuxt.resolver).toBeInstanceOf(Resolver)
|
||||||
|
expect(nuxt.moduleContainer).toBeInstanceOf(ModuleContainer)
|
||||||
|
expect(nuxt.server).toBeInstanceOf(Server)
|
||||||
|
|
||||||
|
expect(nuxt._deprecatedHooks).toEqual({
|
||||||
|
'render:context': 'render:routeContext',
|
||||||
|
'showReady': 'webpack:done'
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(defineAlias).toBeCalledTimes(2)
|
||||||
|
expect(defineAlias).nthCalledWith(1, nuxt, nuxt.server, ['renderRoute', 'renderAndGetWindow', 'listen'])
|
||||||
|
expect(defineAlias).nthCalledWith(2, nuxt, nuxt.resolver, ['resolveAlias', 'resolvePath'])
|
||||||
|
|
||||||
|
expect(nuxt.renderer).toBe(nuxt.server)
|
||||||
|
expect(nuxt.render).toBe(nuxt.server.app)
|
||||||
|
expect(nuxt.showReady).toBeInstanceOf(Function)
|
||||||
|
expect(nuxt.initialized).toEqual(false)
|
||||||
|
|
||||||
|
expect(nuxt.ready).toBeCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should call hook webpack:done in showReady', () => {
|
||||||
|
const nuxt = new Nuxt()
|
||||||
|
nuxt.callHook = jest.fn()
|
||||||
|
|
||||||
|
nuxt.showReady()
|
||||||
|
|
||||||
|
expect(nuxt.callHook).toBeCalledTimes(1)
|
||||||
|
expect(nuxt.callHook).toBeCalledWith('webpack:done')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should display fatal message if ready failed', async () => {
|
||||||
|
const err = new Error('nuxt ready failed')
|
||||||
|
Nuxt.prototype.ready.mockImplementation(() => Promise.reject(err))
|
||||||
|
const nuxt = new Nuxt()
|
||||||
|
|
||||||
|
await nuxt._ready
|
||||||
|
|
||||||
|
expect(consola.fatal).toBeCalledTimes(1)
|
||||||
|
expect(consola.fatal).toBeCalledWith(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should return nuxt version from package.json', () => {
|
||||||
|
expect(Nuxt.version).toEqual(`v${version}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should return nuxt version from global.__NUXT', () => {
|
||||||
|
global.__NUXT = {
|
||||||
|
version: 'latest'
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(Nuxt.version).toEqual('latest')
|
||||||
|
|
||||||
|
delete global.__NUXT
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should call module/server ready in nuxt.ready', async () => {
|
||||||
|
const nuxt = new Nuxt()
|
||||||
|
delete nuxt._ready
|
||||||
|
Nuxt.prototype.ready.mockRestore()
|
||||||
|
|
||||||
|
nuxt.callHook = jest.fn()
|
||||||
|
nuxt.server = { ready: jest.fn() }
|
||||||
|
nuxt.moduleContainer = { ready: jest.fn() }
|
||||||
|
|
||||||
|
const result = await nuxt.ready()
|
||||||
|
|
||||||
|
expect(result).toBe(nuxt)
|
||||||
|
expect(nuxt.moduleContainer.ready).toBeCalledTimes(1)
|
||||||
|
expect(nuxt.server.ready).toBeCalledTimes(1)
|
||||||
|
expect(nuxt.initialized).toEqual(true)
|
||||||
|
expect(nuxt.callHook).toBeCalledTimes(1)
|
||||||
|
expect(nuxt.callHook).toBeCalledWith('ready', nuxt)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should ignore ready when _ready exists', async () => {
|
||||||
|
const nuxt = new Nuxt()
|
||||||
|
Nuxt.prototype.ready.mockRestore()
|
||||||
|
const _ready = nuxt._ready = jest.fn()
|
||||||
|
nuxt.server = { ready: jest.fn() }
|
||||||
|
|
||||||
|
const result = await nuxt.ready()
|
||||||
|
|
||||||
|
expect(result).toBe(_ready)
|
||||||
|
expect(nuxt.server.ready).not.toBeCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should add object hooks', async () => {
|
||||||
|
const hooks = {}
|
||||||
|
getNuxtConfig.mockReturnValueOnce({ hooks })
|
||||||
|
const nuxt = new Nuxt()
|
||||||
|
delete nuxt._ready
|
||||||
|
Nuxt.prototype.ready.mockRestore()
|
||||||
|
|
||||||
|
nuxt.addHooks = jest.fn()
|
||||||
|
nuxt.server = { ready: jest.fn() }
|
||||||
|
nuxt.moduleContainer = { ready: jest.fn() }
|
||||||
|
|
||||||
|
await nuxt.ready()
|
||||||
|
|
||||||
|
expect(nuxt.addHooks).toBeCalledTimes(1)
|
||||||
|
expect(nuxt.addHooks).toBeCalledWith(hooks)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should add function hooks', async () => {
|
||||||
|
const hooks = jest.fn()
|
||||||
|
getNuxtConfig.mockReturnValueOnce({ hooks })
|
||||||
|
const nuxt = new Nuxt()
|
||||||
|
delete nuxt._ready
|
||||||
|
Nuxt.prototype.ready.mockRestore()
|
||||||
|
|
||||||
|
nuxt.addHooks = jest.fn()
|
||||||
|
nuxt.server = { ready: jest.fn() }
|
||||||
|
nuxt.moduleContainer = { ready: jest.fn() }
|
||||||
|
|
||||||
|
await nuxt.ready()
|
||||||
|
|
||||||
|
expect(nuxt.addHooks).not.toBeCalled()
|
||||||
|
expect(hooks).toBeCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should close nuxt with hook triggered', async () => {
|
||||||
|
const nuxt = new Nuxt()
|
||||||
|
nuxt.callHook = jest.fn()
|
||||||
|
nuxt.clearHooks = jest.fn()
|
||||||
|
|
||||||
|
const cb = jest.fn()
|
||||||
|
await nuxt.close(cb)
|
||||||
|
|
||||||
|
expect(cb).toBeCalledTimes(1)
|
||||||
|
expect(nuxt.callHook).toBeCalledTimes(1)
|
||||||
|
expect(nuxt.callHook).toBeCalledWith('close', nuxt)
|
||||||
|
expect(nuxt.clearHooks).toBeCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should ignore non-function callback in close', async () => {
|
||||||
|
const nuxt = new Nuxt()
|
||||||
|
delete nuxt._ready
|
||||||
|
Nuxt.prototype.ready.mockRestore()
|
||||||
|
|
||||||
|
nuxt.callHook = jest.fn()
|
||||||
|
nuxt.server = { ready: jest.fn() }
|
||||||
|
nuxt.moduleContainer = { ready: jest.fn() }
|
||||||
|
|
||||||
|
const result = await nuxt.ready()
|
||||||
|
|
||||||
|
expect(result).toBe(nuxt)
|
||||||
|
expect(nuxt.moduleContainer.ready).toBeCalledTimes(1)
|
||||||
|
expect(nuxt.server.ready).toBeCalledTimes(1)
|
||||||
|
expect(nuxt.initialized).toEqual(true)
|
||||||
|
expect(nuxt.callHook).toBeCalledTimes(1)
|
||||||
|
expect(nuxt.callHook).toBeCalledWith('ready', nuxt)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should ignore non-function callback in close', async () => {
|
||||||
|
const nuxt = new Nuxt()
|
||||||
|
nuxt.callHook = jest.fn()
|
||||||
|
nuxt.clearHooks = jest.fn()
|
||||||
|
|
||||||
|
const cb = {}
|
||||||
|
await nuxt.close(cb)
|
||||||
|
|
||||||
|
expect(nuxt.callHook).toBeCalledTimes(1)
|
||||||
|
expect(nuxt.clearHooks).toBeCalledTimes(1)
|
||||||
|
})
|
||||||
|
})
|
469
packages/core/test/resolver.test.js
Normal file
469
packages/core/test/resolver.test.js
Normal file
@ -0,0 +1,469 @@
|
|||||||
|
import Module from 'module'
|
||||||
|
import path from 'path'
|
||||||
|
import esm from 'esm'
|
||||||
|
import fs from 'fs-extra'
|
||||||
|
import consola from 'consola'
|
||||||
|
import { startsWithRootAlias, startsWithSrcAlias } from '@nuxt/utils'
|
||||||
|
|
||||||
|
import Resolver from '../src/resolver'
|
||||||
|
|
||||||
|
jest.mock('module')
|
||||||
|
jest.mock('path')
|
||||||
|
jest.mock('esm', () => jest.fn(() => jest.fn()))
|
||||||
|
jest.mock('fs-extra')
|
||||||
|
jest.mock('@nuxt/utils')
|
||||||
|
|
||||||
|
describe('core: resolver', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should construct resolver', () => {
|
||||||
|
const nuxt = jest.fn()
|
||||||
|
nuxt.options = jest.fn()
|
||||||
|
const resolver = new Resolver(nuxt)
|
||||||
|
|
||||||
|
expect(resolver.nuxt).toBe(nuxt)
|
||||||
|
expect(resolver.options).toBe(nuxt.options)
|
||||||
|
expect(resolver.resolvePath).toBeInstanceOf(Function)
|
||||||
|
expect(resolver.resolveAlias).toBeInstanceOf(Function)
|
||||||
|
expect(resolver.resolveModule).toBeInstanceOf(Function)
|
||||||
|
expect(resolver.requireModule).toBeInstanceOf(Function)
|
||||||
|
expect(resolver.esm).toEqual(expect.any(Function))
|
||||||
|
expect(esm).toBeCalledTimes(1)
|
||||||
|
expect(esm).toBeCalledWith(expect.any(Object), {})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should call _resolveFilename in resolveModule', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: { modulesDir: '/var/nuxt/node_modules' }
|
||||||
|
})
|
||||||
|
Module._resolveFilename = jest.fn(() => '/var/nuxt/resolver/module')
|
||||||
|
|
||||||
|
const modulePath = resolver.resolveModule('/var/nuxt/resolver')
|
||||||
|
|
||||||
|
expect(modulePath).toEqual('/var/nuxt/resolver/module')
|
||||||
|
expect(Module._resolveFilename).toBeCalledTimes(1)
|
||||||
|
expect(Module._resolveFilename).toBeCalledWith('/var/nuxt/resolver', { paths: '/var/nuxt/node_modules' })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should return undefined when module is not found', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: { modulesDir: '/var/nuxt/node_modules' }
|
||||||
|
})
|
||||||
|
Module._resolveFilename = jest.fn(() => {
|
||||||
|
const err = new Error()
|
||||||
|
err.code = 'MODULE_NOT_FOUND'
|
||||||
|
throw err
|
||||||
|
})
|
||||||
|
|
||||||
|
const modulePath = resolver.resolveModule('/var/nuxt/resolver')
|
||||||
|
|
||||||
|
expect(modulePath).toBeUndefined()
|
||||||
|
expect(Module._resolveFilename).toBeCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should throw error when _resolveFilename failed', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: { modulesDir: '/var/nuxt/node_modules' }
|
||||||
|
})
|
||||||
|
Module._resolveFilename = jest.fn(() => {
|
||||||
|
throw new Error('resolve failed')
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(() => resolver.resolveModule('/var/nuxt/resolver')).toThrow('resolve failed')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should throw error when _resolveFilename failed', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: { modulesDir: '/var/nuxt/node_modules' }
|
||||||
|
})
|
||||||
|
Module._resolveFilename = jest.fn(() => {
|
||||||
|
throw new Error('resolve failed')
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(() => resolver.resolveModule('/var/nuxt/resolver')).toThrow('resolve failed')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should resolve root alias', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: { rootDir: '/var/nuxt' }
|
||||||
|
})
|
||||||
|
startsWithRootAlias.mockReturnValue(true)
|
||||||
|
|
||||||
|
const aliasPath = { substr: jest.fn(p => String(p)) }
|
||||||
|
resolver.resolveAlias(aliasPath)
|
||||||
|
|
||||||
|
expect(path.join).toBeCalledTimes(1)
|
||||||
|
expect(path.join).toBeCalledWith('/var/nuxt', '2')
|
||||||
|
expect(aliasPath.substr).toBeCalledTimes(1)
|
||||||
|
expect(aliasPath.substr).toBeCalledWith(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should resolve src alias', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: { srcDir: '/var/nuxt/src' }
|
||||||
|
})
|
||||||
|
startsWithRootAlias.mockReturnValue(false)
|
||||||
|
startsWithSrcAlias.mockReturnValue(true)
|
||||||
|
|
||||||
|
const aliasPath = { substr: jest.fn(p => String(p)) }
|
||||||
|
resolver.resolveAlias(aliasPath)
|
||||||
|
|
||||||
|
expect(path.join).toBeCalledTimes(1)
|
||||||
|
expect(path.join).toBeCalledWith('/var/nuxt/src', '1')
|
||||||
|
expect(aliasPath.substr).toBeCalledTimes(1)
|
||||||
|
expect(aliasPath.substr).toBeCalledWith(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should resolve other alias', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: { srcDir: '/var/nuxt/src' }
|
||||||
|
})
|
||||||
|
startsWithRootAlias.mockReturnValue(false)
|
||||||
|
startsWithSrcAlias.mockReturnValue(false)
|
||||||
|
|
||||||
|
const aliasPath = { substr: jest.fn(p => String(p)) }
|
||||||
|
resolver.resolveAlias(aliasPath)
|
||||||
|
|
||||||
|
expect(path.resolve).toBeCalledTimes(1)
|
||||||
|
expect(path.resolve).toBeCalledWith('/var/nuxt/src', aliasPath)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('core: resolver resolvePath', () => {
|
||||||
|
test('should resolve existed path', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
fs.existsSync = jest.fn(() => true)
|
||||||
|
|
||||||
|
const resolvedPath = resolver.resolvePath('/var/nuxt/resolver/file')
|
||||||
|
|
||||||
|
expect(fs.existsSync).toBeCalledTimes(1)
|
||||||
|
expect(fs.existsSync).toBeCalledWith('/var/nuxt/resolver/file')
|
||||||
|
expect(resolvedPath).toEqual('/var/nuxt/resolver/file')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should resolve a module path', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
fs.existsSync = jest.fn(path => path === '/var/nuxt/resolver/module')
|
||||||
|
fs.lstatSync = jest.fn(() => ({ isDirectory: () => false }))
|
||||||
|
resolver.resolveModule = jest.fn(() => '/var/nuxt/resolver/module')
|
||||||
|
|
||||||
|
const resolvedPath = resolver.resolvePath('/var/nuxt/resolver')
|
||||||
|
|
||||||
|
expect(fs.existsSync).toBeCalledTimes(2)
|
||||||
|
expect(fs.existsSync).nthCalledWith(1, '/var/nuxt/resolver')
|
||||||
|
expect(fs.existsSync).nthCalledWith(2, '/var/nuxt/resolver/module')
|
||||||
|
expect(fs.lstatSync).toBeCalledTimes(1)
|
||||||
|
expect(fs.lstatSync).nthCalledWith(1, '/var/nuxt/resolver/module')
|
||||||
|
expect(resolvedPath).toEqual('/var/nuxt/resolver/module')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should resolve a alias path', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
fs.existsSync = jest.fn(path => path === '/var/nuxt/resolver/alias')
|
||||||
|
fs.lstatSync = jest.fn(() => ({
|
||||||
|
isDirectory: () => false
|
||||||
|
}))
|
||||||
|
resolver.resolveModule = jest.fn(() => false)
|
||||||
|
resolver.resolveAlias = jest.fn(() => '/var/nuxt/resolver/alias')
|
||||||
|
|
||||||
|
const resolvedPath = resolver.resolvePath('/var/nuxt/resolver')
|
||||||
|
|
||||||
|
expect(fs.existsSync).toBeCalledTimes(2)
|
||||||
|
expect(fs.existsSync).nthCalledWith(1, '/var/nuxt/resolver')
|
||||||
|
expect(fs.existsSync).nthCalledWith(2, '/var/nuxt/resolver/alias')
|
||||||
|
expect(fs.lstatSync).toBeCalledTimes(1)
|
||||||
|
expect(fs.lstatSync).nthCalledWith(1, '/var/nuxt/resolver/alias')
|
||||||
|
expect(resolvedPath).toEqual('/var/nuxt/resolver/alias')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should resolve path with extension', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {
|
||||||
|
extensions: ['js']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
fs.existsSync = jest.fn(path => path === '/var/nuxt/resolver/file.js')
|
||||||
|
resolver.resolveModule = jest.fn(() => false)
|
||||||
|
resolver.resolveAlias = jest.fn(() => false)
|
||||||
|
|
||||||
|
const resolvedPath = resolver.resolvePath('/var/nuxt/resolver/file')
|
||||||
|
|
||||||
|
expect(fs.existsSync).toBeCalledTimes(3)
|
||||||
|
expect(fs.existsSync).nthCalledWith(1, '/var/nuxt/resolver/file')
|
||||||
|
expect(fs.existsSync).nthCalledWith(2, '/var/nuxt/resolver/file')
|
||||||
|
expect(fs.existsSync).nthCalledWith(3, '/var/nuxt/resolver/file.js')
|
||||||
|
expect(resolvedPath).toEqual('/var/nuxt/resolver/file.js')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should resolve module path with extension', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {
|
||||||
|
extensions: ['js']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
fs.existsSync = jest.fn(path => path === '/var/nuxt/resolver/module.js')
|
||||||
|
resolver.resolveModule = jest.fn(() => '/var/nuxt/resolver/module')
|
||||||
|
|
||||||
|
const resolvedPath = resolver.resolvePath('/var/nuxt/resolver/file')
|
||||||
|
|
||||||
|
expect(fs.existsSync).toBeCalledTimes(3)
|
||||||
|
expect(fs.existsSync).nthCalledWith(1, '/var/nuxt/resolver/file')
|
||||||
|
expect(fs.existsSync).nthCalledWith(2, '/var/nuxt/resolver/module')
|
||||||
|
expect(fs.existsSync).nthCalledWith(3, '/var/nuxt/resolver/module.js')
|
||||||
|
expect(resolvedPath).toEqual('/var/nuxt/resolver/module.js')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should resolve alias path with extension', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {
|
||||||
|
extensions: ['js']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
fs.existsSync = jest.fn(path => path === '/var/nuxt/resolver/alias.js')
|
||||||
|
resolver.resolveModule = jest.fn(() => false)
|
||||||
|
resolver.resolveAlias = jest.fn(() => '/var/nuxt/resolver/alias')
|
||||||
|
|
||||||
|
const resolvedPath = resolver.resolvePath('/var/nuxt/resolver/file')
|
||||||
|
|
||||||
|
expect(fs.existsSync).toBeCalledTimes(3)
|
||||||
|
expect(fs.existsSync).nthCalledWith(1, '/var/nuxt/resolver/file')
|
||||||
|
expect(fs.existsSync).nthCalledWith(2, '/var/nuxt/resolver/alias')
|
||||||
|
expect(fs.existsSync).nthCalledWith(3, '/var/nuxt/resolver/alias.js')
|
||||||
|
expect(resolvedPath).toEqual('/var/nuxt/resolver/alias.js')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should resolve index.[ext] when path is directory', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {
|
||||||
|
extensions: ['js']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
fs.existsSync = jest.fn(path => ['/var/nuxt/resolver/alias', '/var/nuxt/resolver/alias/index.js'].includes(path))
|
||||||
|
fs.lstatSync = jest.fn(() => ({ isDirectory: () => true }))
|
||||||
|
resolver.resolveModule = jest.fn(() => false)
|
||||||
|
resolver.resolveAlias = jest.fn(() => '/var/nuxt/resolver/alias')
|
||||||
|
|
||||||
|
const resolvedPath = resolver.resolvePath('/var/nuxt/resolver')
|
||||||
|
|
||||||
|
expect(fs.existsSync).toBeCalledTimes(3)
|
||||||
|
expect(fs.existsSync).nthCalledWith(1, '/var/nuxt/resolver')
|
||||||
|
expect(fs.existsSync).nthCalledWith(2, '/var/nuxt/resolver/alias')
|
||||||
|
expect(fs.existsSync).nthCalledWith(3, '/var/nuxt/resolver/alias/index.js')
|
||||||
|
expect(resolvedPath).toEqual('/var/nuxt/resolver/alias/index.js')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should resolve style path', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {
|
||||||
|
extensions: ['js'],
|
||||||
|
styleExtensions: ['css', 'scss']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
fs.existsSync = jest.fn(path => ['/var/nuxt/resolver/alias', '/var/nuxt/resolver/alias/index.scss'].includes(path))
|
||||||
|
fs.lstatSync = jest.fn(path => ({ isDirectory: () => path === '/var/nuxt/resolver/alias' }))
|
||||||
|
resolver.resolveModule = jest.fn(() => false)
|
||||||
|
resolver.resolveAlias = jest.fn(() => '/var/nuxt/resolver/alias')
|
||||||
|
|
||||||
|
const resolvedPath = resolver.resolvePath('/var/nuxt/resolver', { isStyle: true })
|
||||||
|
|
||||||
|
expect(fs.existsSync).toBeCalledTimes(4)
|
||||||
|
expect(fs.existsSync).nthCalledWith(1, '/var/nuxt/resolver')
|
||||||
|
expect(fs.existsSync).nthCalledWith(2, '/var/nuxt/resolver/alias')
|
||||||
|
expect(fs.existsSync).nthCalledWith(3, '/var/nuxt/resolver/alias/index.css')
|
||||||
|
expect(fs.existsSync).nthCalledWith(4, '/var/nuxt/resolver/alias/index.scss')
|
||||||
|
expect(resolvedPath).toEqual('/var/nuxt/resolver/alias/index.scss')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should resolve the directory path if no file', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {
|
||||||
|
extensions: ['js', 'vue']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
fs.existsSync = jest.fn(path => path === '/var/nuxt/resolver/alias')
|
||||||
|
fs.lstatSync = jest.fn(() => ({ isDirectory: () => true }))
|
||||||
|
resolver.resolveModule = jest.fn(() => false)
|
||||||
|
resolver.resolveAlias = jest.fn(() => '/var/nuxt/resolver/alias')
|
||||||
|
|
||||||
|
const resolvedPath = resolver.resolvePath('/var/nuxt/resolver')
|
||||||
|
|
||||||
|
expect(fs.existsSync).toBeCalledTimes(4)
|
||||||
|
expect(fs.existsSync).nthCalledWith(1, '/var/nuxt/resolver')
|
||||||
|
expect(fs.existsSync).nthCalledWith(2, '/var/nuxt/resolver/alias')
|
||||||
|
expect(fs.existsSync).nthCalledWith(3, '/var/nuxt/resolver/alias/index.js')
|
||||||
|
expect(fs.existsSync).nthCalledWith(4, '/var/nuxt/resolver/alias/index.vue')
|
||||||
|
expect(resolvedPath).toEqual('/var/nuxt/resolver/alias')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should throw error if no dir and file', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {
|
||||||
|
extensions: ['js', 'vue']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
fs.existsSync = jest.fn(() => false)
|
||||||
|
fs.lstatSync = jest.fn(() => ({ isDirectory: () => false }))
|
||||||
|
resolver.resolveModule = jest.fn(() => false)
|
||||||
|
resolver.resolveAlias = jest.fn(() => '/var/nuxt/resolver/alias')
|
||||||
|
|
||||||
|
const errMsg = 'Cannot resolve "/var/nuxt/resolver/file" from "/var/nuxt/resolver/alias"'
|
||||||
|
expect(() => resolver.resolvePath('/var/nuxt/resolver/file')).toThrow(errMsg)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should ignore module resolve if isModule is false', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
fs.existsSync = jest.fn(path => path === '/var/nuxt/resolver/alias')
|
||||||
|
resolver.resolveModule = jest.fn(() => '/var/nuxt/resolver/module')
|
||||||
|
resolver.resolveAlias = jest.fn(() => '/var/nuxt/resolver/alias')
|
||||||
|
|
||||||
|
const resolvedPath = resolver.resolvePath('/var/nuxt/resolver/file', { isModule: false })
|
||||||
|
|
||||||
|
expect(fs.existsSync).toBeCalledTimes(2)
|
||||||
|
expect(fs.existsSync).nthCalledWith(1, '/var/nuxt/resolver/file')
|
||||||
|
expect(fs.existsSync).nthCalledWith(2, '/var/nuxt/resolver/alias')
|
||||||
|
expect(resolver.resolveModule).not.toBeCalled()
|
||||||
|
expect(resolvedPath).toEqual('/var/nuxt/resolver/alias')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should display deprecated alias options', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
fs.existsSync = jest.fn(() => true)
|
||||||
|
|
||||||
|
resolver.resolvePath('/var/nuxt/resolver/file', { alias: true })
|
||||||
|
const warnMsg = 'Using alias is deprecated and will be removed in Nuxt 3. Use `isAlias` instead.'
|
||||||
|
expect(consola.warn).toBeCalledTimes(1)
|
||||||
|
expect(consola.warn).toBeCalledWith(warnMsg)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should display deprecated module options', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
fs.existsSync = jest.fn(() => true)
|
||||||
|
|
||||||
|
resolver.resolvePath('/var/nuxt/resolver/file', { module: true })
|
||||||
|
const warnMsg = 'Using module is deprecated and will be removed in Nuxt 3. Use `isModule` instead.'
|
||||||
|
expect(consola.warn).toBeCalledTimes(1)
|
||||||
|
expect(consola.warn).toBeCalledWith(warnMsg)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('core: resolver resolveModule', () => {
|
||||||
|
test('should require es modules with default export', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
resolver.resolvePath = jest.fn()
|
||||||
|
resolver.esm = jest.fn(() => ({ default: 'resolved module' }))
|
||||||
|
|
||||||
|
const resolvedModule = resolver.requireModule('/var/nuxt/resolver/module')
|
||||||
|
|
||||||
|
expect(resolvedModule).toEqual('resolved module')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should require es modules without default export', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
resolver.resolvePath = jest.fn()
|
||||||
|
resolver.esm = jest.fn(() => 'resolved module')
|
||||||
|
|
||||||
|
const resolvedModule = resolver.requireModule('/var/nuxt/resolver/module')
|
||||||
|
|
||||||
|
expect(resolvedModule).toEqual('resolved module')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should require es modules without default export when intropDefault is disabled', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
resolver.resolvePath = jest.fn()
|
||||||
|
resolver.esm = jest.fn(() => ({ default: 'resolved module' }))
|
||||||
|
|
||||||
|
const resolvedModule = resolver.requireModule('/var/nuxt/resolver/module', { intropDefault: false })
|
||||||
|
|
||||||
|
expect(resolvedModule).toEqual({ default: 'resolved module' })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should require common module', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
resolver.resolvePath = jest.fn(() => 'path')
|
||||||
|
resolver.esm = jest.fn(() => ({ default: 'resolved module' }))
|
||||||
|
|
||||||
|
const resolvedModule = resolver.requireModule('path', { esm: false })
|
||||||
|
|
||||||
|
expect(resolvedModule).toBe(path)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should resolve with commonjs for ts module', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
resolver.resolvePath = jest.fn(() => '/var/nuxt/resolver/module.ts')
|
||||||
|
resolver.esm = jest.fn(() => ({ default: 'resolved ts module' }))
|
||||||
|
|
||||||
|
expect(() => resolver.requireModule('/var/nuxt/resolver/module')).toThrow(
|
||||||
|
"Cannot find module '/var/nuxt/resolver/module.ts'"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should throw error if resolvePath failed', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
resolver.resolvePath = jest.fn(() => { throw new Error('resolve failed') })
|
||||||
|
resolver.esm = jest.fn(() => undefined)
|
||||||
|
|
||||||
|
expect(() => resolver.requireModule('/var/nuxt/resolver/module')).toThrow('resolve failed')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should throw last error', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
resolver.resolvePath = jest.fn(() => { throw new Error('resolve failed') })
|
||||||
|
resolver.esm = jest.fn(() => { throw new Error('resolve esm failed') })
|
||||||
|
|
||||||
|
expect(() => resolver.requireModule('/var/nuxt/resolver/module')).toThrow('resolve esm failed')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should display deprecated alias options', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
resolver.resolvePath = jest.fn()
|
||||||
|
resolver.esm = jest.fn()
|
||||||
|
|
||||||
|
resolver.requireModule('/var/nuxt/resolver/file', { alias: true })
|
||||||
|
const warnMsg = 'Using alias is deprecated and will be removed in Nuxt 3. Use `isAlias` instead.'
|
||||||
|
expect(consola.warn).toBeCalledTimes(1)
|
||||||
|
expect(consola.warn).toBeCalledWith(warnMsg)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should display deprecated esm options', () => {
|
||||||
|
const resolver = new Resolver({
|
||||||
|
options: {}
|
||||||
|
})
|
||||||
|
resolver.resolvePath = jest.fn()
|
||||||
|
resolver.esm = jest.fn()
|
||||||
|
|
||||||
|
resolver.requireModule('/var/nuxt/resolver/file', { esm: true })
|
||||||
|
const warnMsg = 'Using esm is deprecated and will be removed in Nuxt 3. Use `useESM` instead.'
|
||||||
|
expect(consola.warn).toBeCalledTimes(1)
|
||||||
|
expect(consola.warn).toBeCalledWith(warnMsg)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user