blob: 3ef7f310ad84613052e9fd187ed5bae6f6ff0d9e [file] [log] [blame] [view]
Paul Lewis40e28672020-11-27 09:51:371# samsam
2
3> Same same, but different
4
5`samsam` is a collection of predicate and comparison functions useful for
6identifiying the type of values and to compare values with varying degrees of
7strictness.
8
9`samsam` is a general-purpose library. It works in browsers and Node. It will
10define itself as an AMD module if you want it to (i.e. if there's a `define`
11function available).
12
13## Predicate functions
14
Paul Lewis40e28672020-11-27 09:51:3715### `isArguments(value)`
16
17Returns `true` if `value` is an `arguments` object, `false` otherwise.
18
Paul Lewis40e28672020-11-27 09:51:3719### `isNegZero(value)`
20
21Returns `true` if `value` is `-0`.
22
Paul Lewis40e28672020-11-27 09:51:3723### `isElement(value)`
24
25Returns `true` if `value` is a DOM element node. Unlike
26Underscore.js/lodash, this function will return `false` if `value` is an
Tim van der Lippe61fe6852021-09-13 12:21:1627_element-like_ object, i.e. a regular object with a `nodeType` property that
Paul Lewis40e28672020-11-27 09:51:3728holds the value `1`.
29
30### `isSet(value)`
31
32Returns `true` if `value` is a [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set).
33
Paul Lewis40e28672020-11-27 09:51:3734## Comparison functions
35
Paul Lewis40e28672020-11-27 09:51:3736### `identical(x, y)`
37
38Strict equality check according to EcmaScript Harmony's `egal`.
39
40**From the Harmony wiki:**
41
42> An egal function simply makes available the internal `SameValue` function
Tim van der Lippe61fe6852021-09-13 12:21:1643> from section 9.12 of the ES5 spec. If two values are egal, then they are not
44> observably distinguishable.
Paul Lewis40e28672020-11-27 09:51:3745
46`identical` returns `true` when `===` is `true`, except for `-0` and
47`+0`, where it returns `false`. Additionally, it returns `true` when
48`NaN` is compared to itself.
49
Paul Lewis40e28672020-11-27 09:51:3750### `deepEqual(actual, expectation)`
51
52Deep equal comparison. Two values are "deep equal" if:
53
Tim van der Lippe61fe6852021-09-13 12:21:1654- They are identical
55- They are both date objects representing the same time
56- They are both arrays containing elements that are all deepEqual
57- They are objects with the same set of properties, and each property
58 in `actual` is deepEqual to the corresponding property in `expectation`
Paul Lewis40e28672020-11-27 09:51:3759
Tim van der Lippe61fe6852021-09-13 12:21:1660 - `actual` can have [symbolic properties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol) that are missing from `expectation`
Paul Lewis40e28672020-11-27 09:51:3761
62### Matcher
63
64Match values and objects by type or or other fuzzy criteria. `samsam` ships
65with these built in matchers:
66
67#### `sinon.match.any`
68
69Matches anything.
70
Paul Lewis40e28672020-11-27 09:51:3771#### `sinon.match.defined`
72
73Requires the value to be defined.
74
Paul Lewis40e28672020-11-27 09:51:3775#### `sinon.match.truthy`
76
77Requires the value to be truthy.
78
Paul Lewis40e28672020-11-27 09:51:3779#### `sinon.match.falsy`
80
81Requires the value to be falsy.
82
Paul Lewis40e28672020-11-27 09:51:3783#### `sinon.match.bool`
84
85Requires the value to be a `Boolean`
86
Paul Lewis40e28672020-11-27 09:51:3787#### `sinon.match.number`
88
89Requires the value to be a `Number`.
90
Paul Lewis40e28672020-11-27 09:51:3791#### `sinon.match.string`
92
93Requires the value to be a `String`.
94
Paul Lewis40e28672020-11-27 09:51:3795#### `sinon.match.object`
96
97Requires the value to be an `Object`.
98
Paul Lewis40e28672020-11-27 09:51:3799#### `sinon.match.func`
100
101Requires the value to be a `Function`.
102
Paul Lewis40e28672020-11-27 09:51:37103#### `sinon.match.array`
104
105Requires the value to be an `Array`.
106
Paul Lewis40e28672020-11-27 09:51:37107#### `sinon.match.array.deepEquals(arr)`
108
109Requires an `Array` to be deep equal another one.
110
Paul Lewis40e28672020-11-27 09:51:37111#### `sinon.match.array.startsWith(arr)`
112
113Requires an `Array` to start with the same values as another one.
114
Paul Lewis40e28672020-11-27 09:51:37115#### `sinon.match.array.endsWith(arr)`
116
117Requires an `Array` to end with the same values as another one.
118
Paul Lewis40e28672020-11-27 09:51:37119#### `sinon.match.array.contains(arr)`
120
121Requires an `Array` to contain each one of the values the given array has.
122
Paul Lewis40e28672020-11-27 09:51:37123#### `sinon.match.map`
124
125Requires the value to be a `Map`.
126
Paul Lewis40e28672020-11-27 09:51:37127#### `sinon.match.map.deepEquals(map)`
128
129Requires a `Map` to be deep equal another one.
130
131#### `sinon.match.map.contains(map)`
132
133Requires a `Map` to contain each one of the items the given map has.
134
Paul Lewis40e28672020-11-27 09:51:37135#### `sinon.match.set`
136
137Requires the value to be a `Set`.
138
Paul Lewis40e28672020-11-27 09:51:37139#### `sinon.match.set.deepEquals(set)`
140
141Requires a `Set` to be deep equal another one.
142
Paul Lewis40e28672020-11-27 09:51:37143#### `sinon.match.set.contains(set)`
144
145Requires a `Set` to contain each one of the items the given set has.
146
Paul Lewis40e28672020-11-27 09:51:37147#### `sinon.match.regexp`
148
149Requires the value to be a regular expression.
150
Paul Lewis40e28672020-11-27 09:51:37151#### `sinon.match.date`
152
153Requires the value to be a `Date` object.
154
Paul Lewis40e28672020-11-27 09:51:37155#### `sinon.match.symbol`
156
157Requires the value to be a `Symbol`.
158
159#### `sinon.match.in(array)`
160
161Requires the value to be in the `array`.
162
163#### `sinon.match.same(ref)`
164
165Requires the value to strictly equal `ref`.
166
167#### `sinon.match.typeOf(type)`
168
169Requires the value to be of the given type, where `type` can be one of
Tim van der Lippe61fe6852021-09-13 12:21:16170`"undefined"`,
171`"null"`,
172`"boolean"`,
173`"number"`,
174`"string"`,
175`"object"`,
176`"function"`,
177`"array"`,
178`"regexp"`,
179`"date"` or
180`"symbol"`.
Paul Lewis40e28672020-11-27 09:51:37181
182#### `sinon.match.instanceOf(type)`
183
184Requires the value to be an instance of the given `type`.
185
186#### `sinon.match.has(property[, expectation])`
187
188Requires the value to define the given `property`.
189
190The property might be inherited via the prototype chain. If the optional expectation is given, the value of the property is deeply compared with the expectation. The expectation can be another matcher.
191
192#### `sinon.match.hasOwn(property[, expectation])`
193
194Same as `sinon.match.has` but the property must be defined by the value itself. Inherited properties are ignored.
195
Paul Lewis40e28672020-11-27 09:51:37196#### `sinon.match.hasNested(propertyPath[, expectation])`
197
198Requires the value to define the given `propertyPath`. Dot (`prop.prop`) and bracket (`prop[0]`) notations are supported as in [Lodash.get](https://lodash.com/docs/4.4.2#get).
199
200The propertyPath might be inherited via the prototype chain. If the optional expectation is given, the value at the propertyPath is deeply compared with the expectation. The expectation can be another matcher.
201
Paul Lewis40e28672020-11-27 09:51:37202```javascript
203sinon.match.hasNested("a[0].b.c");
204
205// Where actual is something like
Tim van der Lippe61fe6852021-09-13 12:21:16206var actual = { a: [{ b: { c: 3 } }] };
Paul Lewis40e28672020-11-27 09:51:37207
208sinon.match.hasNested("a.b.c");
209
210// Where actual is something like
Tim van der Lippe61fe6852021-09-13 12:21:16211var actual = { a: { b: { c: 3 } } };
Paul Lewis40e28672020-11-27 09:51:37212```
213
214#### `sinon.match.every(matcher)`
215
216Requires **every** element of an `Array`, `Set` or `Map`, or alternatively **every** value of an `Object` to match the given `matcher`.
217
218#### `sinon.match.some(matcher)`
219
220Requires **any** element of an `Array`, `Set` or `Map`, or alternatively **any** value of an `Object` to match the given `matcher`.
221
222## Combining matchers
223
224All matchers implement `and` and `or`. This allows to logically combine mutliple matchers. The result is a new matchers that requires both (and) or one of the matchers (or) to return `true`.
225
226```javascript
227var stringOrNumber = sinon.match.string.or(sinon.match.number);
228var bookWithPages = sinon.match.instanceOf(Book).and(sinon.match.has("pages"));
229```
230
231### `match(object, matcher)`
232
233Creates a custom matcher to perform partial equality check. Compares `object`
234with matcher according a wide set of rules:
235
236#### String matcher
237
238In its simplest form, `match` performs a case insensitive substring match.
239When the matcher is a string, `object` is converted to a string, and the
240function returns `true` if the matcher is a case-insensitive substring of
241`object` as a string.
242
243```javascript
244samsam.match("Give me something", "Give"); //true
245samsam.match("Give me something", "sumptn"); // false
Tim van der Lippe61fe6852021-09-13 12:21:16246samsam.match(
247 {
248 toString: function () {
249 return "yeah";
250 },
251 },
252 "Yeah!"
253); // true
Paul Lewis40e28672020-11-27 09:51:37254```
255
256The last example is not symmetric. When the matcher is a string, the `object`
257is coerced to a string - in this case using `toString`. Changing the order of
258the arguments would cause the matcher to be an object, in which case different
259rules apply (see below).
260
Paul Lewis40e28672020-11-27 09:51:37261#### Boolean matcher
262
263Performs a strict (i.e. `===`) match with the object. So, only `true`
264matches `true`, and only `false` matches `false`.
265
Paul Lewis40e28672020-11-27 09:51:37266#### Regular expression matcher
267
268When the matcher is a regular expression, the function will pass if
269`object.test(matcher)` is `true`. `match` is written in a generic way, so
270any object with a `test` method will be used as a matcher this way.
271
272```javascript
273samsam.match("Give me something", /^[a-z\s]$/i); // true
274samsam.match("Give me something", /[0-9]/); // false
Tim van der Lippe61fe6852021-09-13 12:21:16275samsam.match(
276 {
277 toString: function () {
278 return "yeah!";
279 },
280 },
281 /yeah/
282); // true
Paul Lewis40e28672020-11-27 09:51:37283samsam.match(234, /[a-z]/); // false
284```
285
Paul Lewis40e28672020-11-27 09:51:37286#### Number matcher
287
288When the matcher is a number, the assertion will pass if `object == matcher`.
289
290```javascript
291samsam.match("123", 123); // true
292samsam.match("Give me something", 425); // false
Tim van der Lippe61fe6852021-09-13 12:21:16293samsam.match(
294 {
295 toString: function () {
296 return "42";
297 },
298 },
299 42
300); // true
Paul Lewis40e28672020-11-27 09:51:37301samsam.match(234, 1234); // false
302```
303
Paul Lewis40e28672020-11-27 09:51:37304#### Function matcher
305
306When the matcher is a function, it is called with `object` as its only
307argument. `match` returns `true` if the function returns `true`. A strict
308match is performed against the return value, so a boolean `true` is required,
309truthy is not enough.
310
311```javascript
312// true
313samsam.match("123", function (exp) {
314 return exp == "123";
315});
316
317// false
318samsam.match("Give me something", function () {
319 return "ok";
320});
321
322// true
Tim van der Lippe61fe6852021-09-13 12:21:16323samsam.match(
324 {
325 toString: function () {
326 return "42";
327 },
328 },
329 function () {
330 return true;
Paul Lewis40e28672020-11-27 09:51:37331 }
Tim van der Lippe61fe6852021-09-13 12:21:16332);
Paul Lewis40e28672020-11-27 09:51:37333
334// false
335samsam.match(234, function () {});
336```
337
Paul Lewis40e28672020-11-27 09:51:37338#### Object matcher
339
340As mentioned above, if an object matcher defines a `test` method, `match`
341will return `true` if `matcher.test(object)` returns truthy.
342
343If the matcher does not have a test method, a recursive match is performed. If
344all properties of `matcher` matches corresponding properties in `object`,
345`match` returns `true`. Note that the object matcher does not care if the
346number of properties in the two objects are the same - only if all properties in
347the matcher recursively matches ones in `object`. If supported, this object matchers
348include [symbolic properties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol)
349in the comparison.
350
351```javascript
352// true
353samsam.match("123", {
354 test: function (arg) {
355 return arg == 123;
Tim van der Lippe61fe6852021-09-13 12:21:16356 },
Paul Lewis40e28672020-11-27 09:51:37357});
358
359// false
360samsam.match({}, { prop: 42 });
361
362// true
Tim van der Lippe61fe6852021-09-13 12:21:16363samsam.match(
364 {
365 name: "Chris",
366 profession: "Programmer",
367 },
368 {
369 name: "Chris",
370 }
371);
Paul Lewis40e28672020-11-27 09:51:37372
373// false
374samsam.match(234, { name: "Chris" });
375```
376
Paul Lewis40e28672020-11-27 09:51:37377#### DOM elements
378
379`match` can be very helpful when comparing DOM elements, because it allows
380you to compare several properties with one call:
381
382```javascript
383var el = document.getElementById("myEl");
384
385samsam.match(el, {
386 tagName: "h2",
387 className: "item",
Tim van der Lippe61fe6852021-09-13 12:21:16388 innerHTML: "Howdy",
Paul Lewis40e28672020-11-27 09:51:37389});
390```