'use strict';

var assert = require('assert');
var Promise = require('../');

var fulfilled = Promise.resolve.bind(Promise);
var rejected = Promise.reject.bind(Promise);
var pending = Promise.defer.bind(Promise);

describe('Promise.props', function() {

  specify('should reject undefined', function() {
    return Promise.props().then(assert.fail, function(e) {
      assert(e instanceof TypeError);
    });
  });

  specify('should reject primitive', function() {
    return Promise.props('str').then(assert.fail, function(e) {
      assert(e instanceof TypeError);
    });
  });

  specify('should resolve to new object', function() {
    var o = {};
    return Promise.props(o).then(function(v) {
      assert(v !== o);
      assert.deepEqual(o, v);
    });
  });

  specify('should resolve value properties', function() {
    var o = {
      one: 1,
      two: 2,
      three: 3,
    };
    return Promise.props(o).then(function(v) {
      assert.deepEqual({
        one: 1,
        two: 2,
        three: 3,
      }, v);
    });
  });

  specify('should resolve immediate properties', function() {
    var o = {
      one: fulfilled(1),
      two: fulfilled(2),
      three: fulfilled(3),
    };
    return Promise.props(o).then(function(v) {
      assert.deepEqual({
        one: 1,
        two: 2,
        three: 3,
      }, v);
    });
  });

  specify('should resolve eventual properties', function() {
    var d1 = pending();
    var d2 = pending();
    var d3 = pending();
    var o = {
      one: d1.promise,
      two: d2.promise,
      three: d3.promise,
    };
    setTimeout(function() {
      d1.resolve(1);
      d2.resolve(2);
      d3.resolve(3);
    }, 13);
    return Promise.props(o).then(function(v) {
      assert.deepEqual({
        one: 1,
        two: 2,
        three: 3,
      }, v);
    });
  });

  specify('should reject if any input promise rejects', function() {
    var o = {
      one: fulfilled(1),
      two: rejected(2),
      three: fulfilled(3),
    };
    return Promise.props(o).then(assert.fail, function(v) {
      assert(v === 2);
    });
  });

  specify('should accept a promise for an object', function() {
    var o = {
      one: fulfilled(1),
      two: fulfilled(2),
      three: fulfilled(3),
    };
    var d1 = pending();
    setTimeout(function() {
      d1.resolve(o);
    }, 13);
    return Promise.props(d1.promise).then(function(v) {
      assert.deepEqual({
        one: 1,
        two: 2,
        three: 3,
      }, v);
    });
  });

  specify('should reject a promise for a primitive', function() {
    var d1 = pending();
    setTimeout(function() {
      d1.resolve('text');
    }, 13);
    return Promise.props(d1.promise).then(assert.fail, function(e) {
      assert(e instanceof TypeError);
    });
  });

  specify('should accept thenables in properties', function() {
    var t1 = {then: function(cb) {cb(1);}};
    var t2 = {then: function(cb) {cb(2);}};
    var t3 = {then: function(cb) {cb(3);}};
    var o = {
      one: t1,
      two: t2,
      three: t3,
    };
    return Promise.props(o).then(function(v) {
      assert.deepEqual({
        one: 1,
        two: 2,
        three: 3,
      }, v);
    });
  });

  specify('should accept a thenable for thenables in properties', function() {
    var o = {
      then: function(f) {
        f({
          one: {
            then: function(cb) {
              cb(1);
            },
          },
          two: {
            then: function(cb) {
              cb(2);
            },
          },
          three: {
            then: function(cb) {
              cb(3);
            },
          },
        });
      },
    };
    return Promise.props(o).then(function(v) {
      assert.deepEqual({
        one: 1,
        two: 2,
        three: 3,
      }, v);
    });
  });

  // jscs: disable requireCapitalizedComments
  /*
    specify('sends { key, value } progress updates', function(done) {
    var deferred1 = Q.defer();
    var deferred2 = Q.defer();

    var progressValues = [];

    Q.delay(50).then(function () {
    deferred1.notify('a');
    });
    Q.delay(100).then(function () {
    deferred2.notify('b');
    deferred2.resolve();
    });
    Q.delay(150).then(function () {
    deferred1.notify('c');
    deferred1.resolve();
    });

    Promise.props({
    one: deferred1.promise,
    two: deferred2.promise
    }).then(function () {
    assert.deepEqual(progressValues, [
    { key: 'one', value: 'a' },
    { key: 'two', value: 'b' },
    { key: 'one', value: 'c' }
    ]);
    done();
    },
    undefined,
    function (progressValue) {
    progressValues.push(progressValue);
    });
    });
  */
  // jscs: enable requireCapitalizedComments

  specify('treats arrays for their properties', function() {
    var o = [1, 2, 3];

    return Promise.props(o).then(function(v) {
      assert.deepEqual({
        0: 1,
        1: 2,
        2: 3,
      }, v);
    });
  });

});
