lib/subject/mapSubject.js

/** @module certainty */
var Subject = require('./subject');
var ProxyBase = require('./proxy');

/** A fluent context object containing the value of the field that was just tested. Used for
    additional assertions about a field.
    @constructor
  */
function KeyValue(subject, key) {
  this.subject = subject;
  this.key = key;
  this.format = subject.format;
  this.value = subject.value.get(key);
}

/** Ensure that the field has the expected value.
    @param {*} value The expected value of the field.
*/
KeyValue.prototype.withValue = function (expected) {
  if (this.value != expected) {
    this.subject.fail('Expected ' + this.subject.describe() + ' to contain key ' +
      this.format(this.key) + ' with value ' + this.format(expected) + ', actual value was ' +
      this.value + '.');
  }
}
ProxyBase.addMethods(KeyValue);

// Used when the key is missing, so that .withValue() doesn't produce a second error message.
function MissingKeyValue() {}
MissingKeyValue.prototype.withValue = function (_expected) {
  return this;
}

/** Subclass of Subject which provides assertions methods for Map types.
    @param {FailureStrategy} failureStrategy The failure strategy to use when an assertion fails.
    @param {object} value The value being checked.
    @constructor
    @extends Subject
*/
function MapSubject(failureStrategy, value) {
  Subject.call(this, failureStrategy, value);
}
MapSubject.prototype = Object.create(Subject.prototype);
MapSubject.prototype.constructor = MapSubject;

/** Ensure that the map is empty.
    @return {MapSubject} `this` for chaining.
*/
MapSubject.prototype.isEmpty = function () {
  if (this.value.size !== 0) {
    this.fail('Expected ' + this.describe() + ' to be an empty map.');
  }
  return this;
};

/** Ensure that the map is non-empty.
    @return {MapSubject} `this` for chaining.
*/
MapSubject.prototype.isNotEmpty = function () {
  if (this.value.size === 0) {
    this.fail('Expected ' + this.describe() + ' to be a non-empty map.');
  }
  return this;
};

/** Ensure that the map is the expected size.
    @param {number} size The expected size of the map.
    @return {MapSubject} `this` for chaining.
*/
MapSubject.prototype.hasSize = function (size) {
  if (this.value.size !== size) {
    this.fail('Expected ' + this.describe() + ' to be of size ' + size +
      ', actual size was ' + this.value.size + '.');
  }
  return this;
};

/** Ensure that the map contains a given key.
    @return {MapSubject} `this` for chaining.
    @param {*} key The key expected to be in the map.
*/
MapSubject.prototype.containsKey = function (key) {
  if (!this.value.has(key)) {
    this.fail('Expected ' + this.describe() + ' to contain key ' + this.format(key) + '.');
    return new MissingKeyValue();
  }
  return new KeyValue(this, key);
};

/** Ensure that the map does not contain a given key.
    @param {*} key The key not expected to be in the map.
    @return {MapSubject} `this` for chaining.
*/
MapSubject.prototype.doesNotContainKey = function (key) {
  if (this.value.has(key)) {
    this.fail('Expected ' + this.describe() + ' to not contain key ' +
      this.format(key) + '.');
  }
  return this;
};

/** Ensure that the map contains a given key / value pair.
    @param {*} key The key expected to be in the map.
    @param {*} value The expected value for that key.
    @return {MapSubject} `this` for chaining.
*/
MapSubject.prototype.containsEntry = function (key, value) {
  if (!this.value.has(key)) {
    this.fail('Expected ' + this.describe() + ' to contain key ' + this.format(key) + '.');
  } else if (this.value.get(key) != value) {
    this.fail('Expected ' + this.describe() + ' to contain key ' + this.format(key) +
      ' with value ' + this.format(value) + ', actual value was ' +
      this.format(this.value.get(key)) + '.');
  }
  return this;
};

module.exports = MapSubject;