(function () {
  'use strict';

  var expect = chai.expect;

  function logger(value, keyChain, nodeInfo) {
    this.push({
      value: value,
      keyChain: keyChain,
      nodeInfo: nodeInfo,
    });
  }

  var fnNoReturn = function () {};
  var fnReturnNull = function () {
    return null;
  };
  var fnReturnBool = function () {
    return true;
  };
  var fnReturnNum = function () {
    return 123;
  };
  var fnReturnStr = function () {
    return 'abc';
  };
  var fnReturnFunc = function () {
    return fnNoReturn;
  };
  var fnReturnObj = function () {
    return {};
  };
  var fnSort = function (arr) {
    return arr.sort(sorter);
    function sorter(a, b) {
      return b < a ? -1 : 1;
    }
  };

  /* eslint max-statements: "off", brace-style: "off" */

  describe('Argument test', function () {
    it('Should process normally when args are normal', function (done) {
      var logs = [];

      var obj = { a: 1, b: { c: 2 } };
      var opts = { xxx: true };
      eachProps(obj, logger.bind(logs), opts);

      expect(obj).to.deep.equal({ a: 1, b: { c: 2 } });
      expect(logs).to.deep.equal([
        {
          value: 1,
          keyChain: 'a',
          nodeInfo: {
            index: 0,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            xxx: true,
            name: 'a',
          },
        },
        {
          value: { c: 2 },
          keyChain: 'b',
          nodeInfo: {
            index: 1,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            xxx: true,
            name: 'b',
          },
        },
        {
          value: 2,
          keyChain: 'b.c',
          nodeInfo: {
            index: 0,
            count: 1,
            depth: 2,
            parent: { c: 2 },
            xxx: true,
            name: 'c',
          },
        },
      ]);
      done();
    });

    it('Should process normally when args are normal (opts.sort is specified', function (done) {
      var logs = [];

      var obj = { a: 1, b: { c: 2 } };
      var opts = { sort: fnSort };
      eachProps(obj, logger.bind(logs), opts);

      expect(obj).to.deep.equal({ a: 1, b: { c: 2 } });
      expect(logs).to.deep.equal([
        {
          value: { c: 2 },
          keyChain: 'b',
          nodeInfo: {
            index: 0,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            sort: fnSort,
            name: 'b',
          },
        },
        {
          value: 2,
          keyChain: 'b.c',
          nodeInfo: {
            index: 0,
            count: 1,
            depth: 2,
            parent: { c: 2 },
            sort: fnSort,
            name: 'c',
          },
        },
        {
          value: 1,
          keyChain: 'a',
          nodeInfo: {
            index: 1,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            sort: fnSort,
            name: 'a',
          },
        },
      ]);
      done();
    });

    it('Should process nothing when 1st arg is undefined', function (done) {
      var logs = [];

      var obj = undefined;
      var opts = {};
      eachProps(obj, logger.bind(logs), opts);

      expect(obj).to.equal(undefined);
      expect(logs).to.deep.equal([]);
      done();
    });

    it('Should process nothing when 1st arg is null', function (done) {
      var logs = [];

      var obj = null;
      var opts = {};
      eachProps(obj, logger.bind(logs), opts);

      expect(obj).to.equal(null);
      expect(logs).to.deep.equal([]);
      done();
    });

    it('Should process nothing when 1st arg is not a boolean', function (done) {
      var logs = [];

      var obj = true;
      var opts = {};
      eachProps(obj, logger.bind(logs), opts);

      expect(obj).to.equal(true);
      expect(logs).to.deep.equal([]);
      done();
    });

    it('Should process nothing when 1st arg is a number', function (done) {
      var logs = [];

      var obj = 9;
      var opts = {};
      eachProps(obj, logger.bind(logs), opts);

      expect(obj).to.equal(9);
      expect(logs).to.deep.equal([]);
      done();
    });

    it('Should process nothing when 1st arg is a string', function (done) {
      var logs = [];

      var obj = 'ABC';
      var opts = {};
      eachProps(obj, logger.bind(logs), opts);

      expect(obj).to.equal('ABC');
      expect(logs).to.deep.equal([]);
      done();
    });

    it('Should process nothing when 1st arg is an array', function (done) {
      var logs = [];

      var obj = [{ a: 1 }, { b: 2 }, { c: 3 }];
      var opts = {};
      eachProps(obj, logger.bind(logs), opts);

      expect(obj).to.deep.equal([{ a: 1 }, { b: 2 }, { c: 3 }]);
      expect(logs).to.deep.equal([]);
      done();
    });

    it('Should process nothing when 1st arg is a function', function (done) {
      var logs = [];

      var obj = fnNoReturn;
      var opts = {};
      eachProps(obj, logger.bind(logs), opts);

      expect(logs).to.deep.equal([]);
      done();
    });

    it('Should process nothing when 1st arg is a typed object', function (done) {
      var logs = [];

      var obj = new Date(0);
      var opts = {};
      eachProps(obj, logger.bind(logs), opts);

      expect(logs).to.deep.equal([]);
      done();
    });

    it('Should process nothing when 1st arg is an empty plain object', function (done) {
      var logs = [];

      var obj = {};
      var opts = {};
      eachProps(obj, logger.bind(logs), opts);

      expect(obj).to.deep.equal({});
      expect(logs).to.deep.equal([]);
      done();
    });

    it('Should process nothing when 2nd arg is undefined', function (done) {
      var obj = { a: 1 };
      var fn = undefined;
      eachProps(obj, fn);
      expect(obj).to.deep.equal({ a: 1 });
      done();
    });

    it('Should process nothing when 2nd arg is null', function (done) {
      var obj = { a: 1 };
      var fn = null;
      eachProps(obj, fn);
      expect(obj).to.deep.equal({ a: 1 });
      done();
    });

    it('Should process nothing when 2nd arg is a boolean', function (done) {
      var obj = { a: 1 };
      var fn = true;
      eachProps(obj, fn);
      expect(obj).to.deep.equal({ a: 1 });
      done();
    });

    it('Should process nothing when 2nd arg is a number', function (done) {
      var obj = { a: 1 };
      var fn = 123;
      eachProps(obj, fn);
      expect(obj).to.deep.equal({ a: 1 });
      done();
    });

    it('Should process nothing when 2nd arg is a string', function (done) {
      var obj = { a: 1 };
      var fn = 'ABC';
      eachProps(obj, fn);
      expect(obj).to.deep.equal({ a: 1 });
      done();
    });

    it('Should process nothing when 2nd arg is an array', function (done) {
      var obj = { a: 1 };
      var fn = ['A', 'B', 'C'];
      eachProps(obj, fn);
      expect(obj).to.deep.equal({ a: 1 });
      done();
    });

    it('Should process nothing when 2nd arg is an plain object', function (done) {
      var obj = { a: 1 };
      var fn = { a: 'A', b: 'B', c: 'C' };
      eachProps(obj, fn);
      expect(obj).to.deep.equal({ a: 1 });
      done();
    });

    it('Should process nothing when 2nd arg is an typed object', function (done) {
      var obj = { a: 1 };
      var fn = new Date(0);
      eachProps(obj, fn);
      expect(obj).to.deep.equal({ a: 1 });
      done();
    });

    it('Should process nothing when 3rd arg is undefined', function (done) {
      var logs = [];

      var obj = { a: 1, b: { c: 2 } };
      var opts = undefined;
      eachProps(obj, logger.bind(logs), opts);

      expect(logs).to.deep.equal([
        {
          value: 1,
          keyChain: 'a',
          nodeInfo: {
            index: 0,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            name: 'a',
          },
        },
        {
          value: { c: 2 },
          keyChain: 'b',
          nodeInfo: {
            index: 1,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            name: 'b',
          },
        },
        {
          value: 2,
          keyChain: 'b.c',
          nodeInfo: {
            index: 0,
            count: 1,
            depth: 2,
            parent: { c: 2 },
            name: 'c',
          },
        },
      ]);
      done();
    });

    it('Should process nothing when 3rd arg is null', function (done) {
      var logs = [];

      var obj = { a: 1, b: { c: 2 } };
      var opts = null;
      eachProps(obj, logger.bind(logs), opts);

      expect(logs).to.deep.equal([
        {
          value: 1,
          keyChain: 'a',
          nodeInfo: {
            index: 0,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            name: 'a',
          },
        },
        {
          value: { c: 2 },
          keyChain: 'b',
          nodeInfo: {
            index: 1,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            name: 'b',
          },
        },
        {
          value: 2,
          keyChain: 'b.c',
          nodeInfo: {
            index: 0,
            count: 1,
            depth: 2,
            parent: { c: 2 },
            name: 'c',
          },
        },
      ]);
      done();
    });

    it('Should process nothing when 3rd arg is a boolean', function (done) {
      var logs = [];

      var obj = { a: 1, b: { c: 2 } };
      var opts = true;
      eachProps(obj, logger.bind(logs), opts);
      expect(logs).to.deep.equal([
        {
          value: 1,
          keyChain: 'a',
          nodeInfo: {
            index: 0,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            name: 'a',
          },
        },
        {
          value: { c: 2 },
          keyChain: 'b',
          nodeInfo: {
            index: 1,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            name: 'b',
          },
        },
        {
          value: 2,
          keyChain: 'b.c',
          nodeInfo: {
            index: 0,
            count: 1,
            depth: 2,
            parent: { c: 2 },
            name: 'c',
          },
        },
      ]);
      done();
    });

    it('Should process nothing when 3rd arg is a number', function (done) {
      var logs = [];

      var obj = { a: 1, b: { c: 2 } };
      var opts = 123;
      eachProps(obj, logger.bind(logs), opts);
      expect(logs).to.deep.equal([
        {
          value: 1,
          keyChain: 'a',
          nodeInfo: {
            index: 0,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            name: 'a',
          },
        },
        {
          value: { c: 2 },
          keyChain: 'b',
          nodeInfo: {
            index: 1,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            name: 'b',
          },
        },
        {
          value: 2,
          keyChain: 'b.c',
          nodeInfo: {
            index: 0,
            count: 1,
            depth: 2,
            parent: { c: 2 },
            name: 'c',
          },
        },
      ]);
      done();
    });

    it('Should process nothing when 3rd arg is a string', function (done) {
      var logs = [];

      var obj = { a: 1, b: { c: 2 } };
      var opts = 'ABC';
      eachProps(obj, logger.bind(logs), opts);
      expect(logs).to.deep.equal([
        {
          value: 1,
          keyChain: 'a',
          nodeInfo: {
            index: 0,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            name: 'a',
          },
        },
        {
          value: { c: 2 },
          keyChain: 'b',
          nodeInfo: {
            index: 1,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            name: 'b',
          },
        },
        {
          value: 2,
          keyChain: 'b.c',
          nodeInfo: {
            index: 0,
            count: 1,
            depth: 2,
            parent: { c: 2 },
            name: 'c',
          },
        },
      ]);
      done();
    });

    it('Should process nothing when 3rd arg is an array', function (done) {
      var logs = [];

      var obj = { a: 1, b: { c: 2 } };
      var opts = [1, 2, 3];
      eachProps(obj, logger.bind(logs), opts);
      expect(logs).to.deep.equal([
        {
          value: 1,
          keyChain: 'a',
          nodeInfo: {
            index: 0,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            name: 'a',
          },
        },
        {
          value: { c: 2 },
          keyChain: 'b',
          nodeInfo: {
            index: 1,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            name: 'b',
          },
        },
        {
          value: 2,
          keyChain: 'b.c',
          nodeInfo: {
            index: 0,
            count: 1,
            depth: 2,
            parent: { c: 2 },
            name: 'c',
          },
        },
      ]);
      done();
    });

    it('Should process nothing when 3rd arg is a function', function (done) {
      var logs = [];

      var obj = { a: 1, b: { c: 2 } };
      var opts = fnNoReturn;
      eachProps(obj, logger.bind(logs), opts);
      expect(logs).to.deep.equal([
        {
          value: 1,
          keyChain: 'a',
          nodeInfo: {
            index: 0,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            name: 'a',
          },
        },
        {
          value: { c: 2 },
          keyChain: 'b',
          nodeInfo: {
            index: 1,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            name: 'b',
          },
        },
        {
          value: 2,
          keyChain: 'b.c',
          nodeInfo: {
            index: 0,
            count: 1,
            depth: 2,
            parent: { c: 2 },
            name: 'c',
          },
        },
      ]);
      done();
    });

    it('Should process nothing when 3rd arg is a typed object', function (done) {
      var logs = [];

      var obj = { a: 1, b: { c: 2 } };
      var opts = new Date(0);
      eachProps(obj, logger.bind(logs), opts);
      expect(logs).to.deep.equal([
        {
          value: 1,
          keyChain: 'a',
          nodeInfo: {
            index: 0,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            name: 'a',
          },
        },
        {
          value: { c: 2 },
          keyChain: 'b',
          nodeInfo: {
            index: 1,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            name: 'b',
          },
        },
        {
          value: 2,
          keyChain: 'b.c',
          nodeInfo: {
            index: 0,
            count: 1,
            depth: 2,
            parent: { c: 2 },
            name: 'c',
          },
        },
      ]);
      done();
    });

    it('Should process nothing when 3rd arg is an empty plain object', function (done) {
      var logs = [];

      var obj = { a: 1, b: { c: 2 } };
      var opts = {};
      eachProps(obj, logger.bind(logs), opts);
      expect(logs).to.deep.equal([
        {
          value: 1,
          keyChain: 'a',
          nodeInfo: {
            index: 0,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            name: 'a',
          },
        },
        {
          value: { c: 2 },
          keyChain: 'b',
          nodeInfo: {
            index: 1,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            name: 'b',
          },
        },
        {
          value: 2,
          keyChain: 'b.c',
          nodeInfo: {
            index: 0,
            count: 1,
            depth: 2,
            parent: { c: 2 },
            name: 'c',
          },
        },
      ]);
      done();
    });

    it('Should not sort keys when `.sort` of 3rd arg is undefined', function (done) {
      var logs = [];

      var obj = { a: 1, b: { c: 2 } };
      var opts = { sort: undefined };
      eachProps(obj, logger.bind(logs), opts);
      expect(logs).to.deep.equal([
        {
          value: 1,
          keyChain: 'a',
          nodeInfo: {
            index: 0,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            sort: undefined,
            name: 'a',
          },
        },
        {
          value: { c: 2 },
          keyChain: 'b',
          nodeInfo: {
            index: 1,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            sort: undefined,
            name: 'b',
          },
        },
        {
          value: 2,
          keyChain: 'b.c',
          nodeInfo: {
            index: 0,
            count: 1,
            depth: 2,
            parent: { c: 2 },
            sort: undefined,
            name: 'c',
          },
        },
      ]);
      done();
    });

    it('Should not sort keys when `.sort` of 3rd arg is null', function (done) {
      var logs = [];

      var obj = { a: 1, b: { c: 2 } };
      var opts = { sort: null };
      eachProps(obj, logger.bind(logs), opts);
      expect(logs).to.deep.equal([
        {
          value: 1,
          keyChain: 'a',
          nodeInfo: {
            index: 0,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            sort: null,
            name: 'a',
          },
        },
        {
          value: { c: 2 },
          keyChain: 'b',
          nodeInfo: {
            index: 1,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            sort: null,
            name: 'b',
          },
        },
        {
          value: 2,
          keyChain: 'b.c',
          nodeInfo: {
            index: 0,
            count: 1,
            depth: 2,
            parent: { c: 2 },
            sort: null,
            name: 'c',
          },
        },
      ]);
      done();
    });

    it('Should not sort keys when `.sort` of 3rd arg is a boolean', function (done) {
      var logs = [];

      var obj = { a: 1, b: { c: 2 } };
      var opts = { sort: true };
      eachProps(obj, logger.bind(logs), opts);
      expect(logs).to.deep.equal([
        {
          value: 1,
          keyChain: 'a',
          nodeInfo: {
            index: 0,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            sort: true,
            name: 'a',
          },
        },
        {
          value: { c: 2 },
          keyChain: 'b',
          nodeInfo: {
            index: 1,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            sort: true,
            name: 'b',
          },
        },
        {
          value: 2,
          keyChain: 'b.c',
          nodeInfo: {
            index: 0,
            count: 1,
            depth: 2,
            parent: { c: 2 },
            sort: true,
            name: 'c',
          },
        },
      ]);
      done();
    });

    it('Should not sort keys when `.sort` of 3rd arg is a number', function (done) {
      var logs = [];

      var obj = { a: 1, b: { c: 2 } };
      var opts = { sort: 123 };
      eachProps(obj, logger.bind(logs), opts);
      expect(logs).to.deep.equal([
        {
          value: 1,
          keyChain: 'a',
          nodeInfo: {
            index: 0,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            sort: 123,
            name: 'a',
          },
        },
        {
          value: { c: 2 },
          keyChain: 'b',
          nodeInfo: {
            index: 1,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            sort: 123,
            name: 'b',
          },
        },
        {
          value: 2,
          keyChain: 'b.c',
          nodeInfo: {
            index: 0,
            count: 1,
            depth: 2,
            parent: { c: 2 },
            sort: 123,
            name: 'c',
          },
        },
      ]);
      done();
    });

    it('Should not sort keys when `.sort` of 3rd arg is a string', function (done) {
      var logs = [];

      var obj = { a: 1, b: { c: 2 } };
      var opts = { sort: 'ABC' };
      eachProps(obj, logger.bind(logs), opts);
      expect(logs).to.deep.equal([
        {
          value: 1,
          keyChain: 'a',
          nodeInfo: {
            index: 0,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            sort: 'ABC',
            name: 'a',
          },
        },
        {
          value: { c: 2 },
          keyChain: 'b',
          nodeInfo: {
            index: 1,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            sort: 'ABC',
            name: 'b',
          },
        },
        {
          value: 2,
          keyChain: 'b.c',
          nodeInfo: {
            index: 0,
            count: 1,
            depth: 2,
            parent: { c: 2 },
            sort: 'ABC',
            name: 'c',
          },
        },
      ]);
      done();
    });

    it('Should not sort keys when `.sort` of 3rd arg is an array', function (done) {
      var logs = [];

      var obj = { a: 1, b: { c: 2 } };
      var opts = { sort: [1, 2, 3] };
      eachProps(obj, logger.bind(logs), opts);
      expect(logs).to.deep.equal([
        {
          value: 1,
          keyChain: 'a',
          nodeInfo: {
            index: 0,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            sort: [1, 2, 3],
            name: 'a',
          },
        },
        {
          value: { c: 2 },
          keyChain: 'b',
          nodeInfo: {
            index: 1,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            sort: [1, 2, 3],
            name: 'b',
          },
        },
        {
          value: 2,
          keyChain: 'b.c',
          nodeInfo: {
            index: 0,
            count: 1,
            depth: 2,
            parent: { c: 2 },
            sort: [1, 2, 3],
            name: 'c',
          },
        },
      ]);
      done();
    });

    it('Should not sort keys when `.sort` of 3rd arg is a object', function (done) {
      var logs = [];

      var obj = { a: 1, b: { c: 2 } };
      var opts = { sort: { x: 1 } };
      eachProps(obj, logger.bind(logs), opts);
      expect(logs).to.deep.equal([
        {
          value: 1,
          keyChain: 'a',
          nodeInfo: {
            index: 0,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            sort: { x: 1 },
            name: 'a',
          },
        },
        {
          value: { c: 2 },
          keyChain: 'b',
          nodeInfo: {
            index: 1,
            count: 2,
            depth: 1,
            parent: { a: 1, b: { c: 2 } },
            sort: { x: 1 },
            name: 'b',
          },
        },
        {
          value: 2,
          keyChain: 'b.c',
          nodeInfo: {
            index: 0,
            count: 1,
            depth: 2,
            parent: { c: 2 },
            sort: { x: 1 },
            name: 'c',
          },
        },
      ]);
      done();
    });

    it(
      'Should not sort keys when `.sort` of 3rd arg is a function which' +
        '\n\treturns nothing',
      function (done) {
        var logs = [];

        var obj = { a: 1, b: { c: 2 } };
        var opts = { sort: fnNoReturn };
        eachProps(obj, logger.bind(logs), opts);
        expect(logs).to.deep.equal([
          {
            value: 1,
            keyChain: 'a',
            nodeInfo: {
              index: 0,
              count: 2,
              depth: 1,
              parent: { a: 1, b: { c: 2 } },
              sort: fnNoReturn,
              name: 'a',
            },
          },
          {
            value: { c: 2 },
            keyChain: 'b',
            nodeInfo: {
              index: 1,
              count: 2,
              depth: 1,
              parent: { a: 1, b: { c: 2 } },
              sort: fnNoReturn,
              name: 'b',
            },
          },
          {
            value: 2,
            keyChain: 'b.c',
            nodeInfo: {
              index: 0,
              count: 1,
              depth: 2,
              parent: { c: 2 },
              sort: fnNoReturn,
              name: 'c',
            },
          },
        ]);
        done();
      }
    );

    it(
      'Should not sort keys when `.sort` of 3rd arg is a function which' +
        '\n\treturns null',
      function (done) {
        var logs = [];

        var obj = { a: 1, b: { c: 2 } };
        var opts = { sort: fnReturnNull };
        eachProps(obj, logger.bind(logs), opts);
        expect(logs).to.deep.equal([
          {
            value: 1,
            keyChain: 'a',
            nodeInfo: {
              index: 0,
              count: 2,
              depth: 1,
              parent: { a: 1, b: { c: 2 } },
              sort: fnReturnNull,
              name: 'a',
            },
          },
          {
            value: { c: 2 },
            keyChain: 'b',
            nodeInfo: {
              index: 1,
              count: 2,
              depth: 1,
              parent: { a: 1, b: { c: 2 } },
              sort: fnReturnNull,
              name: 'b',
            },
          },
          {
            value: 2,
            keyChain: 'b.c',
            nodeInfo: {
              index: 0,
              count: 1,
              depth: 2,
              parent: { c: 2 },
              sort: fnReturnNull,
              name: 'c',
            },
          },
        ]);
        done();
      }
    );

    it(
      'Should not sort keys when `.sort` of 3rd arg is a function which' +
        '\n\treturns a boolean',
      function (done) {
        var logs = [];

        var obj = { a: 1, b: { c: 2 } };
        var opts = { sort: fnReturnBool };
        eachProps(obj, logger.bind(logs), opts);
        expect(logs).to.deep.equal([
          {
            value: 1,
            keyChain: 'a',
            nodeInfo: {
              index: 0,
              count: 2,
              depth: 1,
              parent: { a: 1, b: { c: 2 } },
              sort: fnReturnBool,
              name: 'a',
            },
          },
          {
            value: { c: 2 },
            keyChain: 'b',
            nodeInfo: {
              index: 1,
              count: 2,
              depth: 1,
              parent: { a: 1, b: { c: 2 } },
              sort: fnReturnBool,
              name: 'b',
            },
          },
          {
            value: 2,
            keyChain: 'b.c',
            nodeInfo: {
              index: 0,
              count: 1,
              depth: 2,
              parent: { c: 2 },
              sort: fnReturnBool,
              name: 'c',
            },
          },
        ]);
        done();
      }
    );

    it(
      'Should not sort keys when `.sort` of 3rd arg is a function which' +
        '\n\treturns a number',
      function (done) {
        var logs = [];

        var obj = { a: 1, b: { c: 2 } };
        var opts = { sort: fnReturnNum };
        eachProps(obj, logger.bind(logs), opts);
        expect(logs).to.deep.equal([
          {
            value: 1,
            keyChain: 'a',
            nodeInfo: {
              index: 0,
              count: 2,
              depth: 1,
              parent: { a: 1, b: { c: 2 } },
              sort: fnReturnNum,
              name: 'a',
            },
          },
          {
            value: { c: 2 },
            keyChain: 'b',
            nodeInfo: {
              index: 1,
              count: 2,
              depth: 1,
              parent: { a: 1, b: { c: 2 } },
              sort: fnReturnNum,
              name: 'b',
            },
          },
          {
            value: 2,
            keyChain: 'b.c',
            nodeInfo: {
              index: 0,
              count: 1,
              depth: 2,
              parent: { c: 2 },
              sort: fnReturnNum,
              name: 'c',
            },
          },
        ]);
        done();
      }
    );

    it(
      'Should not sort keys when `.sort` of 3rd arg is a function which' +
        '\n\treturns a string',
      function (done) {
        var logs = [];

        var obj = { a: 1, b: { c: 2 } };
        var opts = { sort: fnReturnStr };
        eachProps(obj, logger.bind(logs), opts);
        expect(logs).to.deep.equal([
          {
            value: 1,
            keyChain: 'a',
            nodeInfo: {
              index: 0,
              count: 2,
              depth: 1,
              parent: { a: 1, b: { c: 2 } },
              sort: fnReturnStr,
              name: 'a',
            },
          },
          {
            value: { c: 2 },
            keyChain: 'b',
            nodeInfo: {
              index: 1,
              count: 2,
              depth: 1,
              parent: { a: 1, b: { c: 2 } },
              sort: fnReturnStr,
              name: 'b',
            },
          },
          {
            value: 2,
            keyChain: 'b.c',
            nodeInfo: {
              index: 0,
              count: 1,
              depth: 2,
              parent: { c: 2 },
              sort: fnReturnStr,
              name: 'c',
            },
          },
        ]);
        done();
      }
    );

    it(
      'Should not sort keys when `.sort` of 3rd arg is a function which' +
        '\n\treturns a function',
      function (done) {
        var logs = [];

        var obj = { a: 1, b: { c: 2 } };
        var opts = { sort: fnReturnFunc };
        eachProps(obj, logger.bind(logs), opts);
        expect(logs).to.deep.equal([
          {
            value: 1,
            keyChain: 'a',
            nodeInfo: {
              index: 0,
              count: 2,
              depth: 1,
              parent: { a: 1, b: { c: 2 } },
              sort: fnReturnFunc,
              name: 'a',
            },
          },
          {
            value: { c: 2 },
            keyChain: 'b',
            nodeInfo: {
              index: 1,
              count: 2,
              depth: 1,
              parent: { a: 1, b: { c: 2 } },
              sort: fnReturnFunc,
              name: 'b',
            },
          },
          {
            value: 2,
            keyChain: 'b.c',
            nodeInfo: {
              index: 0,
              count: 1,
              depth: 2,
              parent: { c: 2 },
              sort: fnReturnFunc,
              name: 'c',
            },
          },
        ]);
        done();
      }
    );

    it(
      'Should not sort keys when `.sort` of 3rd arg is a function which' +
        '\n\treturns an object',
      function (done) {
        var logs = [];

        var obj = { a: 1, b: { c: 2 } };
        var opts = { sort: fnReturnObj };
        eachProps(obj, logger.bind(logs), opts);
        expect(logs).to.deep.equal([
          {
            value: 1,
            keyChain: 'a',
            nodeInfo: {
              index: 0,
              count: 2,
              depth: 1,
              parent: { a: 1, b: { c: 2 } },
              sort: fnReturnObj,
              name: 'a',
            },
          },
          {
            value: { c: 2 },
            keyChain: 'b',
            nodeInfo: {
              index: 1,
              count: 2,
              depth: 1,
              parent: { a: 1, b: { c: 2 } },
              sort: fnReturnObj,
              name: 'b',
            },
          },
          {
            value: 2,
            keyChain: 'b.c',
            nodeInfo: {
              index: 0,
              count: 1,
              depth: 2,
              parent: { c: 2 },
              sort: fnReturnObj,
              name: 'c',
            },
          },
        ]);
        done();
      }
    );
  });
})();
