hybridables/redolent

View on GitHub
test.js

Summary

Maintainability
B
5 hrs
Test Coverage
/*!
 * redolent <https://github.com/tunnckoCore/redolent>
 *
 * Copyright (c) Charlike Mike Reagent <@tunnckoCore> (https://i.am.charlike.online)
 * Released under the MIT license.
 */

/* jshint asi:true */

'use strict'

var fs = require('fs')
var test = require('mukla')
var semver = require('semver')
var redolent = require('./dist/redolent-testing.js')
var extend = require('extend-shallow')
var Pinkie = require('pinkie')

function noop () {}

function notSkipOne (one, two, cb) {
  cb(null, one, two, noop)
}

function notSkipTwo (one, two, cb) {
  cb(null, one, two, fs.readFileSync)
}

function multipleArgs (one, two, three, cb) {
  cb(null, one, two, three)
}

test('should throw TypeError if not function given', function (done) {
  function fixture () {
    redolent(false)
  }

  test.throws(fixture, TypeError)
  test.throws(fixture, /expect `fn` to be a function/)
  done()
})

function factory (promisify) {
  test('should promisify a sync function', function () {
    var fn = promisify(function () {
      return 'foo bar baz'
    })
    return fn().then(function (str) {
      test.strictEqual(str, 'foo bar baz')
    })
  })

  test('should flatten args', function () {
    var func = promisify(function (a, b, c, d, e) {
      return [a, b, c, d, e]
    }, {
      args: ['foo', 'bar']
    })

    return func(123, [1, 2], { a: 'b' }).then(function (arr) {
      test.deepEqual(arr, [ 'foo', 'bar', 123, [ 1, 2 ], { a: 'b' } ])
    })
  })

  test('should promisify with given opts.Promise module (pinkie)', function (done) {
    var readFile = promisify(fs.readFile, {
      Promise: Pinkie
    })
    var promise = readFile('package.json', 'utf8')

    promise.then(function (res) {
      test.strictEqual(typeof res, 'string')
      if (semver.lt(process.version, '0.11.13')) {
        test.strictEqual(promise.___customPromise, true)
      }
      done()
    }).catch(done)
  })

  test('should flatten multiple arguments to array by default', function (done) {
    promisify(multipleArgs)(11, 22, 33).then(function (res) {
      test.strictEqual(Array.isArray(res), true)
      test.deepEqual(res, [11, 22, 33])
      done()
    }).catch(done)
  })

  test('should not skip if pass callback fn, e.g. fn(err, res) as last argument', function (done) {
    function foo (_err, res) { return [_err, res] }

    promisify(function (one, fn, cb) {
      cb(null, one, fn)
    })(123, foo).then(function (res) {
      test.strictEqual(Array.isArray(res), true)
      test.deepEqual(res, [123, foo])
      done()
    }).catch(done)
  })

  test('should promisify `fs.readFileSync` and handle buffer result', function (done) {
    var readFile = promisify(fs.readFileSync)
    readFile('package.json').then(function (buf) {
      test.strictEqual(typeof buf.toString(), 'string')
      done()
    }).catch(done)
  })

  test('should catch errors from failing sync function', function (done) {
    promisify(fs.readFileSync)('foobar.json', 'utf8')
      .catch(function (err) {
        test.strictEqual(err.code, 'ENOENT')
        test.strictEqual(/no such file or directory/.test(err.message), true)
        done()
      })
      .catch(done)
  })

  test('should skip last argument only if it is `fn(foo, bar, cb)` (async fn)', function (done) {
    promisify(notSkipOne)(111, 222).then(function (res) {
      test.strictEqual(Array.isArray(res), true)
      test.deepEqual(res, [111, 222, noop])
      done()
    }).catch(done)
  })

  test('should not skip last argument and work core api (fs.readFileSync)', function (done) {
    promisify(notSkipTwo)(333, 5555).then(function (res) {
      test.strictEqual(Array.isArray(res), true)
      test.deepEqual(res, [333, 5555, fs.readFileSync])
      done()
    }).catch(done)
  })

  test('should call the callback once', function (done) {
    var fn = promisify(function (cb) {
      cb(null, 1)
      cb(null, 2)
    })

    fn()
      .then(function (res) {
        test.strictEqual(res, 1)
        done()
      })
      .catch(done)
  })

  test('should be rejected promise if callback(err)', function (done) {
    var fn = promisify(function (cb) {
      cb(new Error('foo qux'))
    })

    fn()
      .catch(function (err) {
        test.strictEqual(err.name, 'Error')
        test.strictEqual(err.message, 'foo qux')
        done()
      })
      .catch(done)
  })

  test('should work if `done` is present, but return a promise', function (done) {
    var fn = promisify(function (xxx, cb) {
      return Pinkie.resolve(100 + xxx)
    })

    return fn(200).then(function (val) {
      test.strictEqual(val, 300)
      done()
    }, done).catch(done)
  })

  test('should reject if async fn returns non Promise, nor call a callback', function (done) {
    var func = promisify(function (cb) {
      return 'foo bar'
    })

    func().catch(function (err) {
      test.strictEqual(err instanceof Error, true)
      test.strictEqual(/Asynchronous functions can only/.test(err.message), true)
      test.strictEqual(/return a Promise or invoke a callback/.test(err.message), true)
      done()
    }).catch(done)
  })
}

if (semver.lt(process.version, '0.12.0')) {
  factory(function (fn, opts) {
    return redolent(fn, extend({
      Promise: Pinkie
    }, opts))
  })
  test('should autoload some custom Promise (installed in some of the devDeps)', function (done) {
    var func = redolent(function () { return 123 })
    var promise = func()

    test.strictEqual(Promise.___customPromise, true)
    test.strictEqual(promise.___customPromise, true)

    promise.then(function (res) {
      test.strictEqual(res, 123)
      done()
    }, done).catch(done)
  })
} else {
  factory(function (fn, opts) {
    return redolent(fn, extend({}, opts))
  })
  test('should always load native promise if >= 0.12', function (done) {
    var promise = redolent(function () {
      return 1
    })()

    promise.then(function (num) {
      test.strictEqual(Promise.___nativePromise, true)
      test.strictEqual(promise.___nativePromise, true)
      test.strictEqual(num, 1)
      done()
    }, done).catch(done)
  })
}