Skip to content

Instantly share code, notes, and snippets.

@onechiporenko
Last active December 11, 2015 17:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save onechiporenko/2454aa9f963bd9e6583b to your computer and use it in GitHub Desktop.
Save onechiporenko/2454aa9f963bd9e6583b to your computer and use it in GitHub Desktop.
Testing Ember.computed.customAnd and customOr
// some imports are skipped
const {
    computed,
    get,
    Object: eO,
    propertyWillChange,
    propertyDidChange,
    tryInvoke
} = Ember;

/**
 * Generates array of all possible boolean combinations
 * Example:
 * <code>
 *   var keys = ['a', 'b'];
 *   var result = getBinaryCombos(keys);
 *   console.log(result); // [{a: true, b: true}, {a: true, b: false}, {a: false, b: true}, {a: false, b: false}]
 * </code>
 *
 * @param {string[]} items
 */
function getBinaryCombos(items) {
  var result = [];
  var l = items.length;
  var combosCount = Math.pow(2, l);
  for (var i = 0; i < combosCount; i++) {
    var combo = {};
    for (var j = 0; j < l; j++) {
      var x = Math.pow(2, j);
      var key = items[j];
      combo[key] = !!(i & x);
    }
    result.push(combo);
  }
  return result;
}

/**
 * Stub `get`-method for `context`
 * Example:
 * <code>
 *  var hash = {a: 1, b: 2};
 *  var obj = Ember.Object.create({a: 4, b: 5, c: 6});
 *  stubGet(obj, hash);
 *  console.log(obj.get('a')); // 1
 *  console.log(obj.get('b')); // 2
 *  console.log(obj.get('c')); // 6
 * </code>
 * 
 * @param {Ember.Object} context
 * @param {object} hash result of `getBinaryCombos`
 */
function stubGet(context, hash) {
  sinon.stub(context, 'get', function (k) {
    if (hash.hasOwnProperty(k)) {
      return hash[k];
    }
    return get(context, k);
  });
}

/**
 * Check if two object have same keys with same values
 * 
 * @param {object} hash1
 * @param {object} hash2
 */
function checkBinaryCombosEquality(hash1, hash2) {
  if (Object.keys(hash1).length !== Object.keys(hash2).length) {
    return false;
  }
  function subObj(obj1, obj2) {
    var keys = Object.keys(obj1);
    for (let i = 0; i < keys.length; i++) {
      const k = keys[i];
      if (obj1[k] !== obj2[k]) {
        return false;
      }
    }
    return true;
  }
  if (!subObj(o1, o2) || !subObj(o2, o1)) {
    return false;
  }
  return true;
}

/**
 * Get boolean combination for string-keys
 * Example:
 * <code>
 * var dependentKeys = ['a', '!b', 'c'];
 * var result = getTrulyCombination(dependentKeys);
 * console.log(result); // {a: true, b: false, c: true}
 * </code>
 *
 * @param {string[]} dependentKeys
 */
function getTrulyCombination(dependentKeys) {
  var hash = {};
  dependentKeys.forEach(key => {
    if ('!' === key[0]) {
      hash[key.substr(1)] = false;
    }
    else {
      hash[key] = true;
    }
  };
  return hash;
}

/**
 * Get boolean combination for string-keys
 * Example:
 * <code>
 * var dependentKeys = ['a', '!b', 'c'];
 * var result = getFalsyCombination(dependentKeys);
 * console.log(result); // {a: false, b: true, c: false}
 * </code>
 *
 * @param {string[]} dependentKeys
 */
function getFalsyCombination(dependentKeys) {
  var hash = {};
  dependentKeys.forEach(function (key) {
    if (key.startsWith('!')) {
      hash[key.substr(1)] = true;
    }
    else {
      hash[key] = false;
    }
  });
  return hash;
}

/**
 * @param {Ember.Object} context
 * @param {string} propertyName
 * @param {string[]} dependentKeys
 */
function testAsComputedCustomAnd(context, propertyName, dependentKeys) {

    var realKeys = dependentKeys.invoke('replace', '!', '');
    var trulyCombination = getTrulyCombination(dependentKeys);
    var binaryCombos = getBinaryCombos(realKeys);

    describe(`#${propertyName} as Ember.computed.customAnd`, function () {

        afterEach(function () {
            Ember.tryInvoke(context.get, 'restore');
        });

        it('has valid dependent keys (without `!`)', function() {
            expect(context[propertyName]._dependentKeys).to.include.members(realKeys);
            expect(realKeys).to.include.members(context[propertyName]._dependentKeys);
        });

        binaryCombos.forEach(function (combo) {
            var expectedResult = checkBinaryCombosEquality(trulyCombination, combo);

            it(`${expectedResult} for ${JSON.stringify(combo)}`, function () {
                propertyWillChange(context, propertyName);
                stubGet(context, combo);
                propertyDidChange(context, propertyName);
                var value = context.get(propertyName);
                expect(value).to.equal(expectedResult);
            });

        });

    });

}

/**
 * @param {Ember.Object} context
 * @param {string} propertyName
 * @param {string[]} dependentKeys
 */
function testAsComputedCustomOr(context, propertyName, dependentKeys) {

    var realKeys = dependentKeys.invoke('replace', '!', '');
    var falsyCombination = getFalsyCombination(dependentKeys);
    var binaryCombos = getBinaryCombos(realKeys);

    describe(`#${propertyName} as Ember.computed.customOr`, function () {

        afterEach(function () {
            tryInvoke(context.get, 'restore');
        });

        it('has valid dependent keys (without `!`)', function() {
            expect(context[propertyName]._dependentKeys).to.include.members(realKeys);
            expect(realKeys).to.include.members(context[propertyName]._dependentKeys);
        });

        binaryCombos.forEach(function (combo) {
            var expectedResult = !checkBinaryCombosEquality(falsyCombination, combo);

            it(`${expectedResult} for ${JSON.stringify(combo)}`, function () {
                propertyWillChange(context, propertyName);
                stubGet(context, combo);
                propertyDidChange(context, propertyName);
                var value = context.get(propertyName);
                expect(value).to.equal(expectedResult);
            });

        });

    });

}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment