core.js

/**
 * A Hexo context object
 * @typedef {Object} HexoContext
 */

/**
 * @callback sandboxFactoryFn
 * @param {string|object} options - a string with a fixture name or a configuration object
 * @param {string} options.fixtureName - a fixture name, the folder of the Hexo testing project
 * @param {boolean} options.skipInit - set to true if you don't want to initialize Hexo. Default: false
 * @return {Promise<HexoContext>}
 */

/**
 * @module core
 */

import path from 'path'
import Promise from 'bluebird'

/**
 * ```js
 * // CommonJS
 * const {createSandbox} = require('hexo-test-utils')
 *
 * // ES2015
 * import {createSandbox} from 'hexo-test-utils'
 * ```
 *
 * This is the main function of `hexo-test-utils` used to build a factory of sandboxes.
 *
 * By default it uses an empty fixture folder, which means no source files, no theme, no config, etc.
 * To prepare data for your test you have multiple options:
 *  - use {@link HexoContext} methods directly
 *  - use helper methods from this package, like {@link module:core.mockConfig|mockConfig}
 *  - provide a folder with fixture files
 *
 * @param {constructor} Hexo - Hexo constructor to be used
 * @param {Object} options - Sandbox options
 * @param {string} options.fixture_folder - a root path to the folder with fixtures
 * @param {string[]} options.plugins - list of plugins paths to be loaded in your Sandbox, the easiest way is to use `require.resolve`
 * @return {sandboxFactoryFn} a Sandbox factory function
 *
 * @example <caption>Basic example</caption>
 * const {createSandbox} = require('hexo-test-utils')
 * const Hexo = require('hexo')
 * const sandbox = createSandbox(Hexo)
 *
 * const ctx = await sandbox()
 *
 * @example <caption>Example with custom fixture</caption>
 * const {createSandbox} = require('hexo-test-utils')
 * const Hexo = require('hexo')
 * const path = require('path')
 *
 * const sandbox = createSandbox(Hexo, {
 *   fixture_folder: path.join(__dirname, '..', 'fixtures')
 * })
 *
 * // Create a context with a fixture folder set to '../fixtures/test1'
 * const ctx = await sandbox('test1')
 *
 * @example <caption>Example with custom plugins</caption>
 * const {createSandbox} = require('hexo-test-utils')
 * const Hexo = require('hexo')
 * const path = require('path')
 *
 * const sandbox = createSandbox(Hexo, {
 *   plugins: [
 *     require.resolve('hexo-some-plugin'),
 *     require.resolve('../src')
 *   ]
 * })
 *
 * const ctx = await sandbox()
 */
export function createSandbox(Hexo, options) {
  options = {
    plugins: [],
    ...options
  }

  return function sandboxFactoryFn (initOptions) {
    initOptions = initOptions || {}

    const name = typeof initOptions == 'string' ? initOptions : initOptions.fixtureName
    const skipInit = typeof initOptions == 'string' ? false : initOptions.skipInit

    const baseFolder = name
      ? path.join(options.fixture_folder, name)
      : path.join(__dirname, 'fixtures', 'default')

    const ctx = new Hexo(baseFolder, {silent: true})

    // Bypass the package.json check and make sure the config file can be loaded
    ctx.env.init = true

    ctx.extend.filter.register('after_init', () => {
      return Promise.each(options.plugins, pluginPath => {
        ctx.loadPlugin(pluginPath)
      })
    }, 0)

    if (skipInit) {
      return Promise.resolve(ctx)
    }

    return ctx.init().then(() => ctx)
  }
}

/**
 * ```
 * // CommonJS
 * const {init} = require('hexo-test-utils')
 *
 * // ES2015
 * import {init} from 'hexo-test-utils'
 * ```
 *
 * Initializes all plugins, generates content from folders. Useful if you need to mock main configuration like `source_dir`
 * Use with `skipInit` in {@link sandboxFactoryFn}.
 *
 * @param {HexoContext} ctx
 * @return {Promise.<HexoContext>}
 */
export function init(ctx) {
  return ctx.init().then(() => ctx)
}

/**
 * ```
 * // CommonJS
 * const {process} = require('hexo-test-utils')
 *
 * // ES2015
 * import {process} from 'hexo-test-utils'
 * ```
 *
 * Loads and processes all the blog data. After calling this function the passed {@link HexoContext} will have all information
 * about the blog.
 *
 * @param {HexoContext} ctx
 * @return {Promise.<HexoContext>}
 */
export function process(ctx) {
  return ctx.load().then(() => ctx)
}

/**
 * ```
 * // CommonJS
 * const {mockConfig} = require('hexo-test-utils')
 *
 * // ES2015
 * import {mockConfig} from 'hexo-test-utils'
 * ```
 *
 * Mocks the Hexo configuration. Useful to test the plugin with different configurations without using fixtures.
 * > Note: Make sure to call it before `process`
 *
 * @param {HexoContext} ctx
 * @param {string} name - a name of configuration, it the config YML file it would be the root key
 * @param {*} value - a new configuration for a given name, usually an Object
 */

export function mockConfig(ctx, name, value) {
  ctx.config[name] = value
}