From c5dfaaeec7bd21e766d26a344cc39ac8bcca1c90 Mon Sep 17 00:00:00 2001 From: Niv Sherf Date: Sun, 21 Jan 2018 11:48:54 +0200 Subject: [PATCH] Add match.every and match.some (#1624) (#1661) * Add match.every (#1624) Added new matcher - match.every, which matches on an object or iterable if all of the properties (or elements, respectively) match the given predicate. * Add match.some (#1624) Added new matcher - match.some, which matches on an object or iterable if some of the properties (or elements, respectively) match the given predicate. --- docs/release-source/release/matchers.md | 7 ++ lib/sinon/match.js | 36 ++++++ test/match-test.js | 150 +++++++++++++++++++++++- 3 files changed, 192 insertions(+), 1 deletion(-) diff --git a/docs/release-source/release/matchers.md b/docs/release-source/release/matchers.md index ad5d44e8c..27dcdafeb 100644 --- a/docs/release-source/release/matchers.md +++ b/docs/release-source/release/matchers.md @@ -239,6 +239,13 @@ sinon.match.hasNested("a.b.c"); var actual = { "a": { "b": { "c": 3 } } }; ``` +#### `sinon.match.every(matcher)` + +Requires **every** element of an `Array`, `Set` or `Map`, or alternatively **every** value of an `Object` to match the given `matcher`. + +#### `sinon.match.some(matcher)` + +Requires **any** element of an `Array`, `Set` or `Map`, or alternatively **any** value of an `Object` to match the given `matcher`. ## Combining matchers diff --git a/lib/sinon/match.js b/lib/sinon/match.js index 8aecc7303..7a6d55577 100644 --- a/lib/sinon/match.js +++ b/lib/sinon/match.js @@ -243,6 +243,42 @@ match.hasNested = function (property, value) { }, message); }; +match.every = function (predicate) { + if (!isMatcher(predicate)) { + throw new TypeError("Matcher expected"); + } + + return match(function (actual) { + if (typeOf(actual) === "object") { + return every(Object.keys(actual), function (key) { + return predicate.test(actual[key]); + }); + } + + return !!actual && typeOf(actual.forEach) === "function" && every(actual, function (element) { + return predicate.test(element); + }); + }, "every(" + predicate.message + ")"); +}; + +match.some = function (predicate) { + if (!isMatcher(predicate)) { + throw new TypeError("Matcher expected"); + } + + return match(function (actual) { + if (typeOf(actual) === "object") { + return !every(Object.keys(actual), function (key) { + return !predicate.test(actual[key]); + }); + } + + return !!actual && typeOf(actual.forEach) === "function" && !every(actual, function (element) { + return !predicate.test(element); + }); + }, "some(" + predicate.message + ")"); +}; + match.array = match.typeOf("array"); match.array.deepEquals = function (expectation) { diff --git a/test/match-test.js b/test/match-test.js index 41c9eff93..b2e780c9b 100644 --- a/test/match-test.js +++ b/test/match-test.js @@ -1,6 +1,8 @@ "use strict"; -var assert = require("referee").assert; +var referee = require("referee"); +var assert = referee.assert; +var refute = referee.refute; var sinonMatch = require("../lib/sinon/match"); function propertyMatcherTests(matcher, additionalTests) { @@ -615,6 +617,152 @@ describe("sinonMatch", function () { }); }); + describe(".every", function () { + it("throws if given argument is not a matcher", function () { + assert.exception(function () { + sinonMatch.every({}); + }, "TypeError"); + assert.exception(function () { + sinonMatch.every(123); + }, "TypeError"); + assert.exception(function () { + sinonMatch.every("123"); + }, "TypeError"); + }); + + it("returns matcher", function () { + var every = sinonMatch.every(sinonMatch.any); + + assert(sinonMatch.isMatcher(every)); + }); + + it("wraps the given matcher message with an \"every()\"", function () { + var every = sinonMatch.every(sinonMatch.number); + + assert.equals(every.toString(), "every(typeOf(\"number\"))"); + }); + + it("fails to match anything that is not an object or an iterable", function () { + var every = sinonMatch.every(sinonMatch.any); + + refute(every.test(1)); + refute(every.test("a")); + refute(every.test(null)); + refute(every.test(function () {})); + }); + + it("matches an object if the predicate is true for every property", function () { + var every = sinonMatch.every(sinonMatch.number); + + assert(every.test({a: 1, b: 2})); + }); + + it("fails if the predicate is false for some of the object properties", function () { + var every = sinonMatch.every(sinonMatch.number); + + refute(every.test({a: 1, b: "b"})); + }); + + it("matches an array if the predicate is true for every element", function () { + var every = sinonMatch.every(sinonMatch.number); + + assert(every.test([1, 2])); + }); + + it("fails if the predicate is false for some of the array elements", function () { + var every = sinonMatch.every(sinonMatch.number); + + refute(every.test([1, "b"])); + }); + + if (typeof Set === "function") { + it("matches an iterable if the predicate is true for every element", function () { + var every = sinonMatch.every(sinonMatch.number); + + assert(every.test(new Set([1, 2]))); + }); + + it("fails if the predicate is false for some of the iterable elements", function () { + var every = sinonMatch.every(sinonMatch.number); + + refute(every.test(new Set([1, "b"]))); + }); + } + }); + + describe(".some", function () { + it("throws if given argument is not a matcher", function () { + assert.exception(function () { + sinonMatch.some({}); + }, "TypeError"); + assert.exception(function () { + sinonMatch.some(123); + }, "TypeError"); + assert.exception(function () { + sinonMatch.some("123"); + }, "TypeError"); + }); + + it("returns matcher", function () { + var some = sinonMatch.some(sinonMatch.any); + + assert(sinonMatch.isMatcher(some)); + }); + + it("wraps the given matcher message with an \"some()\"", function () { + var some = sinonMatch.some(sinonMatch.number); + + assert.equals(some.toString(), "some(typeOf(\"number\"))"); + }); + + it("fails to match anything that is not an object or an iterable", function () { + var some = sinonMatch.some(sinonMatch.any); + + refute(some.test(1)); + refute(some.test("a")); + refute(some.test(null)); + refute(some.test(function () {})); + }); + + it("matches an object if the predicate is true for some of the properties", function () { + var some = sinonMatch.some(sinonMatch.number); + + assert(some.test({a: 1, b: "b"})); + }); + + it("fails if the predicate is false for all of the object properties", function () { + var some = sinonMatch.some(sinonMatch.number); + + refute(some.test({a: "a", b: "b"})); + }); + + it("matches an array if the predicate is true for some element", function () { + var some = sinonMatch.some(sinonMatch.number); + + assert(some.test([1, "b"])); + }); + + it("fails if the predicate is false for all of the array elements", function () { + var some = sinonMatch.some(sinonMatch.number); + + refute(some.test(["a", "b"])); + }); + + if (typeof Set === "function") { + it("matches an iterable if the predicate is true for some element", function () { + var some = sinonMatch.some(sinonMatch.number); + + assert(some.test(new Set([1, "b"]))); + }); + + it("fails if the predicate is false for all of the iterable elements", function () { + var some = sinonMatch.some(sinonMatch.number); + + refute(some.test(new Set(["a", "b"]))); + }); + } + }); + describe(".bool", function () { it("is typeOf boolean matcher", function () { var bool = sinonMatch.bool;