New matcher "hashContaining" similar to rspec's hash_including
This commit is contained in:
@@ -196,6 +196,21 @@ jasmine.any = function(clazz) {
|
||||
return new jasmine.Matchers.Any(clazz);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a matchable subset of a hash/JSON object. For use in expectations when you don't care about all of the
|
||||
* attributes on the object.
|
||||
*
|
||||
* @example
|
||||
* // don't care about any other attributes than foo.
|
||||
* expect(mySpy).toHaveBeenCalledWith(jasmine.hashContaining({foo: "bar"});
|
||||
*
|
||||
* @param sample {Object} sample
|
||||
* @returns matchable object for the sample
|
||||
*/
|
||||
jasmine.hashContaining = function (sample) {
|
||||
return new jasmine.Matchers.HashContaining(sample);
|
||||
};
|
||||
|
||||
/**
|
||||
* Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
|
||||
*
|
||||
@@ -922,6 +937,14 @@ jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
|
||||
return b.matches(a);
|
||||
}
|
||||
|
||||
if (a instanceof jasmine.Matchers.HashContaining) {
|
||||
return a.matches(b);
|
||||
}
|
||||
|
||||
if (b instanceof jasmine.Matchers.HashContaining) {
|
||||
return b.matches(a);
|
||||
}
|
||||
|
||||
if (jasmine.isString_(a) && jasmine.isString_(b)) {
|
||||
return (a == b);
|
||||
}
|
||||
@@ -1477,6 +1500,35 @@ jasmine.Matchers.Any.prototype.toString = function() {
|
||||
return '<jasmine.any(' + this.expectedClass + ')>';
|
||||
};
|
||||
|
||||
jasmine.Matchers.HashContaining = function (sample) {
|
||||
this.sample = sample;
|
||||
};
|
||||
|
||||
jasmine.Matchers.HashContaining.prototype.matches = function(other, mismatchKeys, mismatchValues) {
|
||||
mismatchKeys = mismatchKeys || [];
|
||||
mismatchValues = mismatchValues || [];
|
||||
|
||||
var env = jasmine.getEnv();
|
||||
|
||||
var hasKey = function(obj, keyName) {
|
||||
return obj != null && obj[keyName] !== jasmine.undefined;
|
||||
};
|
||||
|
||||
for (var property in this.sample) {
|
||||
if (!hasKey(other, property) && hasKey(this.sample, property)) {
|
||||
mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
|
||||
}
|
||||
else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) {
|
||||
mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual.");
|
||||
}
|
||||
}
|
||||
|
||||
return (mismatchKeys.length === 0 && mismatchValues.length === 0);
|
||||
};
|
||||
|
||||
jasmine.Matchers.HashContaining.prototype.toString = function () {
|
||||
return "<jasmine.hashContaining(" + jasmine.pp(this.sample) + ")>";
|
||||
};
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
@@ -830,6 +830,90 @@ describe("jasmine.Matchers", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("HashContaining", function () {
|
||||
describe("with an empty hash", function () {
|
||||
var containing;
|
||||
beforeEach(function () {
|
||||
containing = new jasmine.Matchers.HashContaining({});
|
||||
});
|
||||
it("matches everything", function () {
|
||||
expect(containing.matches("foo", [], [])).toBe(true);
|
||||
});
|
||||
|
||||
it("says it didn't expect to contain anything", function () {
|
||||
expect(containing.toString()).toEqual("<jasmine.hashContaining({ })>");
|
||||
});
|
||||
});
|
||||
|
||||
describe("with a hash with items in it", function () {
|
||||
var containing, mismatchKeys, mismatchValues;
|
||||
beforeEach(function () {
|
||||
mismatchKeys = [];
|
||||
mismatchValues = [];
|
||||
containing = new jasmine.Matchers.HashContaining({foo: "fooVal", bar: "barVal"});
|
||||
});
|
||||
|
||||
it("doesn't match an empty object", function () {
|
||||
expect(containing.matches({}, mismatchKeys, mismatchValues)).toBe(false);
|
||||
});
|
||||
|
||||
it("doesn't match an object with none of the specified options", function () {
|
||||
expect(containing.matches({baz:"stuff"}, mismatchKeys, mismatchValues)).toBe(false);
|
||||
});
|
||||
|
||||
it("adds a message for each missing key", function () {
|
||||
containing.matches({foo: "fooVal"}, mismatchKeys, mismatchValues);
|
||||
expect(mismatchKeys.length).toEqual(1);
|
||||
});
|
||||
|
||||
it("doesn't match an object when the values are different", function () {
|
||||
expect(containing.matches({foo:"notFoo", bar:"notBar"}, mismatchKeys, mismatchValues)).toBe(false);
|
||||
});
|
||||
|
||||
it("adds a message when values don't match", function () {
|
||||
containing.matches({foo: "fooVal", bar: "notBar"}, mismatchKeys, mismatchValues);
|
||||
expect(mismatchValues.length).toEqual(1);
|
||||
});
|
||||
|
||||
it("doesn't match an object with only one of the values matching", function () {
|
||||
expect(containing.matches({foo:"notFoo", bar:"barVal"}, mismatchKeys, mismatchValues)).toBe(false);
|
||||
});
|
||||
|
||||
it("matches when all the values are the same", function () {
|
||||
expect(containing.matches({foo: "fooVal", bar: "barVal"}, mismatchKeys, mismatchValues)).toBe(true);
|
||||
});
|
||||
|
||||
it("matches when there are additional values", function () {
|
||||
expect(containing.matches({foo: "fooVal", bar: "barVal", baz: "bazVal"}, mismatchKeys, mismatchValues)).toBe(true);
|
||||
});
|
||||
|
||||
it("doesn't modify missingKeys or missingValues when match is successful", function () {
|
||||
containing.matches({foo: "fooVal", bar: "barVal"}, mismatchKeys, mismatchValues);
|
||||
expect(mismatchKeys.length).toEqual(0);
|
||||
expect(mismatchValues.length).toEqual(0);
|
||||
});
|
||||
|
||||
it("says what it expects to contain", function () {
|
||||
expect(containing.toString()).toEqual("<jasmine.hashContaining(" + jasmine.pp({foo:"fooVal", bar:"barVal"}) + ")>");
|
||||
});
|
||||
});
|
||||
|
||||
describe("in real life", function () {
|
||||
var method;
|
||||
beforeEach(function () {
|
||||
method = jasmine.createSpy("method");
|
||||
method({a:"b", c:"d"});
|
||||
});
|
||||
it("works correctly for positive matches", function () {
|
||||
expect(method).toHaveBeenCalledWith(jasmine.hashContaining({a:"b"}));
|
||||
});
|
||||
|
||||
it("works correctly for negative matches", function () {
|
||||
expect(method).not.toHaveBeenCalledWith(jasmine.hashContaining({z:"x"}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("all matchers", function() {
|
||||
it("should return null, for future-proofing, since we might eventually allow matcher chaining", function() {
|
||||
expect(match(true).toBe(true)).toBeUndefined();
|
||||
|
||||
@@ -238,6 +238,14 @@ jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
|
||||
return b.matches(a);
|
||||
}
|
||||
|
||||
if (a instanceof jasmine.Matchers.HashContaining) {
|
||||
return a.matches(b);
|
||||
}
|
||||
|
||||
if (b instanceof jasmine.Matchers.HashContaining) {
|
||||
return b.matches(a);
|
||||
}
|
||||
|
||||
if (jasmine.isString_(a) && jasmine.isString_(b)) {
|
||||
return (a == b);
|
||||
}
|
||||
|
||||
@@ -369,3 +369,32 @@ jasmine.Matchers.Any.prototype.toString = function() {
|
||||
return '<jasmine.any(' + this.expectedClass + ')>';
|
||||
};
|
||||
|
||||
jasmine.Matchers.HashContaining = function (sample) {
|
||||
this.sample = sample;
|
||||
};
|
||||
|
||||
jasmine.Matchers.HashContaining.prototype.matches = function(other, mismatchKeys, mismatchValues) {
|
||||
mismatchKeys = mismatchKeys || [];
|
||||
mismatchValues = mismatchValues || [];
|
||||
|
||||
var env = jasmine.getEnv();
|
||||
|
||||
var hasKey = function(obj, keyName) {
|
||||
return obj != null && obj[keyName] !== jasmine.undefined;
|
||||
};
|
||||
|
||||
for (var property in this.sample) {
|
||||
if (!hasKey(other, property) && hasKey(this.sample, property)) {
|
||||
mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
|
||||
}
|
||||
else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) {
|
||||
mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual.");
|
||||
}
|
||||
}
|
||||
|
||||
return (mismatchKeys.length === 0 && mismatchValues.length === 0);
|
||||
};
|
||||
|
||||
jasmine.Matchers.HashContaining.prototype.toString = function () {
|
||||
return "<jasmine.hashContaining(" + jasmine.pp(this.sample) + ")>";
|
||||
};
|
||||
|
||||
@@ -196,6 +196,21 @@ jasmine.any = function(clazz) {
|
||||
return new jasmine.Matchers.Any(clazz);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a matchable subset of a hash/JSON object. For use in expectations when you don't care about all of the
|
||||
* attributes on the object.
|
||||
*
|
||||
* @example
|
||||
* // don't care about any other attributes than foo.
|
||||
* expect(mySpy).toHaveBeenCalledWith(jasmine.hashContaining({foo: "bar"});
|
||||
*
|
||||
* @param sample {Object} sample
|
||||
* @returns matchable object for the sample
|
||||
*/
|
||||
jasmine.hashContaining = function (sample) {
|
||||
return new jasmine.Matchers.HashContaining(sample);
|
||||
};
|
||||
|
||||
/**
|
||||
* Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user