From 43491f6e530953836fa31bed8ddd425f42477d79 Mon Sep 17 00:00:00 2001 From: "Xin Du (Clark)" Date: Mon, 4 Feb 2019 10:34:04 +0000 Subject: [PATCH] feat(test): unit tests for @nuxt/builder (#4834) --- packages/builder/src/builder.js | 9 +- packages/builder/test/__utils__/index.js | 15 + packages/builder/test/builder.build.test.js | 287 ++++++++ packages/builder/test/builder.common.test.js | 59 ++ packages/builder/test/builder.ctor.test.js | 117 ++++ .../builder/test/builder.generate.test.js | 642 ++++++++++++++++++ packages/builder/test/builder.plugin.test.js | 145 ++++ packages/builder/test/builder.watch.test.js | 330 +++++++++ .../__snapshots__/template.test.js.snap | 105 +++ packages/builder/test/context/build.test.js | 23 + .../builder/test/context/template.test.js | 81 +++ packages/builder/test/ignore.test.js | 147 ++++ packages/builder/test/index.test.js | 9 + 13 files changed, 1963 insertions(+), 6 deletions(-) create mode 100644 packages/builder/test/__utils__/index.js create mode 100644 packages/builder/test/builder.build.test.js create mode 100644 packages/builder/test/builder.common.test.js create mode 100644 packages/builder/test/builder.ctor.test.js create mode 100644 packages/builder/test/builder.generate.test.js create mode 100644 packages/builder/test/builder.plugin.test.js create mode 100644 packages/builder/test/builder.watch.test.js create mode 100644 packages/builder/test/context/__snapshots__/template.test.js.snap create mode 100644 packages/builder/test/context/build.test.js create mode 100644 packages/builder/test/context/template.test.js create mode 100644 packages/builder/test/ignore.test.js create mode 100644 packages/builder/test/index.test.js diff --git a/packages/builder/src/builder.js b/packages/builder/src/builder.js index 7626e3cf5d..76ca642adf 100644 --- a/packages/builder/src/builder.js +++ b/packages/builder/src/builder.js @@ -107,12 +107,10 @@ export default class Builder { async build() { // Avoid calling build() method multiple times when dev:true - /* istanbul ignore if */ if (this._buildStatus === STATUS.BUILD_DONE && this.options.dev) { return this } // If building - /* istanbul ignore if */ if (this._buildStatus === STATUS.BUILDING) { await waitFor(1000) return this.build() @@ -302,7 +300,7 @@ export default class Builder { } async resolveLayouts({ templateVars, templateFiles }) { - if (fsExtra.existsSync(path.resolve(this.options.srcDir, this.options.dir.layouts))) { + if (await fsExtra.exists(path.resolve(this.options.srcDir, this.options.dir.layouts))) { for (const file of await this.resolveFiles(this.options.dir.layouts)) { const name = file .replace(new RegExp(`^${this.options.dir.layouts}/`), '') @@ -467,7 +465,7 @@ export default class Builder { this.options.loadingIndicator.name ) - if (fsExtra.existsSync(indicatorPath)) { + if (await fsExtra.exists(indicatorPath)) { customIndicator = true } else { indicatorPath = null @@ -572,7 +570,7 @@ export default class Builder { } // TODO: Uncomment when generateConfig enabled again - // async generateConfig() /* istanbul ignore next */ { + // async generateConfig() { // const config = path.resolve(this.options.buildDir, 'build.config.js') // const options = omit(this.options, Options.unsafeKeys) // await fsExtra.writeFile( @@ -603,7 +601,6 @@ export default class Builder { patterns = patterns.map(upath.normalizeSafe) const options = this.options.watchers.chokidar - /* istanbul ignore next */ const refreshFiles = debounce(() => this.generateRoutesAndFiles(), 200) // Watch for src Files diff --git a/packages/builder/test/__utils__/index.js b/packages/builder/test/__utils__/index.js new file mode 100644 index 0000000000..054ce5eeda --- /dev/null +++ b/packages/builder/test/__utils__/index.js @@ -0,0 +1,15 @@ +export const createNuxt = () => ({ + options: { + globalName: 'global_name', + globals: [], + build: {}, + router: {} + }, + ready: jest.fn(), + hook: jest.fn(), + callHook: jest.fn(), + resolver: { + requireModule: jest.fn(() => ({ template: 'builder-template' })), + resolveAlias: jest.fn(src => `resolveAlias(${src})`) + } +}) diff --git a/packages/builder/test/builder.build.test.js b/packages/builder/test/builder.build.test.js new file mode 100644 index 0000000000..8c16aea08b --- /dev/null +++ b/packages/builder/test/builder.build.test.js @@ -0,0 +1,287 @@ +import path from 'path' +import consola from 'consola' +import fsExtra from 'fs-extra' +import semver from 'semver' +import { r, waitFor } from '@nuxt/utils' + +import Builder from '../src/builder' +import { createNuxt } from './__utils__' + +jest.mock('fs-extra') +jest.mock('semver') +jest.mock('hash-sum', () => src => `hash(${src})`) +jest.mock('@nuxt/utils') +jest.mock('../src/ignore') + +describe('builder: builder build', () => { + beforeAll(() => { + jest.spyOn(path, 'join').mockImplementation((...args) => `join(${args.join(', ')})`) + }) + + afterAll(() => { + path.join.mockRestore() + }) + + beforeEach(() => { + jest.clearAllMocks() + }) + + test('should build all resources', async () => { + const nuxt = createNuxt() + nuxt.options.srcDir = '/var/nuxt/src' + nuxt.options.buildDir = '/var/nuxt/build' + nuxt.options.dir = { pages: '/var/nuxt/src/pages' } + nuxt.options.build.createRoutes = jest.fn() + + const bundleBuilder = { build: jest.fn() } + const builder = new Builder(nuxt, bundleBuilder) + builder.validatePages = jest.fn() + builder.validateTemplate = jest.fn() + builder.generateRoutesAndFiles = jest.fn() + builder.resolvePlugins = jest.fn() + + r.mockImplementation((dir, src) => `r(${dir})`) + + const buildReturn = await builder.build() + + expect(consola.info).toBeCalledTimes(1) + expect(consola.info).toBeCalledWith('Production build') + expect(nuxt.ready).toBeCalledTimes(1) + expect(nuxt.callHook).toBeCalledTimes(2) + expect(nuxt.callHook).nthCalledWith(1, 'build:before', builder, nuxt.options.build) + expect(builder.validatePages).toBeCalledTimes(1) + expect(builder.validateTemplate).toBeCalledTimes(1) + expect(consola.success).toBeCalledTimes(1) + expect(consola.success).toBeCalledWith('Builder initialized') + expect(consola.debug).toBeCalledTimes(1) + expect(consola.debug).toBeCalledWith('App root: /var/nuxt/src') + expect(fsExtra.remove).toBeCalledTimes(1) + expect(fsExtra.remove).toBeCalledWith('r(/var/nuxt/build)') + expect(fsExtra.mkdirp).toBeCalledTimes(3) + expect(fsExtra.mkdirp).nthCalledWith(1, 'r(/var/nuxt/build)') + expect(fsExtra.mkdirp).nthCalledWith(2, 'r(/var/nuxt/build)') + expect(fsExtra.mkdirp).nthCalledWith(3, 'r(/var/nuxt/build)') + expect(r).toBeCalledTimes(4) + expect(r).nthCalledWith(1, '/var/nuxt/build') + expect(r).nthCalledWith(2, '/var/nuxt/build', 'components') + expect(r).nthCalledWith(3, '/var/nuxt/build', 'dist', 'client') + expect(r).nthCalledWith(4, '/var/nuxt/build', 'dist', 'server') + expect(builder.generateRoutesAndFiles).toBeCalledTimes(1) + expect(builder.resolvePlugins).toBeCalledTimes(1) + expect(bundleBuilder.build).toBeCalledTimes(1) + expect(builder._buildStatus).toEqual(2) + expect(nuxt.callHook).nthCalledWith(2, 'build:done', builder) + expect(buildReturn).toBe(builder) + }) + + test('should prevent duplicate build in dev mode', async () => { + const nuxt = createNuxt() + nuxt.options.dev = true + const builder = new Builder(nuxt, {}) + builder._buildStatus = 3 + + waitFor.mockImplementationOnce(() => { + builder.build = jest.fn(() => 'calling build') + }) + + const buildReturn = await builder.build() + + expect(nuxt.ready).not.toBeCalled() + expect(waitFor).toBeCalledTimes(1) + expect(waitFor).toBeCalledWith(1000) + expect(builder.build).toBeCalledTimes(1) + expect(buildReturn).toBe('calling build') + }) + + test('should wait 1000ms and retry if building is in progress', async () => { + const nuxt = createNuxt() + nuxt.options.dev = true + const builder = new Builder(nuxt, {}) + builder._buildStatus = 2 + + const buildReturn = await builder.build() + + expect(nuxt.ready).not.toBeCalled() + expect(buildReturn).toBe(builder) + }) + + test('should build in dev mode and print dev mode building messages', async () => { + const nuxt = createNuxt() + nuxt.options.dev = true + nuxt.options.srcDir = '/var/nuxt/src' + nuxt.options.buildDir = '/var/nuxt/build' + nuxt.options.dir = { pages: '/var/nuxt/src/pages' } + nuxt.options.build.createRoutes = jest.fn() + + const bundleBuilder = { build: jest.fn() } + const builder = new Builder(nuxt, bundleBuilder) + builder.validatePages = jest.fn() + builder.validateTemplate = jest.fn() + builder.generateRoutesAndFiles = jest.fn() + builder.resolvePlugins = jest.fn() + + await builder.build() + + expect(consola.info).toBeCalledTimes(2) + expect(consola.info).nthCalledWith(1, 'Preparing project for development') + expect(consola.info).nthCalledWith(2, 'Initial build may take a while') + expect(fsExtra.mkdirp).toBeCalledTimes(1) + expect(fsExtra.mkdirp).toBeCalledWith('r(/var/nuxt/build)') + expect(r).toBeCalledTimes(2) + expect(r).nthCalledWith(1, '/var/nuxt/build') + expect(r).nthCalledWith(2, '/var/nuxt/build', 'components') + }) + + test('should throw error when validateTemplate failed', async () => { + const nuxt = createNuxt() + const builder = new Builder(nuxt, {}) + builder.validatePages = jest.fn() + builder.validateTemplate = jest.fn(() => { + throw new Error('validate failed') + }) + consola.success.mockImplementationOnce(() => { + throw new Error('exit') + }) + + await expect(builder.build()).rejects.toThrow('exit') + + expect(builder._buildStatus).toEqual(3) + expect(consola.fatal).toBeCalledTimes(1) + expect(consola.fatal).toBeCalledWith(new Error('validate failed')) + }) + + test('should warn built-in page will be used if no pages dir found', async () => { + const nuxt = createNuxt() + nuxt.options.srcDir = '/var/nuxt/src' + nuxt.options.dir = { pages: '/var/nuxt/src/pages' } + const builder = new Builder(nuxt, {}) + fsExtra.exists.mockReturnValue(false) + + await builder.validatePages() + + expect(builder._nuxtPages).toEqual(true) + expect(path.join).toBeCalledTimes(2) + expect(path.join).nthCalledWith(1, '/var/nuxt/src', '/var/nuxt/src/pages') + expect(path.join).nthCalledWith(2, '/var/nuxt/src', '..', '/var/nuxt/src/pages') + expect(fsExtra.exists).toBeCalledTimes(2) + expect(fsExtra.exists).nthCalledWith(1, 'join(/var/nuxt/src, /var/nuxt/src/pages)') + expect(fsExtra.exists).nthCalledWith(2, 'join(/var/nuxt/src, .., /var/nuxt/src/pages)') + expect(builder._defaultPage).toEqual(true) + expect(consola.warn).toBeCalledTimes(1) + expect(consola.warn).toBeCalledWith('No `/var/nuxt/src/pages` directory found in /var/nuxt/src. Using the default built-in page.') + }) + + test('should throw error if pages is found in parent dir', async () => { + const nuxt = createNuxt() + nuxt.options.srcDir = '/var/nuxt/src' + nuxt.options.dir = { pages: '/var/nuxt/src/pages' } + const builder = new Builder(nuxt, {}) + fsExtra.exists + .mockReturnValueOnce(false) + .mockReturnValueOnce(true) + + await expect(builder.validatePages()).rejects.toThrow( + 'No `/var/nuxt/src/pages` directory found in /var/nuxt/src. Did you mean to run `nuxt` in the parent (`../`) directory?' + ) + + expect(builder._nuxtPages).toEqual(true) + expect(path.join).toBeCalledTimes(2) + expect(path.join).nthCalledWith(1, '/var/nuxt/src', '/var/nuxt/src/pages') + expect(path.join).nthCalledWith(2, '/var/nuxt/src', '..', '/var/nuxt/src/pages') + expect(fsExtra.exists).toBeCalledTimes(2) + expect(fsExtra.exists).nthCalledWith(1, 'join(/var/nuxt/src, /var/nuxt/src/pages)') + expect(fsExtra.exists).nthCalledWith(2, 'join(/var/nuxt/src, .., /var/nuxt/src/pages)') + expect(builder._defaultPage).toBeUndefined() + }) + + test('should pass validation if createRoutes is function', async () => { + const nuxt = createNuxt() + nuxt.options.build.createRoutes = jest.fn() + const builder = new Builder(nuxt, {}) + + await builder.validatePages() + + expect(builder._nuxtPages).toEqual(false) + expect(fsExtra.exists).not.toBeCalled() + }) + + test('should pass validation if pages exists', async () => { + const nuxt = createNuxt() + nuxt.options.srcDir = '/var/nuxt/src' + nuxt.options.dir = { pages: '/var/nuxt/src/pages' } + const builder = new Builder(nuxt, {}) + fsExtra.exists.mockReturnValueOnce(true) + + await builder.validatePages() + + expect(builder._nuxtPages).toEqual(true) + expect(path.join).toBeCalledTimes(1) + expect(path.join).toBeCalledWith('/var/nuxt/src', '/var/nuxt/src/pages') + expect(fsExtra.exists).toBeCalledTimes(1) + expect(fsExtra.exists).toBeCalledWith('join(/var/nuxt/src, /var/nuxt/src/pages)') + expect(builder._defaultPage).toBeUndefined() + }) + + test('should validate dependencies in template', () => { + const nuxt = createNuxt() + nuxt.options.build.template = { + dependencies: { + vue: 'latest', + nuxt: 'edge' + } + } + const builder = new Builder(nuxt, {}) + semver.satisfies + .mockReturnValueOnce(true) + .mockReturnValueOnce(true) + nuxt.resolver.requireModule + .mockReturnValueOnce({ version: 'alpha' }) + .mockReturnValueOnce({ version: 'beta' }) + + builder.validateTemplate() + + expect(nuxt.resolver.requireModule).toBeCalledTimes(2) + expect(nuxt.resolver.requireModule).nthCalledWith(1, 'join(vue, package.json)') + expect(nuxt.resolver.requireModule).nthCalledWith(2, 'join(nuxt, package.json)') + expect(semver.satisfies).toBeCalledTimes(2) + expect(semver.satisfies).nthCalledWith(1, 'alpha', 'latest') + expect(semver.satisfies).nthCalledWith(2, 'beta', 'edge') + }) + + test('should warn and throw error if dependencies is not installed', () => { + const nuxt = createNuxt() + nuxt.options.build.template = { + dependencies: { + vue: 'latest', + nuxt: 'edge' + } + } + const builder = new Builder(nuxt, {}) + semver.satisfies + .mockReturnValueOnce(false) + nuxt.resolver.requireModule + .mockReturnValueOnce({ version: 'alpha' }) + .mockReturnValueOnce(undefined) + + expect(() => builder.validateTemplate()).toThrow('Missing Template Dependencies') + + expect(nuxt.resolver.requireModule).toBeCalledTimes(2) + expect(nuxt.resolver.requireModule).nthCalledWith(1, 'join(vue, package.json)') + expect(nuxt.resolver.requireModule).nthCalledWith(2, 'join(nuxt, package.json)') + expect(consola.warn).toBeCalledTimes(2) + expect(consola.warn).nthCalledWith(1, 'vue@latest is required but vue@alpha is installed!') + expect(consola.warn).nthCalledWith(2, 'nuxt@edge is required but not installed!') + expect(consola.error).toBeCalledTimes(1) + expect(consola.error).toBeCalledWith( + 'Please install missing dependencies:\n', + '\n', + 'Using yarn:\n', + 'yarn add vue@latest nuxt@edge\n', + '\n', + 'Using npm:\n', + 'npm i vue@latest nuxt@edge\n' + ) + expect(semver.satisfies).toBeCalledTimes(1) + expect(semver.satisfies).nthCalledWith(1, 'alpha', 'latest') + }) +}) diff --git a/packages/builder/test/builder.common.test.js b/packages/builder/test/builder.common.test.js new file mode 100644 index 0000000000..2f58289083 --- /dev/null +++ b/packages/builder/test/builder.common.test.js @@ -0,0 +1,59 @@ +import { BundleBuilder } from '@nuxt/webpack' + +import Builder from '../src/builder' +import BuildContext from '../src/context/build' +import { createNuxt } from './__utils__' + +jest.mock('@nuxt/webpack', () => ({ + BundleBuilder: jest.fn(function () { + this.name = 'webpack_builder' + }) +})) +jest.mock('../src/context/build', () => jest.fn(function () { + this.name = 'build_context' +})) +jest.mock('../src/ignore') + +describe('builder: builder common', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + test('should get webpack builder by default', () => { + const builder = new Builder(createNuxt(), {}) + + const bundleBuilder = builder.getBundleBuilder() + + expect(BuildContext).toBeCalledTimes(1) + expect(BuildContext).toBeCalledWith(builder) + expect(BundleBuilder).toBeCalledTimes(1) + expect(BundleBuilder).toBeCalledWith({ name: 'build_context' }) + expect(bundleBuilder).toEqual({ name: 'webpack_builder' }) + }) + + test('should get custom builder from given constructor', () => { + const builder = new Builder(createNuxt(), {}) + + const CustomBundleBuilder = jest.fn(function () { + this.name = 'custom_builder' + }) + const bundleBuilder = builder.getBundleBuilder(CustomBundleBuilder) + + expect(BuildContext).toBeCalledTimes(1) + expect(BuildContext).toBeCalledWith(builder) + expect(CustomBundleBuilder).toBeCalledTimes(1) + expect(CustomBundleBuilder).toBeCalledWith({ name: 'build_context' }) + expect(bundleBuilder).toEqual({ name: 'custom_builder' }) + }) + + test('should call bundleBuilder forGenerate', () => { + const bundleBuilder = { + forGenerate: jest.fn() + } + const builder = new Builder(createNuxt(), bundleBuilder) + + builder.forGenerate() + + expect(bundleBuilder.forGenerate).toBeCalledTimes(1) + }) +}) diff --git a/packages/builder/test/builder.ctor.test.js b/packages/builder/test/builder.ctor.test.js new file mode 100644 index 0000000000..73c9ac6750 --- /dev/null +++ b/packages/builder/test/builder.ctor.test.js @@ -0,0 +1,117 @@ +import consola from 'consola' +import { relativeTo, determineGlobals } from '@nuxt/utils' + +import Builder from '../src/builder' +import { createNuxt } from './__utils__' + +jest.mock('@nuxt/utils') +jest.mock('../src/ignore') + +describe('builder: builder constructor', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + test('should construct builder', () => { + const nuxt = createNuxt() + + const bundleBuilder = {} + determineGlobals.mockReturnValueOnce('__global') + + const builder = new Builder(nuxt, bundleBuilder) + + expect(builder.nuxt).toEqual(nuxt) + expect(builder.plugins).toEqual([]) + expect(builder.options).toEqual(nuxt.options) + + expect(determineGlobals).toBeCalledTimes(1) + expect(determineGlobals).toBeCalledWith(nuxt.options.globalName, nuxt.options.globals) + + expect(builder.watchers).toEqual({ + files: null, + custom: null, + restart: null + }) + expect(builder.supportedExtensions).toEqual(['vue', 'js', 'ts', 'tsx']) + expect(builder.relativeToBuild).toBeInstanceOf(Function) + + expect(builder._buildStatus).toEqual(1) + + expect(nuxt.resolver.requireModule).toBeCalledTimes(1) + expect(nuxt.resolver.requireModule).toBeCalledWith('@nuxt/vue-app') + expect(builder.template).toEqual('builder-template') + + expect(builder.bundleBuilder).toBe(bundleBuilder) + }) + + test('should call relativeTo in relativeToBuild', () => { + const nuxt = createNuxt() + nuxt.options.buildDir = '/var/nuxt/build' + const bundleBuilder = {} + const builder = new Builder(nuxt, bundleBuilder) + + const args = [{}, {}] + builder.relativeToBuild(...args) + + expect(relativeTo).toBeCalledTimes(1) + expect(relativeTo).toBeCalledWith('/var/nuxt/build', ...args) + }) + + test('should add hooks in dev mode', () => { + const nuxt = createNuxt() + nuxt.options.dev = true + + const bundleBuilder = {} + determineGlobals.mockReturnValueOnce('__global') + + const builder = new Builder(nuxt, bundleBuilder) + + expect(builder.options.dev).toEqual(true) + + expect(nuxt.hook).toBeCalledTimes(2) + expect(nuxt.hook).toBeCalledWith('build:done', expect.any(Function)) + expect(nuxt.hook).toBeCalledWith('close', expect.any(Function)) + + const doneHook = nuxt.hook.mock.calls[0][1] + builder.watchClient = jest.fn() + builder.watchRestart = jest.fn() + doneHook() + expect(consola.info).toBeCalledTimes(1) + expect(consola.info).toBeCalledWith('Waiting for file changes') + expect(builder.watchClient).toBeCalledTimes(1) + expect(builder.watchRestart).toBeCalledTimes(1) + + const closeHook = nuxt.hook.mock.calls[1][1] + builder.close = jest.fn() + closeHook() + expect(builder.close).toBeCalledTimes(1) + }) + + test('should add hooks in analyze mode', () => { + const nuxt = createNuxt() + nuxt.options.build.analyze = true + + const bundleBuilder = {} + const builder = new Builder(nuxt, bundleBuilder) + + expect(builder.options.build.analyze).toEqual(true) + + expect(nuxt.hook).toBeCalledTimes(1) + expect(nuxt.hook).toBeCalledWith('build:done', expect.any(Function)) + + const doneHook = nuxt.hook.mock.calls[0][1] + doneHook() + expect(consola.warn).toBeCalledTimes(1) + expect(consola.warn).toBeCalledWith('Notice: Please do not deploy bundles built with analyze mode, it\'s only for analyzing purpose.') + }) + + test('should support function template', () => { + const nuxt = createNuxt() + nuxt.options.build.template = jest.fn() + const bundleBuilder = {} + const builder = new Builder(nuxt, bundleBuilder) + + expect(builder.template).toBe(nuxt.options.build.template) + expect(nuxt.resolver.requireModule).not.toBeCalled() + }) +}) diff --git a/packages/builder/test/builder.generate.test.js b/packages/builder/test/builder.generate.test.js new file mode 100644 index 0000000000..47c68f842a --- /dev/null +++ b/packages/builder/test/builder.generate.test.js @@ -0,0 +1,642 @@ +import path from 'path' +import Glob from 'glob' +import fs from 'fs-extra' +import consola from 'consola' +import template from 'lodash/template' +import { r, createRoutes, stripWhitespace } from '@nuxt/utils' +import Builder from '../src/builder' +import TemplateContext from '../src/context/template' +import { createNuxt } from './__utils__' + +jest.mock('glob') +jest.mock('pify', () => fn => fn) +jest.mock('fs-extra') +jest.mock('lodash/template') +jest.mock('@nuxt/utils') +jest.mock('../src/context/template', () => jest.fn()) +jest.mock('../src/ignore', () => function () { + this.filter = jest.fn(files => files) +}) + +describe('builder: builder generate', () => { + beforeAll(() => { + r.mockImplementation((...args) => `r(${args.join(', ')})`) + fs.readFile.mockImplementation((...args) => `readFile(${args.join(', ')})`) + fs.outputFile.mockImplementation((...args) => `outputFile(${args.join(', ')})`) + jest.spyOn(path, 'join').mockImplementation((...args) => `join(${args.join(', ')})`) + jest.spyOn(path, 'resolve').mockImplementation((...args) => `resolve(${args.join(', ')})`) + }) + + afterAll(() => { + path.join.mockRestore() + path.resolve.mockRestore() + }) + + beforeEach(() => { + jest.clearAllMocks() + }) + + test('should generate routes and files', async () => { + const nuxt = createNuxt() + nuxt.options.build = { + template: { + dir: '/var/nuxt/src/template', + files: [ 'App.js', 'index.js' ] + }, + watch: [] + } + const builder = new Builder(nuxt, {}) + builder.normalizePlugins = jest.fn(() => [{ name: 'test_plugin' }]) + builder.resolveLayouts = jest.fn(() => 'resolveLayouts') + builder.resolveRoutes = jest.fn(() => 'resolveRoutes') + builder.resolveStore = jest.fn(() => 'resolveStore') + builder.resolveMiddleware = jest.fn(() => 'resolveMiddleware') + builder.resolveCustomTemplates = jest.fn() + builder.resolveLoadingIndicator = jest.fn() + builder.compileTemplates = jest.fn() + jest.spyOn(Promise, 'all').mockImplementation(() => {}) + + await builder.generateRoutesAndFiles() + + expect(consola.debug).toBeCalledTimes(1) + expect(consola.debug).toBeCalledWith('Generating nuxt files') + expect(TemplateContext).toBeCalledTimes(1) + expect(TemplateContext).toBeCalledWith(builder, builder.options) + expect(builder.normalizePlugins).toBeCalledTimes(1) + expect(builder.resolveLayouts).toBeCalledTimes(1) + expect(builder.resolveRoutes).toBeCalledTimes(1) + expect(builder.resolveStore).toBeCalledTimes(1) + expect(builder.resolveMiddleware).toBeCalledTimes(1) + expect(Promise.all).toBeCalledTimes(1) + expect(Promise.all).toBeCalledWith([ + 'resolveLayouts', + 'resolveRoutes', + 'resolveStore', + 'resolveMiddleware' + ]) + expect(builder.resolveCustomTemplates).toBeCalledTimes(1) + expect(builder.resolveLoadingIndicator).toBeCalledTimes(1) + expect(builder.options.build.watch).toEqual(['/var/nuxt/src/template']) + expect(builder.compileTemplates).toBeCalledTimes(1) + expect(consola.success).toBeCalledTimes(1) + expect(consola.success).toBeCalledWith('Nuxt files generated') + + Promise.all.mockRestore() + }) + + test('should resolve files', async () => { + const nuxt = createNuxt() + nuxt.options.srcDir = '/var/nuxt/src' + nuxt.options.ignore = '/var/nuxt/ignore' + const builder = new Builder(nuxt, {}) + Glob.mockReturnValue('matched files') + + const files = await builder.resolveFiles('/var/nuxt/dir') + + expect(Glob).toBeCalledTimes(1) + expect(Glob).toBeCalledWith( + '/var/nuxt/dir/**/*.{vue,js,ts,tsx}', + { cwd: '/var/nuxt/src', ignore: '/var/nuxt/ignore' } + ) + expect(builder.ignore.filter).toBeCalledTimes(1) + expect(builder.ignore.filter).toBeCalledWith('matched files') + expect(files).toEqual('matched files') + }) + + test('should resolve relative files', async () => { + const nuxt = createNuxt() + const builder = new Builder(nuxt, {}) + builder.resolveFiles = jest.fn(dir => [ `${dir}/foo.vue`, `${dir}/bar.vue`, `${dir}/baz.vue` ]) + + const files = await builder.resolveRelative('/var/nuxt/dir') + + expect(builder.resolveFiles).toBeCalledTimes(1) + expect(builder.resolveFiles).toBeCalledWith('/var/nuxt/dir') + expect(files).toEqual([ + { src: 'foo.vue' }, + { src: 'bar.vue' }, + { src: 'baz.vue' } + ]) + }) + + test('should resolve store modules', async () => { + const nuxt = createNuxt() + nuxt.options.store = true + nuxt.options.dir = { + store: '/var/nuxt/src/store' + } + const builder = new Builder(nuxt, {}) + builder.resolveRelative = jest.fn(dir => [ + { src: `${dir}/index.js` }, + { src: `${dir}/bar.js` }, + { src: `${dir}/baz.js` }, + { src: `${dir}/foo/bar.js` }, + { src: `${dir}/foo/baz.js` }, + { src: `${dir}/foo/index.js` } + ]) + + const templateVars = {} + const templateFiles = [] + await builder.resolveStore({ templateVars, templateFiles }) + + expect(templateVars.storeModules).toEqual([ + { 'src': '/var/nuxt/src/store/index.js' }, + { 'src': '/var/nuxt/src/store/bar.js' }, + { 'src': '/var/nuxt/src/store/baz.js' }, + { 'src': '/var/nuxt/src/store/foo/index.js' }, + { 'src': '/var/nuxt/src/store/foo/bar.js' }, + { 'src': '/var/nuxt/src/store/foo/baz.js' }] + ) + expect(templateFiles).toEqual(['store.js']) + }) + + test('should disable store resolving', async () => { + const nuxt = createNuxt() + nuxt.options.dir = { + store: '/var/nuxt/src/store' + } + const builder = new Builder(nuxt, {}) + + const templateVars = {} + const templateFiles = [] + await builder.resolveStore({ templateVars, templateFiles }) + + expect(templateVars.storeModules).toBeUndefined() + expect(templateFiles).toEqual([]) + }) + + test('should resolve middleware', async () => { + const nuxt = createNuxt() + nuxt.options.store = false + nuxt.options.dir = { + middleware: '/var/nuxt/src/middleware' + } + const builder = new Builder(nuxt, {}) + builder.resolveRelative = jest.fn(dir => [ + { src: `${dir}/midd.js` } + ]) + + const templateVars = {} + await builder.resolveMiddleware({ templateVars }) + + expect(templateVars.middleware).toEqual([ { src: '/var/nuxt/src/middleware/midd.js' } ]) + }) + + test('should custom templates', async () => { + const nuxt = createNuxt() + nuxt.options.srcDir = '/var/nuxt/src' + nuxt.options.build = { + template: { dir: '/var/nuxt/templates' }, + templates: [ + '/var/nuxt/templates/foo.js', + { src: '/var/nuxt/templates/bar.js' }, + { src: '/var/nuxt/templates/baz.js', dst: 'baz.js' } + ] + } + const builder = new Builder(nuxt, {}) + fs.exists.mockReturnValueOnce(true) + + const templateContext = { + templateFiles: [ + 'foo.js', + 'bar.js', + 'baz.js', + 'router.js', + 'store.js', + 'middleware.js' + ] + } + await builder.resolveCustomTemplates(templateContext) + + expect(templateContext.templateFiles).toEqual([ + { custom: true, dst: 'router.js', src: 'r(/var/nuxt/src, app, router.js)' }, + { custom: undefined, dst: 'store.js', src: 'r(/var/nuxt/templates, store.js)' }, + { custom: undefined, dst: 'middleware.js', src: 'r(/var/nuxt/templates, middleware.js)' }, + { custom: true, dst: 'foo.js', src: 'r(/var/nuxt/src, /var/nuxt/templates/foo.js)' }, + { custom: true, dst: 'bar.js', src: '/var/nuxt/templates/bar.js' }, + { custom: true, dst: 'baz.js', src: '/var/nuxt/templates/baz.js' } + ]) + }) + + test('should resolve loading indicator', async () => { + const nuxt = createNuxt() + nuxt.options.loadingIndicator = { + name: 'test_loading_indicator' + } + nuxt.options.build = { + template: { dir: '/var/nuxt/templates' } + } + const builder = new Builder(nuxt, {}) + fs.exists.mockReturnValueOnce(true) + + const templateFiles = [] + await builder.resolveLoadingIndicator({ templateFiles }) + + expect(path.resolve).toBeCalledTimes(1) + expect(path.resolve).toBeCalledWith('/var/nuxt/templates', 'views/loading', 'test_loading_indicator.html') + expect(fs.exists).toBeCalledTimes(1) + expect(fs.exists).toBeCalledWith('resolve(/var/nuxt/templates, views/loading, test_loading_indicator.html)') + expect(templateFiles).toEqual([{ + custom: false, + dst: 'loading.html', + options: { name: 'test_loading_indicator' }, + src: 'resolve(/var/nuxt/templates, views/loading, test_loading_indicator.html)' + }]) + }) + + test('should resolve alias loading indicator', async () => { + const nuxt = createNuxt() + nuxt.options.loadingIndicator = { + name: '@/app/template.vue' + } + nuxt.options.build = { + template: { dir: '/var/nuxt/templates' } + } + const builder = new Builder(nuxt, {}) + fs.exists + .mockReturnValueOnce(false) + .mockReturnValueOnce(true) + + const templateFiles = [] + await builder.resolveLoadingIndicator({ templateFiles }) + + expect(path.resolve).toBeCalledTimes(1) + expect(path.resolve).toBeCalledWith('/var/nuxt/templates', 'views/loading', '@/app/template.vue.html') + expect(fs.exists).toBeCalledTimes(2) + expect(fs.exists).nthCalledWith(1, 'resolve(/var/nuxt/templates, views/loading, @/app/template.vue.html)') + expect(fs.exists).nthCalledWith(2, 'resolveAlias(@/app/template.vue)') + expect(templateFiles).toEqual([{ + custom: true, + dst: 'loading.html', + options: { name: '@/app/template.vue' }, + src: 'resolveAlias(@/app/template.vue)' + }]) + }) + + test('should display error if three is not loading indicator', async () => { + const nuxt = createNuxt() + nuxt.options.loadingIndicator = { + name: '@/app/empty.vue' + } + nuxt.options.build = { + template: { dir: '/var/nuxt/templates' } + } + const builder = new Builder(nuxt, {}) + fs.exists + .mockReturnValueOnce(false) + .mockReturnValueOnce(false) + + const templateFiles = [] + await builder.resolveLoadingIndicator({ templateFiles }) + + expect(path.resolve).toBeCalledTimes(1) + expect(path.resolve).toBeCalledWith('/var/nuxt/templates', 'views/loading', '@/app/empty.vue.html') + expect(fs.exists).toBeCalledTimes(2) + expect(fs.exists).nthCalledWith(1, 'resolve(/var/nuxt/templates, views/loading, @/app/empty.vue.html)') + expect(fs.exists).nthCalledWith(2, 'resolveAlias(@/app/empty.vue)') + expect(consola.error).toBeCalledTimes(1) + expect(consola.error).toBeCalledWith('Could not fetch loading indicator: @/app/empty.vue') + expect(templateFiles).toEqual([]) + }) + + test('should disable loading indicator', async () => { + const nuxt = createNuxt() + nuxt.options.loadingIndicator = { + name: false + } + const builder = new Builder(nuxt, {}) + + await builder.resolveLoadingIndicator({ templateFiles: [] }) + + expect(path.resolve).not.toBeCalled() + }) + + test('should compile templates', async () => { + const nuxt = createNuxt() + nuxt.options.build.watch = [] + nuxt.options.buildDir = '/var/nuxt/build' + const builder = new Builder(nuxt, {}) + builder.relativeToBuild = jest.fn() + const templateFn = jest.fn(() => 'compiled content') + template.mockImplementation(() => templateFn) + stripWhitespace.mockImplementation(content => `trim(${content})`) + + const templateContext = { + templateVars: { test: 'template_vars' }, + templateFiles: [ + { src: '/var/nuxt/src/foo.js', dst: 'foo.js', options: { foo: true } }, + { src: '/var/nuxt/src/bar.js', dst: 'bar.js', options: { bar: true } }, + { src: '/var/nuxt/src/baz.js', dst: 'baz.js', custom: true } + ], + templateOptions: {} + } + await builder.compileTemplates(templateContext) + + expect(nuxt.callHook).toBeCalledTimes(1) + expect(nuxt.callHook).toBeCalledWith('build:templates', { + templateVars: templateContext.templateVars, + templatesFiles: templateContext.templateFiles, + resolve: r + }) + expect(templateContext.templateOptions.imports).toEqual({ + resolvePath: nuxt.resolver.resolvePath, + resolveAlias: nuxt.resolver.resolveAlias, + relativeToBuild: builder.relativeToBuild + }) + expect(nuxt.options.build.watch).toEqual([ '/var/nuxt/src/baz.js' ]) + expect(fs.readFile).toBeCalledTimes(3) + expect(fs.readFile).nthCalledWith(1, '/var/nuxt/src/foo.js', 'utf8') + expect(fs.readFile).nthCalledWith(2, '/var/nuxt/src/bar.js', 'utf8') + expect(fs.readFile).nthCalledWith(3, '/var/nuxt/src/baz.js', 'utf8') + expect(template).toBeCalledTimes(3) + expect(template).nthCalledWith(1, 'readFile(/var/nuxt/src/foo.js, utf8)', templateContext.templateOptions) + expect(template).nthCalledWith(2, 'readFile(/var/nuxt/src/bar.js, utf8)', templateContext.templateOptions) + expect(template).nthCalledWith(3, 'readFile(/var/nuxt/src/baz.js, utf8)', templateContext.templateOptions) + expect(templateFn).toBeCalledTimes(3) + expect(templateFn).nthCalledWith(1, { + ...templateContext.templateVars, + custom: undefined, + dst: 'foo.js', + src: '/var/nuxt/src/foo.js', + options: { foo: true } + }) + expect(templateFn).nthCalledWith(2, { + ...templateContext.templateVars, + custom: undefined, + dst: 'bar.js', + src: '/var/nuxt/src/bar.js', + options: { bar: true } + }) + expect(templateFn).nthCalledWith(3, { + ...templateContext.templateVars, + custom: true, + dst: 'baz.js', + src: '/var/nuxt/src/baz.js', + options: {} + }) + expect(stripWhitespace).toBeCalledTimes(3) + expect(stripWhitespace).nthCalledWith(1, 'compiled content') + expect(stripWhitespace).nthCalledWith(2, 'compiled content') + expect(stripWhitespace).nthCalledWith(3, 'compiled content') + expect(fs.outputFile).toBeCalledTimes(3) + expect(fs.outputFile).nthCalledWith(1, 'r(/var/nuxt/build, foo.js)', 'trim(compiled content)', 'utf8') + expect(fs.outputFile).nthCalledWith(2, 'r(/var/nuxt/build, bar.js)', 'trim(compiled content)', 'utf8') + expect(fs.outputFile).nthCalledWith(3, 'r(/var/nuxt/build, baz.js)', 'trim(compiled content)', 'utf8') + }) + + test('should throw error if compile failed', async () => { + const nuxt = createNuxt() + const builder = new Builder(nuxt, {}) + builder.relativeToBuild = jest.fn() + template.mockImplementation(() => { + throw new Error('compile failed') + }) + + const templateContext = { + templateVars: { test: 'template_vars' }, + templateFiles: [ { src: '/var/nuxt/src/foo.js' } ], + templateOptions: {} + } + await expect(builder.compileTemplates(templateContext)).rejects.toThrow( + 'Could not compile template /var/nuxt/src/foo.js: compile failed' + ) + }) + + describe('builder: builder resolveLayouts', () => { + test('should resolve layouts', async () => { + const nuxt = createNuxt() + nuxt.options.srcDir = '/var/nuxt/src' + nuxt.options.buildDir = '/var/nuxt/build' + nuxt.options.dir = { + layouts: '/var/nuxt/src/layouts' + } + nuxt.options.layouts = { + foo: '/var/nuxt/layouts/foo/index.vue' + } + const builder = new Builder(nuxt, {}) + builder.resolveFiles = jest.fn(layouts => [ + `${layouts}/foo.vue`, + `${layouts}/bar.js`, + `${layouts}/baz.vue`, + `${layouts}/error.vue` + ]) + builder.relativeToBuild = jest.fn((...args) => `relativeBuild(${args.join(', ')})`) + fs.exists.mockReturnValueOnce(true) + + const templateVars = { + components: {}, + layouts: { + bar: '/var/nuxt/layouts/bar/index.vue' + } + } + const templateFiles = [] + await builder.resolveLayouts({ templateVars, templateFiles }) + + expect(path.resolve).toBeCalledTimes(1) + expect(path.resolve).toBeCalledWith('/var/nuxt/src', '/var/nuxt/src/layouts') + expect(fs.exists).toBeCalledTimes(1) + expect(fs.exists).toBeCalledWith('resolve(/var/nuxt/src, /var/nuxt/src/layouts)') + expect(builder.resolveFiles).toBeCalledTimes(1) + expect(builder.resolveFiles).toBeCalledWith('/var/nuxt/src/layouts') + expect(builder.relativeToBuild).toBeCalledTimes(2) + expect(builder.relativeToBuild).nthCalledWith(1, '/var/nuxt/src', '/var/nuxt/src/layouts/baz.vue') + expect(builder.relativeToBuild).nthCalledWith(2, '/var/nuxt/src', '/var/nuxt/src/layouts/error.vue') + expect(templateVars.components.ErrorPage).toEqual('relativeBuild(/var/nuxt/src, /var/nuxt/src/layouts/error.vue)') + expect(consola.warn).toBeCalledTimes(1) + expect(consola.warn).toBeCalledWith('Duplicate layout registration, "foo" has been registered as "/var/nuxt/layouts/foo/index.vue"') + expect(templateVars.layouts).toEqual({ + bar: '/var/nuxt/layouts/bar/index.vue', + baz: 'relativeBuild(/var/nuxt/src, /var/nuxt/src/layouts/baz.vue)', + default: './layouts/default.vue' + }) + expect(fs.mkdirp).toBeCalledTimes(1) + expect(fs.mkdirp).toBeCalledWith('r(/var/nuxt/build, layouts)') + expect(templateFiles).toEqual(['layouts/default.vue']) + expect(templateVars.layouts.default).toEqual('./layouts/default.vue') + }) + + test('should resolve error layouts', async () => { + const nuxt = createNuxt() + nuxt.options.srcDir = '/var/nuxt/src' + nuxt.options.dir = { + layouts: '/var/nuxt/src/layouts' + } + const builder = new Builder(nuxt, {}) + builder.resolveFiles = jest.fn(layouts => [ + `${layouts}/error.vue` + ]) + builder.relativeToBuild = jest.fn((...args) => `relativeBuild(${args.join(', ')})`) + fs.exists.mockReturnValueOnce(true) + + const templateVars = { + components: { + ErrorPage: '/var/nuxt/components/error.vue' + }, + layouts: { + default: '/var/nuxt/layouts/default.vue' + } + } + await builder.resolveLayouts({ templateVars }) + + expect(builder.relativeToBuild).not.toBeCalled() + expect(templateVars.components.ErrorPage).toEqual('/var/nuxt/components/error.vue') + }) + + test('should not resolve layouts if layouts dir does not exist', async () => { + const nuxt = createNuxt() + nuxt.options.srcDir = '/var/nuxt/src' + nuxt.options.dir = { + layouts: '/var/nuxt/src/layouts' + } + const builder = new Builder(nuxt, {}) + + const templateVars = { + layouts: { + default: '/var/nuxt/layouts/default.vue' + } + } + await builder.resolveLayouts({ templateVars }) + builder.resolveFiles = jest.fn() + + expect(path.resolve).toBeCalledTimes(1) + expect(path.resolve).toBeCalledWith('/var/nuxt/src', '/var/nuxt/src/layouts') + expect(fs.exists).toBeCalledTimes(1) + expect(fs.exists).toBeCalledWith('resolve(/var/nuxt/src, /var/nuxt/src/layouts)') + expect(builder.resolveFiles).not.toBeCalled() + expect(fs.mkdirp).not.toBeCalled() + }) + }) + + describe('builder: builder resolveRoutes', () => { + test('should resolve routes via build.createRoutes', async () => { + const nuxt = createNuxt() + nuxt.options.srcDir = '/var/nuxt/src' + nuxt.options.build.createRoutes = jest.fn(() => [ { name: 'default_route' } ]) + nuxt.options.router.extendRoutes = jest.fn(routes => [ ...routes, { name: 'extend_route' } ]) + const builder = new Builder(nuxt, {}) + + const templateVars = { + router: { + routes: [] + } + } + await builder.resolveRoutes({ templateVars }) + + expect(consola.debug).toBeCalledTimes(1) + expect(consola.debug).toBeCalledWith('Generating routes...') + expect(nuxt.options.build.createRoutes).toBeCalledTimes(1) + expect(nuxt.options.build.createRoutes).toBeCalledWith('/var/nuxt/src') + expect(nuxt.callHook).toBeCalledTimes(1) + expect(nuxt.callHook).toBeCalledWith( + 'build:extendRoutes', + [ { name: 'default_route' } ], + r + ) + expect(nuxt.options.router.extendRoutes).toBeCalledTimes(1) + expect(nuxt.options.router.extendRoutes).toBeCalledWith([ { name: 'default_route' } ], r) + expect(templateVars.router.routes).toEqual([ + { name: 'default_route' }, + { name: 'extend_route' } + ]) + expect(builder.routes).toEqual(templateVars.router.routes) + }) + + test('should resolve routes from defualt pages dir', async () => { + const nuxt = createNuxt() + nuxt.options.srcDir = '/var/nuxt/src' + nuxt.options.build = { + createRoutes: jest.fn(), + template: { dir: '/var/nuxt/templates' } + } + nuxt.options.router.routeNameSplitter = '[splitter]' + createRoutes.mockReturnValueOnce([ { name: 'default_route' } ]) + const builder = new Builder(nuxt, {}) + builder._defaultPage = true + + const templateVars = { + router: { + routes: [] + } + } + await builder.resolveRoutes({ templateVars }) + + expect(consola.debug).toBeCalledTimes(1) + expect(consola.debug).toBeCalledWith('Generating routes...') + expect(nuxt.options.build.createRoutes).not.toBeCalled() + expect(createRoutes).toBeCalledTimes(1) + expect(createRoutes).toBeCalledWith([ 'index.vue' ], '/var/nuxt/templates/pages', '', '[splitter]') + expect(nuxt.callHook).toBeCalledTimes(1) + expect(nuxt.callHook).toBeCalledWith( + 'build:extendRoutes', + [ { name: 'default_route' } ], + r + ) + expect(templateVars.router.routes).toEqual([ + { name: 'default_route' } + ]) + expect(builder.routes).toEqual(templateVars.router.routes) + }) + + test('should resolve routes from dir.pages', async () => { + const nuxt = createNuxt() + nuxt.options.srcDir = '/var/nuxt/src' + nuxt.options.dir = { + pages: '/var/nuxt/pages' + } + nuxt.options.build = { + createRoutes: jest.fn() + } + nuxt.options.router = { + routeNameSplitter: '[splitter]', + extendRoutes: jest.fn() + } + createRoutes.mockImplementationOnce(files => files.map(file => ({ path: file }))) + const builder = new Builder(nuxt, {}) + builder._nuxtPages = true + builder.resolveFiles = jest.fn(dir => [ + `${dir}/foo.js`, + `${dir}/bar.vue`, + `${dir}/baz.vue`, + `${dir}/foo.vue`, + `${dir}/bar.js` + ]) + + const templateVars = { + router: { + routes: [] + } + } + await builder.resolveRoutes({ templateVars }) + + expect(consola.debug).toBeCalledTimes(1) + expect(consola.debug).toBeCalledWith('Generating routes...') + expect(nuxt.options.build.createRoutes).not.toBeCalled() + expect(builder.resolveFiles).toBeCalledTimes(1) + expect(builder.resolveFiles).toBeCalledWith('/var/nuxt/pages') + + expect(createRoutes).toBeCalledTimes(1) + expect(createRoutes).toBeCalledWith( + [ '/var/nuxt/pages/foo.vue', '/var/nuxt/pages/bar.vue', '/var/nuxt/pages/baz.vue' ], + '/var/nuxt/src', + '/var/nuxt/pages', + '[splitter]' + ) + expect(nuxt.callHook).toBeCalledTimes(1) + expect(nuxt.callHook).toBeCalledWith( + 'build:extendRoutes', + [ + { path: '/var/nuxt/pages/foo.vue' }, + { path: '/var/nuxt/pages/bar.vue' }, + { path: '/var/nuxt/pages/baz.vue' } + ], + r + ) + expect(templateVars.router.routes).toEqual([ + { path: '/var/nuxt/pages/foo.vue' }, + { path: '/var/nuxt/pages/bar.vue' }, + { path: '/var/nuxt/pages/baz.vue' } + ]) + expect(builder.routes).toEqual(templateVars.router.routes) + }) + }) +}) diff --git a/packages/builder/test/builder.plugin.test.js b/packages/builder/test/builder.plugin.test.js new file mode 100644 index 0000000000..79ae6da50b --- /dev/null +++ b/packages/builder/test/builder.plugin.test.js @@ -0,0 +1,145 @@ +import Glob from 'glob' +import consola from 'consola' +import { isIndexFileAndFolder } from '@nuxt/utils' + +import Builder from '../src/builder' +import { createNuxt } from './__utils__' + +jest.mock('glob') +jest.mock('pify', () => fn => fn) +jest.mock('hash-sum', () => src => `hash(${src})`) +jest.mock('@nuxt/utils') +jest.mock('../src/ignore') + +describe('builder: builder plugins', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + test('should normalize plugins', () => { + const nuxt = createNuxt() + nuxt.options.plugins = [ + '/var/nuxt/plugins/test.js', + { src: '/var/nuxt/plugins/test.server', mode: 'server' }, + { src: '/var/nuxt/plugins/test.client', ssr: false } + ] + const builder = new Builder(nuxt, {}) + + const plugins = builder.normalizePlugins() + + expect(plugins).toEqual([ + { + mode: 'all', + name: 'nuxt_plugin_test_hash(/var/nuxt/plugins/test.js)', + src: 'resolveAlias(/var/nuxt/plugins/test.js)' + }, + { + mode: 'server', + name: 'nuxt_plugin_test_hash(/var/nuxt/plugins/test.server)', + src: 'resolveAlias(/var/nuxt/plugins/test.server)' + }, + { + mode: 'client', + name: 'nuxt_plugin_test_hash(/var/nuxt/plugins/test.client)', + src: 'resolveAlias(/var/nuxt/plugins/test.client)' + } + ]) + }) + + test('should warning and fallback invalid mode when normalize plugins', () => { + const nuxt = createNuxt() + nuxt.options.plugins = [ + { src: '/var/nuxt/plugins/test', mode: 'abc' } + ] + const builder = new Builder(nuxt, {}) + + const plugins = builder.normalizePlugins() + + expect(plugins).toEqual([ + { + mode: 'all', + name: 'nuxt_plugin_test_hash(/var/nuxt/plugins/test)', + src: 'resolveAlias(/var/nuxt/plugins/test)' + } + ]) + expect(consola.warn).toBeCalledTimes(1) + expect(consola.warn).toBeCalledWith("Invalid plugin mode (server/client/all): 'abc'. Falling back to 'all'") + }) + + test('should resolve plugins', async () => { + const nuxt = createNuxt() + const builder = new Builder(nuxt, {}) + builder.plugins = [ + { src: '/var/nuxt/plugins/test.js', mode: 'all' }, + { src: '/var/nuxt/plugins/test.client', mode: 'client' }, + { src: '/var/nuxt/plugins/test.server', mode: 'server' } + ] + builder.relativeToBuild = jest.fn(src => `relative(${src})`) + for (let step = 0; step < builder.plugins.length; step++) { + Glob.mockImplementationOnce(src => [`${src.replace(/\{.*\}/, '')}.js`]) + } + + await builder.resolvePlugins() + + expect(Glob).toBeCalledTimes(3) + expect(Glob).nthCalledWith(1, '/var/nuxt/plugins/test.js{?(.+([^.])),/index.+([^.])}') + expect(Glob).nthCalledWith(2, '/var/nuxt/plugins/test.client{?(.+([^.])),/index.+([^.])}') + expect(Glob).nthCalledWith(3, '/var/nuxt/plugins/test.server{?(.+([^.])),/index.+([^.])}') + expect(builder.plugins).toEqual([ + { mode: 'all', src: 'relative(/var/nuxt/plugins/test.js)' }, + { mode: 'client', src: 'relative(/var/nuxt/plugins/test.client)' }, + { mode: 'server', src: 'relative(/var/nuxt/plugins/test.server)' } + ]) + }) + + test('should throw error if plugin no existed', () => { + const nuxt = createNuxt() + const builder = new Builder(nuxt, {}) + builder.plugins = [ + { src: '/var/nuxt/plugins/test.js', mode: 'all' } + ] + Glob.mockImplementationOnce(() => []) + + expect(builder.resolvePlugins()).rejects.toThrow('Plugin not found: /var/nuxt/plugins/test.js') + }) + + test('should warn if there are multiple files and not index', async () => { + const nuxt = createNuxt() + const builder = new Builder(nuxt, {}) + builder.plugins = [ + { src: '/var/nuxt/plugins/test', mode: 'all' } + ] + builder.relativeToBuild = jest.fn(src => `relative(${src})`) + + Glob.mockImplementationOnce(src => [`${src}.js`, `${src}.ts`]) + isIndexFileAndFolder.mockReturnValueOnce(false) + + await builder.resolvePlugins() + + expect(builder.plugins).toEqual([ + { mode: 'all', src: 'relative(/var/nuxt/plugins/test)' } + ]) + }) + + test('should detect plugin mode for client/server plugins', async () => { + const nuxt = createNuxt() + const builder = new Builder(nuxt, {}) + builder.plugins = [ + { src: '/var/nuxt/plugins/test.js', mode: 'all' }, + { src: '/var/nuxt/plugins/test.client', mode: 'all' }, + { src: '/var/nuxt/plugins/test.server', mode: 'all' } + ] + builder.relativeToBuild = jest.fn(src => `relative(${src})`) + for (let step = 0; step < builder.plugins.length; step++) { + Glob.mockImplementationOnce(src => [`${src.replace(/\{.*\}/, '')}.js`]) + } + + await builder.resolvePlugins() + + expect(builder.plugins).toEqual([ + { mode: 'all', src: 'relative(/var/nuxt/plugins/test.js)' }, + { mode: 'client', src: 'relative(/var/nuxt/plugins/test.client)' }, + { mode: 'server', src: 'relative(/var/nuxt/plugins/test.server)' } + ]) + }) +}) diff --git a/packages/builder/test/builder.watch.test.js b/packages/builder/test/builder.watch.test.js new file mode 100644 index 0000000000..bf5b079680 --- /dev/null +++ b/packages/builder/test/builder.watch.test.js @@ -0,0 +1,330 @@ +import chokidar from 'chokidar' +import upath from 'upath' +import debounce from 'lodash/debounce' +import { r, isString } from '@nuxt/utils' + +import Builder from '../src/builder' +import { createNuxt } from './__utils__' + +jest.mock('chokidar', () => ({ + watch: jest.fn().mockReturnThis(), + on: jest.fn().mockReturnThis(), + close: jest.fn().mockReturnThis() +})) +jest.mock('upath', () => ({ normalizeSafe: jest.fn(src => src) })) +jest.mock('lodash/debounce', () => jest.fn(fn => fn)) +jest.mock('@nuxt/utils') +jest.mock('../src/ignore') + +describe('builder: builder watch', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + test('should watch client files', () => { + const nuxt = createNuxt() + nuxt.options.srcDir = '/var/nuxt/src' + nuxt.options.dir = { + layouts: '/var/nuxt/src/layouts', + pages: '/var/nuxt/src/pages', + store: '/var/nuxt/src/store', + middleware: '/var/nuxt/src/middleware' + } + nuxt.options.build.watch = [] + nuxt.options.watchers = { + chokidar: { test: true } + } + const builder = new Builder(nuxt, {}) + r.mockImplementation((dir, src) => src) + + builder.watchClient() + + const patterns = [ + '/var/nuxt/src/layouts', + '/var/nuxt/src/store', + '/var/nuxt/src/middleware', + '/var/nuxt/src/layouts/*.{vue,js,ts,tsx}', + '/var/nuxt/src/layouts/**/*.{vue,js,ts,tsx}' + ] + + expect(r).toBeCalledTimes(5) + expect(r).nthCalledWith(1, '/var/nuxt/src', '/var/nuxt/src/layouts') + expect(r).nthCalledWith(2, '/var/nuxt/src', '/var/nuxt/src/store') + expect(r).nthCalledWith(3, '/var/nuxt/src', '/var/nuxt/src/middleware') + expect(r).nthCalledWith(4, '/var/nuxt/src', '/var/nuxt/src/layouts/*.{vue,js,ts,tsx}') + expect(r).nthCalledWith(5, '/var/nuxt/src', '/var/nuxt/src/layouts/**/*.{vue,js,ts,tsx}') + + expect(upath.normalizeSafe).toBeCalledTimes(5) + expect(upath.normalizeSafe).nthCalledWith(1, '/var/nuxt/src/layouts', 0, patterns) + expect(upath.normalizeSafe).nthCalledWith(2, '/var/nuxt/src/store', 1, patterns) + expect(upath.normalizeSafe).nthCalledWith(3, '/var/nuxt/src/middleware', 2, patterns) + expect(upath.normalizeSafe).nthCalledWith(4, '/var/nuxt/src/layouts/*.{vue,js,ts,tsx}', 3, patterns) + expect(upath.normalizeSafe).nthCalledWith(5, '/var/nuxt/src/layouts/**/*.{vue,js,ts,tsx}', 4, patterns) + + expect(chokidar.watch).toBeCalledTimes(1) + expect(chokidar.watch).toBeCalledWith(patterns, { test: true }) + expect(chokidar.on).toBeCalledTimes(2) + expect(chokidar.on).nthCalledWith(1, 'add', expect.any(Function)) + expect(chokidar.on).nthCalledWith(2, 'unlink', expect.any(Function)) + }) + + test('should watch pages files', () => { + const nuxt = createNuxt() + nuxt.options.srcDir = '/var/nuxt/src' + nuxt.options.dir = { + layouts: '/var/nuxt/src/layouts', + pages: '/var/nuxt/src/pages', + store: '/var/nuxt/src/store', + middleware: '/var/nuxt/src/middleware' + } + nuxt.options.build.watch = [] + nuxt.options.watchers = { + chokidar: { test: true } + } + const builder = new Builder(nuxt, {}) + builder._nuxtPages = true + r.mockImplementation((dir, src) => src) + + builder.watchClient() + + expect(r).toBeCalledTimes(8) + expect(r).nthCalledWith(6, '/var/nuxt/src', '/var/nuxt/src/pages') + expect(r).nthCalledWith(7, '/var/nuxt/src', '/var/nuxt/src/pages/*.{vue,js,ts,tsx}') + expect(r).nthCalledWith(8, '/var/nuxt/src', '/var/nuxt/src/pages/**/*.{vue,js,ts,tsx}') + }) + + test('should watch custom in watchClient', () => { + const nuxt = createNuxt() + nuxt.options.srcDir = '/var/nuxt/src' + nuxt.options.dir = { + layouts: '/var/nuxt/src/layouts', + pages: '/var/nuxt/src/pages', + store: '/var/nuxt/src/store', + middleware: '/var/nuxt/src/middleware' + } + nuxt.options.build.watch = [] + nuxt.options.watchers = { + chokidar: { test: true } + } + const builder = new Builder(nuxt, {}) + builder.watchCustom = jest.fn() + r.mockImplementation((dir, src) => src) + + builder.watchClient() + + expect(debounce).toBeCalledTimes(1) + expect(debounce).toBeCalledWith(expect.any(Function), 200) + + const refreshFiles = chokidar.on.mock.calls[0][1] + builder.generateRoutesAndFiles = jest.fn() + refreshFiles() + expect(builder.generateRoutesAndFiles).toBeCalled() + expect(builder.watchCustom).toBeCalledTimes(1) + expect(builder.watchCustom).toBeCalledWith(refreshFiles) + }) + + test('should watch custom patterns', () => { + const nuxt = createNuxt() + nuxt.options.watchers = { + chokidar: { test: true } + } + nuxt.options.build.watch = [ + '/var/nuxt/src/custom' + ] + nuxt.options.build.styleResources = [ + '/var/nuxt/src/style' + ] + const builder = new Builder(nuxt, {}) + const refreshFiles = jest.fn() + + builder.watchCustom(refreshFiles) + + expect(chokidar.watch).toBeCalledTimes(1) + expect(chokidar.watch).toBeCalledWith( + ['/var/nuxt/src/custom', '/var/nuxt/src/style'], + { test: true } + ) + expect(chokidar.on).toBeCalledTimes(1) + expect(chokidar.on).toBeCalledWith('change', refreshFiles) + }) + + test('should call refreshFiles before watching custom patterns', () => { + const nuxt = createNuxt() + nuxt.options.watchers = { + chokidar: { test: true } + } + nuxt.options.build.watch = [ + '/var/nuxt/src/custom' + ] + const builder = new Builder(nuxt, {}) + const refreshFiles = jest.fn() + + builder.watchCustom(refreshFiles, true) + + expect(refreshFiles).toBeCalledTimes(1) + }) + + test('should rewatch custom patterns when event is included in rewatchOnRawEvents', () => { + const nuxt = createNuxt() + nuxt.options.watchers = { + chokidar: { test: true }, + rewatchOnRawEvents: ['rename'] + } + nuxt.options.build.watch = [ + '/var/nuxt/src/custom' + ] + const builder = new Builder(nuxt, {}) + const refreshFiles = jest.fn() + + builder.watchCustom(refreshFiles) + + expect(chokidar.on).toBeCalledTimes(2) + expect(chokidar.on).nthCalledWith(2, 'raw', expect.any(Function)) + + const rewatchHandler = chokidar.on.mock.calls[1][1] + builder.watchCustom = jest.fn() + rewatchHandler('rename') + rewatchHandler('change') + + expect(chokidar.close).toBeCalledTimes(1) + expect(builder.watchers.custom).toBeNull() + expect(builder.watchCustom).toBeCalledTimes(1) + expect(builder.watchCustom).toBeCalledWith(refreshFiles, true) + }) + + test('should watch files for restarting server', () => { + const nuxt = createNuxt() + nuxt.options.watchers = { + chokidar: { test: true } + } + nuxt.options.watch = [ + '/var/nuxt/src/watch/test' + ] + nuxt.options.serverMiddleware = [ + '/var/nuxt/src/middleware/test', + { obj: 'test' } + ] + const builder = new Builder(nuxt, {}) + builder.ignore.ignoreFile = '/var/nuxt/src/.nuxtignore' + isString.mockImplementationOnce(src => typeof src === 'string') + + builder.watchRestart() + + expect(chokidar.watch).toBeCalledTimes(1) + expect(chokidar.watch).toBeCalledWith( + [ + 'resolveAlias(/var/nuxt/src/middleware/test)', + 'resolveAlias(/var/nuxt/src/watch/test)', + '/var/nuxt/src/.nuxtignore' + ], + { test: true } + ) + expect(chokidar.on).toBeCalledTimes(1) + expect(chokidar.on).toBeCalledWith('all', expect.any(Function)) + }) + + test('should trigger restarting when files changed', () => { + const nuxt = createNuxt() + nuxt.options.watchers = { + chokidar: { test: true } + } + nuxt.options.watch = [ + '/var/nuxt/src/watch/test' + ] + nuxt.options.serverMiddleware = [] + const builder = new Builder(nuxt, {}) + + builder.watchRestart() + + const restartHandler = chokidar.on.mock.calls[0][1] + const watchingFile = '/var/nuxt/src/watch/test/index.js' + restartHandler('add', watchingFile) + restartHandler('change', watchingFile) + restartHandler('unlink', watchingFile) + + expect(nuxt.callHook).toBeCalledTimes(6) + expect(nuxt.callHook).nthCalledWith(1, 'watch:fileChanged', builder, watchingFile) + expect(nuxt.callHook).nthCalledWith(2, 'watch:restart', { event: 'add', path: watchingFile }) + expect(nuxt.callHook).nthCalledWith(3, 'watch:fileChanged', builder, watchingFile) + expect(nuxt.callHook).nthCalledWith(4, 'watch:restart', { event: 'change', path: watchingFile }) + expect(nuxt.callHook).nthCalledWith(5, 'watch:fileChanged', builder, watchingFile) + expect(nuxt.callHook).nthCalledWith(6, 'watch:restart', { event: 'unlink', path: watchingFile }) + }) + + test('should ignore other events in watchRestart', () => { + const nuxt = createNuxt() + nuxt.options.watchers = { + chokidar: { test: true } + } + nuxt.options.watch = [ + '/var/nuxt/src/watch/test' + ] + nuxt.options.serverMiddleware = [] + const builder = new Builder(nuxt, {}) + + builder.watchRestart() + + const restartHandler = chokidar.on.mock.calls[0][1] + const watchingFile = '/var/nuxt/src/watch/test/index.js' + restartHandler('rename', watchingFile) + + expect(nuxt.callHook).not.toBeCalled() + }) + + test('should unwatch every watcher', () => { + const nuxt = createNuxt() + const builder = new Builder(nuxt, {}) + builder.watchers = { + files: { close: jest.fn() }, + custom: { close: jest.fn() }, + restart: { close: jest.fn() } + } + + builder.unwatch() + + expect(builder.watchers.files.close).toBeCalledTimes(1) + expect(builder.watchers.custom.close).toBeCalledTimes(1) + expect(builder.watchers.restart.close).toBeCalledTimes(1) + }) + + test('should close watch and bundle builder', async () => { + const nuxt = createNuxt() + const bundleBuilderClose = jest.fn() + const builder = new Builder(nuxt, { close: bundleBuilderClose }) + builder.unwatch = jest.fn() + + expect(builder.__closed).toBeUndefined() + + await builder.close() + + expect(builder.__closed).toEqual(true) + expect(builder.unwatch).toBeCalledTimes(1) + expect(bundleBuilderClose).toBeCalledTimes(1) + }) + + test('should close bundleBuilder only if close api exists', async () => { + const nuxt = createNuxt() + const builder = new Builder(nuxt, { }) + builder.unwatch = jest.fn() + + expect(builder.__closed).toBeUndefined() + + await builder.close() + + expect(builder.__closed).toEqual(true) + expect(builder.unwatch).toBeCalledTimes(1) + }) + + test('should prevent duplicate close', async () => { + const nuxt = createNuxt() + const bundleBuilderClose = jest.fn() + const builder = new Builder(nuxt, { close: bundleBuilderClose }) + builder.unwatch = jest.fn() + builder.__closed = true + + await builder.close() + + expect(builder.unwatch).not.toBeCalled() + expect(bundleBuilderClose).not.toBeCalled() + }) +}) diff --git a/packages/builder/test/context/__snapshots__/template.test.js.snap b/packages/builder/test/context/__snapshots__/template.test.js.snap new file mode 100644 index 0000000000..952b4e7193 --- /dev/null +++ b/packages/builder/test/context/__snapshots__/template.test.js.snap @@ -0,0 +1,105 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`builder: buildContext should construct context 1`] = ` +TemplateContext { + "templateFiles": Array [ + "template.js", + ], + "templateVars": Object { + "appPath": "./App.js", + "components": Object { + "ErrorPage": "relativeBuild(test_error_page)", + }, + "css": Array [ + "test.css", + ], + "debug": "test_debug", + "dir": Array [ + "test_dir", + ], + "env": "test_env", + "extensions": "test|ext", + "globalName": "test_global", + "globals": Array [ + "globals", + ], + "head": "test_head", + "isDev": "test_dev", + "isTest": "test_test", + "layoutTransition": Object { + "name": "test_layout_trans", + }, + "layouts": Object { + "test-layout": "test.template", + }, + "loading": "relativeBuild(test_src_dir, test-loading)", + "messages": Object { + "test": "test message", + }, + "mode": "test mode", + "options": Object { + "ErrorPage": "test_error_page", + "build": Object { + "splitChunks": Object { + "testSC": true, + }, + }, + "css": Array [ + "test.css", + ], + "debug": "test_debug", + "dev": "test_dev", + "dir": Array [ + "test_dir", + ], + "env": "test_env", + "extensions": Array [ + "test", + "ext", + ], + "globalName": "test_global", + "head": "test_head", + "layoutTransition": Object { + "name": "test_layout_trans", + }, + "layouts": Object { + "test-layout": "test.template", + }, + "loading": "test-loading", + "messages": Object { + "test": "test message", + }, + "mode": "test mode", + "router": Object { + "route": "test", + }, + "srcDir": "test_src_dir", + "store": "test_store", + "test": "test_test", + "transition": Object { + "name": "test_trans", + }, + "vue": Object { + "config": "test_config", + }, + }, + "plugins": Array [ + "plugins", + ], + "router": Object { + "route": "test", + }, + "splitChunks": Object { + "testSC": true, + }, + "store": "test_store", + "transition": Object { + "name": "test_trans", + }, + "uniqBy": [Function], + "vue": Object { + "config": "test_config", + }, + }, +} +`; diff --git a/packages/builder/test/context/build.test.js b/packages/builder/test/context/build.test.js new file mode 100644 index 0000000000..693d96ed23 --- /dev/null +++ b/packages/builder/test/context/build.test.js @@ -0,0 +1,23 @@ +import BuildContext from '../../src/context/build' + +describe('builder: buildContext', () => { + test('should construct context', () => { + const builder = { + nuxt: { options: {} } + } + const context = new BuildContext(builder) + expect(context._builder).toEqual(builder) + expect(context.nuxt).toEqual(builder.nuxt) + expect(context.options).toEqual(builder.nuxt.options) + expect(context.isStatic).toEqual(false) + }) + + test('should return builder plugins context', () => { + const builder = { + plugins: [], + nuxt: { options: {} } + } + const context = new BuildContext(builder) + expect(context.plugins).toEqual(builder.plugins) + }) +}) diff --git a/packages/builder/test/context/template.test.js b/packages/builder/test/context/template.test.js new file mode 100644 index 0000000000..3ad3872c06 --- /dev/null +++ b/packages/builder/test/context/template.test.js @@ -0,0 +1,81 @@ +import hash from 'hash-sum' +import consola from 'consola' +import serialize from 'serialize-javascript' + +import devalue from '@nuxt/devalue' +import { r, wp, wChunk, serializeFunction } from '@nuxt/utils' +import TemplateContext from '../../src/context/template' + +jest.mock('lodash', () => ({ test: 'test lodash', warn: 'only once' })) + +describe('builder: buildContext', () => { + const builder = { + template: { files: ['template.js'] }, + globals: [ 'globals' ], + plugins: [ 'plugins' ], + relativeToBuild: jest.fn((...args) => `relativeBuild(${args.join(', ')})`) + } + const options = { + extensions: [ 'test', 'ext' ], + messages: { test: 'test message' }, + build: { + splitChunks: { testSC: true } + }, + dev: 'test_dev', + test: 'test_test', + debug: 'test_debug', + vue: { config: 'test_config' }, + mode: 'test mode', + router: { route: 'test' }, + env: 'test_env', + head: 'test_head', + store: 'test_store', + globalName: 'test_global', + css: [ 'test.css' ], + layouts: { + 'test-layout': 'test.template' + }, + srcDir: 'test_src_dir', + loading: 'test-loading', + transition: { name: 'test_trans' }, + layoutTransition: { name: 'test_layout_trans' }, + dir: ['test_dir'], + ErrorPage: 'test_error_page' + } + + test('should construct context', () => { + const context = new TemplateContext(builder, options) + expect(context).toMatchSnapshot() + }) + + test('should return object loading template options', () => { + const context = new TemplateContext(builder, { + ...options, + loading: { name: 'test_loading' } + }) + + expect(context.templateVars.loading).toEqual({ name: 'test_loading' }) + }) + + test('should return object loading template options', () => { + const context = new TemplateContext(builder, options) + const templateOptions = context.templateOptions + expect(templateOptions).toEqual({ + imports: { + serialize, + serializeFunction, + devalue, + hash, + r, + wp, + wChunk, + _: {} + }, + interpolate: /<%=([\s\S]+?)%>/g + }) + expect(templateOptions.imports._.test).toEqual('test lodash') + expect(templateOptions.imports._.warn).toEqual('only once') + expect(consola.warn).toBeCalledTimes(1) + expect(consola.warn).toBeCalledWith('Avoid using _ inside templates') + }) +}) diff --git a/packages/builder/test/ignore.test.js b/packages/builder/test/ignore.test.js new file mode 100644 index 0000000000..3f6fe9f071 --- /dev/null +++ b/packages/builder/test/ignore.test.js @@ -0,0 +1,147 @@ +import path from 'path' +import fs from 'fs-extra' + +import Ignore from '../src/ignore' + +jest.mock('path') +jest.mock('fs-extra') +jest.mock('ignore', () => () => ({ + add: jest.fn(), + filter: jest.fn(paths => paths) +})) + +describe('builder: Ignore', () => { + beforeAll(() => { + path.resolve.mockImplementation((...args) => `resolve(${args.join(', ')})`) + }) + + beforeEach(() => { + jest.clearAllMocks() + }) + + test('should construct Ignore', () => { + jest.spyOn(Ignore.prototype, 'addIgnoresRules').mockImplementation(() => {}) + + const ignore = new Ignore({ + rootDir: '/var/nuxt' + }) + + expect(Ignore.IGNORE_FILENAME).toEqual('.nuxtignore') + expect(ignore.rootDir).toEqual('/var/nuxt') + expect(ignore.addIgnoresRules).toBeCalledTimes(1) + + Ignore.prototype.addIgnoresRules.mockRestore() + }) + + test('should add ignore file', () => { + jest.spyOn(Ignore.prototype, 'addIgnoresRules') + jest.spyOn(Ignore.prototype, 'readIgnoreFile').mockReturnValue('') + + const ignore = new Ignore({ + rootDir: '/var/nuxt' + }) + + expect(Ignore.IGNORE_FILENAME).toEqual('.nuxtignore') + expect(ignore.rootDir).toEqual('/var/nuxt') + expect(ignore.addIgnoresRules).toBeCalledTimes(1) + expect(ignore.readIgnoreFile).toBeCalledTimes(1) + + Ignore.prototype.addIgnoresRules.mockRestore() + Ignore.prototype.readIgnoreFile.mockRestore() + }) + + test('should find ignore file', () => { + fs.existsSync.mockReturnValueOnce(true) + fs.statSync.mockReturnValueOnce({ isFile: () => true }) + fs.readFileSync.mockReturnValueOnce('pages/ignore.vue') + + const ignore = new Ignore({ + rootDir: '/var/nuxt' + }) + + expect(path.resolve).toBeCalledTimes(1) + expect(path.resolve).toBeCalledWith('/var/nuxt', '.nuxtignore') + expect(fs.existsSync).toBeCalledTimes(1) + expect(fs.existsSync).toBeCalledWith('resolve(/var/nuxt, .nuxtignore)') + expect(fs.statSync).toBeCalledTimes(1) + expect(fs.statSync).toBeCalledWith('resolve(/var/nuxt, .nuxtignore)') + expect(ignore.ignoreFile).toEqual('resolve(/var/nuxt, .nuxtignore)') + expect(fs.readFileSync).toBeCalledTimes(1) + expect(fs.readFileSync).toBeCalledWith('resolve(/var/nuxt, .nuxtignore)', 'utf8') + expect(ignore.ignore.add).toBeCalledTimes(1) + expect(ignore.ignore.add).toBeCalledWith('pages/ignore.vue') + + fs.existsSync.mockClear() + ignore.findIgnoreFile() + expect(fs.existsSync).not.toBeCalled() + }) + + test('should only find existed ignore file', () => { + fs.existsSync.mockReturnValueOnce(false) + + const ignore = new Ignore({ + rootDir: '/var/nuxt' + }) + + expect(path.resolve).toBeCalledTimes(1) + expect(path.resolve).toBeCalledWith('/var/nuxt', '.nuxtignore') + expect(fs.existsSync).toBeCalledTimes(1) + expect(fs.existsSync).toBeCalledWith('resolve(/var/nuxt, .nuxtignore)') + expect(fs.statSync).not.toBeCalled() + expect(ignore.ignoreFile).toBeUndefined() + expect(ignore.ignore).toBeUndefined() + }) + + test('should filter ignore files', () => { + fs.existsSync.mockReturnValueOnce(true) + fs.statSync.mockReturnValueOnce({ isFile: () => true }) + fs.readFileSync.mockReturnValueOnce('pages/ignore.vue') + + const ignore = new Ignore({ + rootDir: '/var/nuxt' + }) + + ignore.filter() + ignore.filter('pages/ignore.vue') + const paths = ignore.filter([ 'pages/ignore.vue', 'layouts/ignore.vue' ]) + + expect(ignore.ignore.filter).toBeCalledTimes(3) + expect(ignore.ignore.filter).nthCalledWith(1, []) + expect(ignore.ignore.filter).nthCalledWith(2, [ 'pages/ignore.vue' ]) + expect(ignore.ignore.filter).nthCalledWith(3, [ 'pages/ignore.vue', 'layouts/ignore.vue' ]) + expect(paths).toEqual([ 'pages/ignore.vue', 'layouts/ignore.vue' ]) + }) + + test('should return origin paths if there is no ignorefile', () => { + fs.existsSync.mockReturnValueOnce(false) + + const ignore = new Ignore({ + rootDir: '/var/nuxt' + }) + + const paths = ignore.filter([ 'pages/ignore.vue', 'layouts/ignore.vue' ]) + + expect(paths).toEqual([ 'pages/ignore.vue', 'layouts/ignore.vue' ]) + }) + + test('should reload ignore', () => { + fs.existsSync.mockReturnValueOnce(true) + fs.statSync.mockReturnValueOnce({ isFile: () => true }) + fs.readFileSync.mockReturnValueOnce('pages/ignore.vue') + + const ignore = new Ignore({ + rootDir: '/var/nuxt' + }) + + expect(ignore.ignore).toBeDefined() + expect(ignore.ignoreFile).toEqual('resolve(/var/nuxt, .nuxtignore)') + + ignore.addIgnoresRules = jest.fn() + + ignore.reload() + + expect(ignore.ignore).toBeUndefined() + expect(ignore.ignoreFile).toBeUndefined() + expect(ignore.addIgnoresRules).toBeCalledTimes(1) + }) +}) diff --git a/packages/builder/test/index.test.js b/packages/builder/test/index.test.js new file mode 100644 index 0000000000..a8ada36f52 --- /dev/null +++ b/packages/builder/test/index.test.js @@ -0,0 +1,9 @@ +import { Builder } from '../src' + +jest.mock('../src/builder', () => jest.fn(() => 'nuxt builder')) + +describe('builder: entry', () => { + test('should export Builder', () => { + expect(Builder()).toEqual('nuxt builder') + }) +})