chai 5.0.0 → 5.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -71,32 +71,12 @@ You can also use it within the browser; install via npm and use the `chai.js` fi
71
71
  Import the library in your code, and then pick one of the styles you'd like to use - either `assert`, `expect` or `should`:
72
72
 
73
73
  ```js
74
- var chai = require('chai');
75
- var assert = chai.assert; // Using Assert style
76
- var expect = chai.expect; // Using Expect style
77
- var should = chai.should(); // Using Should style
78
- ```
79
-
80
- ### Pre-Native Modules Usage (_registers the chai testing style globally_)
81
-
82
- ```js
83
- require('chai/register-assert'); // Using Assert style
84
- require('chai/register-expect'); // Using Expect style
85
- require('chai/register-should'); // Using Should style
86
- ```
87
-
88
- ### Pre-Native Modules Usage (_as local variables_)
89
-
90
- ```js
91
- const { assert } = require('chai'); // Using Assert style
92
- const { expect } = require('chai'); // Using Expect style
93
- const { should } = require('chai'); // Using Should style
94
- should(); // Modifies `Object.prototype`
95
-
96
- const { expect, use } = require('chai'); // Creates local variables `expect` and `use`; useful for plugin use
74
+ import { assert } from 'chai'; // Using Assert style
75
+ import { expect } from 'chai'; // Using Expect style
76
+ import { should } from 'chai'; // Using Should style
97
77
  ```
98
78
 
99
- ### Native Modules Usage (_registers the chai testing style globally_)
79
+ ### Register the chai testing style globally
100
80
 
101
81
  ```js
102
82
  import 'chai/register-assert'; // Using Assert style
@@ -104,13 +84,15 @@ import 'chai/register-expect'; // Using Expect style
104
84
  import 'chai/register-should'; // Using Should style
105
85
  ```
106
86
 
107
- ### Native Modules Usage (_local import only_)
87
+ ### Import assertion styles as local variables
108
88
 
109
89
  ```js
110
90
  import { assert } from 'chai'; // Using Assert style
111
91
  import { expect } from 'chai'; // Using Expect style
112
92
  import { should } from 'chai'; // Using Should style
113
93
  should(); // Modifies `Object.prototype`
94
+
95
+ import { expect, use } from 'chai'; // Creates local variables `expect` and `use`; useful for plugin use
114
96
  ```
115
97
 
116
98
  ### Usage with Mocha
package/chai.js CHANGED
@@ -142,7 +142,10 @@ __name(type, "type");
142
142
 
143
143
  // node_modules/assertion-error/index.js
144
144
  var canElideFrames = "captureStackTrace" in Error;
145
- var AssertionError = class extends Error {
145
+ var AssertionError = class _AssertionError extends Error {
146
+ static {
147
+ __name(this, "AssertionError");
148
+ }
146
149
  message;
147
150
  get name() {
148
151
  return "AssertionError";
@@ -154,7 +157,7 @@ var AssertionError = class extends Error {
154
157
  super(message);
155
158
  this.message = message;
156
159
  if (canElideFrames) {
157
- Error.captureStackTrace(this, ssf || AssertionError);
160
+ Error.captureStackTrace(this, ssf || _AssertionError);
158
161
  }
159
162
  for (const key in props) {
160
163
  if (!(key in this)) {
@@ -172,7 +175,6 @@ var AssertionError = class extends Error {
172
175
  };
173
176
  }
174
177
  };
175
- __name(AssertionError, "AssertionError");
176
178
 
177
179
  // lib/chai/utils/expectTypes.js
178
180
  function expectTypes(obj, types) {
@@ -372,11 +374,7 @@ function inspectArray(array, options) {
372
374
  options.truncate -= listContents.length;
373
375
  let propertyContents = "";
374
376
  if (nonIndexProperties.length) {
375
- propertyContents = inspectList(
376
- nonIndexProperties.map((key) => [key, array[key]]),
377
- options,
378
- inspectProperty
379
- );
377
+ propertyContents = inspectList(nonIndexProperties.map((key) => [key, array[key]]), options, inspectProperty);
380
378
  }
381
379
  return `[ ${listContents}${propertyContents ? `, ${propertyContents}` : ""} ]`;
382
380
  }
@@ -410,11 +408,7 @@ function inspectTypedArray(array, options) {
410
408
  }
411
409
  let propertyContents = "";
412
410
  if (nonIndexProperties.length) {
413
- propertyContents = inspectList(
414
- nonIndexProperties.map((key) => [key, array[key]]),
415
- options,
416
- inspectProperty
417
- );
411
+ propertyContents = inspectList(nonIndexProperties.map((key) => [key, array[key]]), options, inspectProperty);
418
412
  }
419
413
  return `${name}[ ${output}${propertyContents ? `, ${propertyContents}` : ""} ]`;
420
414
  }
@@ -434,11 +428,12 @@ __name(inspectDate, "inspectDate");
434
428
 
435
429
  // node_modules/loupe/lib/function.js
436
430
  function inspectFunction(func, options) {
431
+ const functionType = func[Symbol.toStringTag] || "Function";
437
432
  const name = func.name;
438
433
  if (!name) {
439
- return options.stylize("[Function]", "special");
434
+ return options.stylize(`[${functionType}]`, "special");
440
435
  }
441
- return options.stylize(`[Function ${truncate(name, options.truncate - 11)}]`, "special");
436
+ return options.stylize(`[${functionType} ${truncate(name, options.truncate - 11)}]`, "special");
442
437
  }
443
438
  __name(inspectFunction, "inspectFunction");
444
439
 
@@ -524,10 +519,7 @@ function inspectSet(set2, options) {
524
519
  __name(inspectSet, "inspectSet");
525
520
 
526
521
  // node_modules/loupe/lib/string.js
527
- var stringEscapeChars = new RegExp(
528
- "['\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]",
529
- "g"
530
- );
522
+ var stringEscapeChars = new RegExp("['\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]", "g");
531
523
  var escapeCharacters = {
532
524
  "\b": "\\b",
533
525
  " ": "\\t",
@@ -590,16 +582,8 @@ function inspectObject(object, options) {
590
582
  return "[Circular]";
591
583
  }
592
584
  options.seen.push(object);
593
- const propertyContents = inspectList(
594
- properties.map((key) => [key, object[key]]),
595
- options,
596
- inspectProperty
597
- );
598
- const symbolContents = inspectList(
599
- symbols.map((key) => [key, object[key]]),
600
- options,
601
- inspectProperty
602
- );
585
+ const propertyContents = inspectList(properties.map((key) => [key, object[key]]), options, inspectProperty);
586
+ const symbolContents = inspectList(symbols.map((key) => [key, object[key]]), options, inspectProperty);
603
587
  options.seen.pop();
604
588
  let sep = "";
605
589
  if (propertyContents && symbolContents) {
@@ -659,11 +643,7 @@ function inspectObject2(error, options) {
659
643
  }
660
644
  message = message ? `: ${message}` : "";
661
645
  options.truncate -= message.length + 5;
662
- const propertyContents = inspectList(
663
- properties.map((key) => [key, error[key]]),
664
- options,
665
- inspectProperty
666
- );
646
+ const propertyContents = inspectList(properties.map((key) => [key, error[key]]), options, inspectProperty);
667
647
  return `${name}${message}${propertyContents ? ` { ${propertyContents} }` : ""}`;
668
648
  }
669
649
  __name(inspectObject2, "inspectObject");
@@ -691,12 +671,7 @@ function inspectHTML(element, options) {
691
671
  let propertyContents = "";
692
672
  if (properties.length > 0) {
693
673
  propertyContents += " ";
694
- propertyContents += inspectList(
695
- properties.map((key) => [key, element.getAttribute(key)]),
696
- options,
697
- inspectAttribute,
698
- " "
699
- );
674
+ propertyContents += inspectList(properties.map((key) => [key, element.getAttribute(key)]), options, inspectAttribute, " ");
700
675
  }
701
676
  options.truncate -= propertyContents.length;
702
677
  const truncate2 = options.truncate;
@@ -902,7 +877,30 @@ var config = {
902
877
  * @param {Array}
903
878
  * @api public
904
879
  */
905
- proxyExcludedKeys: ["then", "catch", "inspect", "toJSON"]
880
+ proxyExcludedKeys: ["then", "catch", "inspect", "toJSON"],
881
+ /**
882
+ * ### config.deepEqual
883
+ *
884
+ * User configurable property, defines which a custom function to use for deepEqual
885
+ * comparisons.
886
+ * By default, the function used is the one from the `deep-eql` package without custom comparator.
887
+ *
888
+ * // use a custom comparator
889
+ * chai.config.deepEqual = (expected, actual) => {
890
+ * return chai.util.eql(expected, actual, {
891
+ * comparator: (expected, actual) => {
892
+ * // for non number comparison, use the default behavior
893
+ * if(typeof expected !== 'number') return null;
894
+ * // allow a difference of 10 between compared numbers
895
+ * return typeof actual === 'number' && Math.abs(actual - expected) < 10
896
+ * }
897
+ * })
898
+ * };
899
+ *
900
+ * @param {Function}
901
+ * @api public
902
+ */
903
+ deepEqual: null
906
904
  };
907
905
 
908
906
  // lib/chai/utils/inspect.js
@@ -1356,6 +1354,7 @@ function Assertion(obj, msg, ssfi, lockSsfi) {
1356
1354
  flag(this, "lockSsfi", lockSsfi);
1357
1355
  flag(this, "object", obj);
1358
1356
  flag(this, "message", msg);
1357
+ flag(this, "eql", config.deepEqual || deep_eql_default);
1359
1358
  return proxify(this);
1360
1359
  }
1361
1360
  __name(Assertion, "Assertion");
@@ -1857,16 +1856,31 @@ Assertion.addProperty("all", function() {
1857
1856
  flag2(this, "all", true);
1858
1857
  flag2(this, "any", false);
1859
1858
  });
1859
+ var functionTypes = {
1860
+ "function": ["function", "asyncfunction", "generatorfunction", "asyncgeneratorfunction"],
1861
+ "asyncfunction": ["asyncfunction", "asyncgeneratorfunction"],
1862
+ "generatorfunction": ["generatorfunction", "asyncgeneratorfunction"],
1863
+ "asyncgeneratorfunction": ["asyncgeneratorfunction"]
1864
+ };
1860
1865
  function an(type3, msg) {
1861
1866
  if (msg)
1862
1867
  flag2(this, "message", msg);
1863
1868
  type3 = type3.toLowerCase();
1864
1869
  var obj = flag2(this, "object"), article = ~["a", "e", "i", "o", "u"].indexOf(type3.charAt(0)) ? "an " : "a ";
1865
- this.assert(
1866
- type3 === type(obj).toLowerCase(),
1867
- "expected #{this} to be " + article + type3,
1868
- "expected #{this} not to be " + article + type3
1869
- );
1870
+ const detectedType = type(obj).toLowerCase();
1871
+ if (functionTypes["function"].includes(type3)) {
1872
+ this.assert(
1873
+ functionTypes[type3].includes(detectedType),
1874
+ "expected #{this} to be " + article + type3,
1875
+ "expected #{this} not to be " + article + type3
1876
+ );
1877
+ } else {
1878
+ this.assert(
1879
+ type3 === detectedType,
1880
+ "expected #{this} to be " + article + type3,
1881
+ "expected #{this} not to be " + article + type3
1882
+ );
1883
+ }
1870
1884
  }
1871
1885
  __name(an, "an");
1872
1886
  Assertion.addChainableMethod("an", an);
@@ -1882,7 +1896,7 @@ __name(includeChainingBehavior, "includeChainingBehavior");
1882
1896
  function include(val, msg) {
1883
1897
  if (msg)
1884
1898
  flag2(this, "message", msg);
1885
- var obj = flag2(this, "object"), objType = type(obj).toLowerCase(), flagMsg = flag2(this, "message"), negate = flag2(this, "negate"), ssfi = flag2(this, "ssfi"), isDeep = flag2(this, "deep"), descriptor = isDeep ? "deep " : "";
1899
+ var obj = flag2(this, "object"), objType = type(obj).toLowerCase(), flagMsg = flag2(this, "message"), negate = flag2(this, "negate"), ssfi = flag2(this, "ssfi"), isDeep = flag2(this, "deep"), descriptor = isDeep ? "deep " : "", isEql = isDeep ? flag2(this, "eql") : SameValueZero;
1886
1900
  flagMsg = flagMsg ? flagMsg + ": " : "";
1887
1901
  var included = false;
1888
1902
  switch (objType) {
@@ -1900,7 +1914,6 @@ function include(val, msg) {
1900
1914
  included = obj.has(val);
1901
1915
  break;
1902
1916
  case "map":
1903
- var isEql = isDeep ? deep_eql_default : SameValueZero;
1904
1917
  obj.forEach(function(item) {
1905
1918
  included = included || isEql(item, val);
1906
1919
  });
@@ -1908,7 +1921,7 @@ function include(val, msg) {
1908
1921
  case "set":
1909
1922
  if (isDeep) {
1910
1923
  obj.forEach(function(item) {
1911
- included = included || deep_eql_default(item, val);
1924
+ included = included || isEql(item, val);
1912
1925
  });
1913
1926
  } else {
1914
1927
  included = obj.has(val);
@@ -1917,7 +1930,7 @@ function include(val, msg) {
1917
1930
  case "array":
1918
1931
  if (isDeep) {
1919
1932
  included = obj.some(function(item) {
1920
- return deep_eql_default(item, val);
1933
+ return isEql(item, val);
1921
1934
  });
1922
1935
  } else {
1923
1936
  included = obj.indexOf(val) !== -1;
@@ -1982,6 +1995,22 @@ Assertion.addProperty("true", function() {
1982
1995
  flag2(this, "negate") ? false : true
1983
1996
  );
1984
1997
  });
1998
+ Assertion.addProperty("callable", function() {
1999
+ const val = flag2(this, "object");
2000
+ const ssfi = flag2(this, "ssfi");
2001
+ const message = flag2(this, "message");
2002
+ const msg = message ? `${message}: ` : "";
2003
+ const negate = flag2(this, "negate");
2004
+ const assertionMessage = negate ? `${msg}expected ${inspect2(val)} not to be a callable function` : `${msg}expected ${inspect2(val)} to be a callable function`;
2005
+ const isCallable = ["Function", "AsyncFunction", "GeneratorFunction", "AsyncGeneratorFunction"].includes(type(val));
2006
+ if (isCallable && negate || !isCallable && !negate) {
2007
+ throw new AssertionError(
2008
+ assertionMessage,
2009
+ void 0,
2010
+ ssfi
2011
+ );
2012
+ }
2013
+ });
1985
2014
  Assertion.addProperty("false", function() {
1986
2015
  this.assert(
1987
2016
  false === flag2(this, "object"),
@@ -2098,8 +2127,9 @@ Assertion.addMethod("eq", assertEqual);
2098
2127
  function assertEql(obj, msg) {
2099
2128
  if (msg)
2100
2129
  flag2(this, "message", msg);
2130
+ var eql = flag2(this, "eql");
2101
2131
  this.assert(
2102
- deep_eql_default(obj, flag2(this, "object")),
2132
+ eql(obj, flag2(this, "object")),
2103
2133
  "expected #{this} to deeply equal #{exp}",
2104
2134
  "expected #{this} to not deeply equal #{exp}",
2105
2135
  obj,
@@ -2411,7 +2441,7 @@ function assertProperty(name, val, msg) {
2411
2441
  ssfi
2412
2442
  );
2413
2443
  }
2414
- var isDeep = flag2(this, "deep"), negate = flag2(this, "negate"), pathInfo = isNested ? getPathInfo(obj, name) : null, value = isNested ? pathInfo.value : obj[name];
2444
+ var isDeep = flag2(this, "deep"), negate = flag2(this, "negate"), pathInfo = isNested ? getPathInfo(obj, name) : null, value = isNested ? pathInfo.value : obj[name], isEql = isDeep ? flag2(this, "eql") : (val1, val2) => val1 === val2;
2415
2445
  var descriptor = "";
2416
2446
  if (isDeep)
2417
2447
  descriptor += "deep ";
@@ -2436,7 +2466,7 @@ function assertProperty(name, val, msg) {
2436
2466
  }
2437
2467
  if (arguments.length > 1) {
2438
2468
  this.assert(
2439
- hasProperty2 && (isDeep ? deep_eql_default(val, value) : val === value),
2469
+ hasProperty2 && isEql(val, value),
2440
2470
  "expected #{this} to have " + descriptor + inspect2(name) + " of #{exp}, but got #{act}",
2441
2471
  "expected #{this} to not have " + descriptor + inspect2(name) + " of #{act}",
2442
2472
  val,
@@ -2463,9 +2493,10 @@ function assertOwnPropertyDescriptor(name, descriptor, msg) {
2463
2493
  flag2(this, "message", msg);
2464
2494
  var obj = flag2(this, "object");
2465
2495
  var actualDescriptor = Object.getOwnPropertyDescriptor(Object(obj), name);
2496
+ var eql = flag2(this, "eql");
2466
2497
  if (actualDescriptor && descriptor) {
2467
2498
  this.assert(
2468
- deep_eql_default(descriptor, actualDescriptor),
2499
+ eql(descriptor, actualDescriptor),
2469
2500
  "expected the own property descriptor for " + inspect2(name) + " on #{this} to match " + inspect2(descriptor) + ", got " + inspect2(actualDescriptor),
2470
2501
  "expected the own property descriptor for " + inspect2(name) + " on #{this} to not match " + inspect2(descriptor),
2471
2502
  descriptor,
@@ -2574,29 +2605,21 @@ function assertKeys(keys) {
2574
2605
  if (!keys.length) {
2575
2606
  throw new AssertionError(flagMsg + "keys required", void 0, ssfi);
2576
2607
  }
2577
- var len = keys.length, any = flag2(this, "any"), all = flag2(this, "all"), expected = keys;
2608
+ var len = keys.length, any = flag2(this, "any"), all = flag2(this, "all"), expected = keys, isEql = isDeep ? flag2(this, "eql") : (val1, val2) => val1 === val2;
2578
2609
  if (!any && !all) {
2579
2610
  all = true;
2580
2611
  }
2581
2612
  if (any) {
2582
2613
  ok = expected.some(function(expectedKey) {
2583
2614
  return actual.some(function(actualKey) {
2584
- if (isDeep) {
2585
- return deep_eql_default(expectedKey, actualKey);
2586
- } else {
2587
- return expectedKey === actualKey;
2588
- }
2615
+ return isEql(expectedKey, actualKey);
2589
2616
  });
2590
2617
  });
2591
2618
  }
2592
2619
  if (all) {
2593
2620
  ok = expected.every(function(expectedKey) {
2594
2621
  return actual.some(function(actualKey) {
2595
- if (isDeep) {
2596
- return deep_eql_default(expectedKey, actualKey);
2597
- } else {
2598
- return expectedKey === actualKey;
2599
- }
2622
+ return isEql(expectedKey, actualKey);
2600
2623
  });
2601
2624
  });
2602
2625
  if (!flag2(this, "contains")) {
@@ -2832,7 +2855,7 @@ Assertion.addMethod("members", function(subset, msg) {
2832
2855
  failMsg = "expected #{this} to have the same " + subject + " as #{exp}";
2833
2856
  failNegateMsg = "expected #{this} to not have the same " + subject + " as #{exp}";
2834
2857
  }
2835
- var cmp = flag2(this, "deep") ? deep_eql_default : void 0;
2858
+ var cmp = flag2(this, "deep") ? flag2(this, "eql") : void 0;
2836
2859
  this.assert(
2837
2860
  isSubsetOf(subset, obj, cmp, contains, ordered),
2838
2861
  failMsg,
@@ -2845,7 +2868,7 @@ Assertion.addMethod("members", function(subset, msg) {
2845
2868
  function oneOf(list, msg) {
2846
2869
  if (msg)
2847
2870
  flag2(this, "message", msg);
2848
- var expected = flag2(this, "object"), flagMsg = flag2(this, "message"), ssfi = flag2(this, "ssfi"), contains = flag2(this, "contains"), isDeep = flag2(this, "deep");
2871
+ var expected = flag2(this, "object"), flagMsg = flag2(this, "message"), ssfi = flag2(this, "ssfi"), contains = flag2(this, "contains"), isDeep = flag2(this, "deep"), eql = flag2(this, "eql");
2849
2872
  new Assertion(list, flagMsg, ssfi, true).to.be.an("array");
2850
2873
  if (contains) {
2851
2874
  this.assert(
@@ -2861,7 +2884,7 @@ function oneOf(list, msg) {
2861
2884
  if (isDeep) {
2862
2885
  this.assert(
2863
2886
  list.some(function(possibility) {
2864
- return deep_eql_default(expected, possibility);
2887
+ return eql(expected, possibility);
2865
2888
  }),
2866
2889
  "expected #{this} to deeply equal one of #{exp}",
2867
2890
  "expected #{this} to deeply equal one of #{exp}",
@@ -3227,11 +3250,11 @@ assert.isUndefined = function(val, msg) {
3227
3250
  assert.isDefined = function(val, msg) {
3228
3251
  new Assertion(val, msg, assert.isDefined, true).to.not.equal(void 0);
3229
3252
  };
3230
- assert.isFunction = function(val, msg) {
3231
- new Assertion(val, msg, assert.isFunction, true).to.be.a("function");
3253
+ assert.isCallable = function(val, msg) {
3254
+ new Assertion(val, msg, assert.isCallable, true).is.callable;
3232
3255
  };
3233
- assert.isNotFunction = function(val, msg) {
3234
- new Assertion(val, msg, assert.isNotFunction, true).to.not.be.a("function");
3256
+ assert.isNotCallable = function(val, msg) {
3257
+ new Assertion(val, msg, assert.isNotCallable, true).is.not.callable;
3235
3258
  };
3236
3259
  assert.isObject = function(val, msg) {
3237
3260
  new Assertion(val, msg, assert.isObject, true).to.be.a("object");
@@ -3672,7 +3695,7 @@ assert.isNotEmpty = function(val, msg) {
3672
3695
  (/* @__PURE__ */ __name(function alias(name, as) {
3673
3696
  assert[as] = assert[name];
3674
3697
  return alias;
3675
- }, "alias"))("isOk", "ok")("isNotOk", "notOk")("throws", "throw")("throws", "Throw")("isExtensible", "extensible")("isNotExtensible", "notExtensible")("isSealed", "sealed")("isNotSealed", "notSealed")("isFrozen", "frozen")("isNotFrozen", "notFrozen")("isEmpty", "empty")("isNotEmpty", "notEmpty");
3698
+ }, "alias"))("isOk", "ok")("isNotOk", "notOk")("throws", "throw")("throws", "Throw")("isExtensible", "extensible")("isNotExtensible", "notExtensible")("isSealed", "sealed")("isNotSealed", "notSealed")("isFrozen", "frozen")("isNotFrozen", "notFrozen")("isEmpty", "empty")("isNotEmpty", "notEmpty")("isCallable", "isFunction")("isNotCallable", "isNotFunction");
3676
3699
 
3677
3700
  // lib/chai.js
3678
3701
  var used = [];
@@ -3774,6 +3797,8 @@ export {
3774
3797
  * from within another assertion. It's also temporarily set to `true` before
3775
3798
  * an overwritten assertion gets called by the overwriting assertion.
3776
3799
  *
3800
+ * - `eql`: This flag contains the deepEqual function to be used by the assertion.
3801
+ *
3777
3802
  * @param {Mixed} obj target of the assertion
3778
3803
  * @param {String} msg (optional) custom error message
3779
3804
  * @param {Function} ssfi (optional) starting point for removing stack frames
@@ -54,7 +54,7 @@ export function Assertion (obj, msg, ssfi, lockSsfi) {
54
54
  util.flag(this, 'lockSsfi', lockSsfi);
55
55
  util.flag(this, 'object', obj);
56
56
  util.flag(this, 'message', msg);
57
- util.flag(this, 'eql', config.deepEqual ?? util.eql);
57
+ util.flag(this, 'eql', config.deepEqual || util.eql);
58
58
 
59
59
  return util.proxify(this);
60
60
  }
@@ -239,6 +239,13 @@ Assertion.addProperty('all', function () {
239
239
  flag(this, 'any', false);
240
240
  });
241
241
 
242
+ const functionTypes = {
243
+ 'function': ['function', 'asyncfunction', 'generatorfunction', 'asyncgeneratorfunction'],
244
+ 'asyncfunction': ['asyncfunction', 'asyncgeneratorfunction'],
245
+ 'generatorfunction': ['generatorfunction', 'asyncgeneratorfunction'],
246
+ 'asyncgeneratorfunction': ['asyncgeneratorfunction']
247
+ }
248
+
242
249
  /**
243
250
  * ### .a(type[, msg])
244
251
  *
@@ -298,18 +305,27 @@ Assertion.addProperty('all', function () {
298
305
  * @namespace BDD
299
306
  * @api public
300
307
  */
301
-
302
308
  function an (type, msg) {
303
309
  if (msg) flag(this, 'message', msg);
304
310
  type = type.toLowerCase();
305
311
  var obj = flag(this, 'object')
306
312
  , article = ~[ 'a', 'e', 'i', 'o', 'u' ].indexOf(type.charAt(0)) ? 'an ' : 'a ';
307
313
 
308
- this.assert(
309
- type === _.type(obj).toLowerCase()
310
- , 'expected #{this} to be ' + article + type
311
- , 'expected #{this} not to be ' + article + type
312
- );
314
+ const detectedType = _.type(obj).toLowerCase();
315
+
316
+ if (functionTypes['function'].includes(type)) {
317
+ this.assert(
318
+ functionTypes[type].includes(detectedType)
319
+ , 'expected #{this} to be ' + article + type
320
+ , 'expected #{this} not to be ' + article + type
321
+ );
322
+ } else {
323
+ this.assert(
324
+ type === detectedType
325
+ , 'expected #{this} to be ' + article + type
326
+ , 'expected #{this} not to be ' + article + type
327
+ );
328
+ }
313
329
  }
314
330
 
315
331
  Assertion.addChainableMethod('an', an);
@@ -672,6 +688,43 @@ Assertion.addProperty('true', function () {
672
688
  );
673
689
  });
674
690
 
691
+ /**
692
+ * ### .callable
693
+ *
694
+ * Asserts that the target a callable function.
695
+ *
696
+ * expect(console.log).to.be.callable;
697
+ *
698
+ * A custom error message can be given as the second argument to `expect`.
699
+ *
700
+ * expect('not a function', 'nooo why fail??').to.be.callable;
701
+ *
702
+ * @name callable
703
+ * @namespace BDD
704
+ * @api public
705
+ */
706
+ Assertion.addProperty('callable', function () {
707
+ const val = flag(this, 'object')
708
+ const ssfi = flag(this, 'ssfi')
709
+ const message = flag(this, 'message')
710
+ const msg = message ? `${message}: ` : ''
711
+ const negate = flag(this, 'negate');
712
+
713
+ const assertionMessage = negate ?
714
+ `${msg}expected ${_.inspect(val)} not to be a callable function` :
715
+ `${msg}expected ${_.inspect(val)} to be a callable function`;
716
+
717
+ const isCallable = ['Function', 'AsyncFunction', 'GeneratorFunction', 'AsyncGeneratorFunction'].includes(_.type(val));
718
+
719
+ if ((isCallable && negate) || (!isCallable && !negate)) {
720
+ throw new AssertionError(
721
+ assertionMessage,
722
+ undefined,
723
+ ssfi
724
+ );
725
+ }
726
+ });
727
+
675
728
  /**
676
729
  * ### .false
677
730
  *
@@ -8,6 +8,7 @@ import * as chai from '../../../index.js';
8
8
  import {Assertion} from '../assertion.js';
9
9
  import {flag, inspect} from '../utils/index.js';
10
10
  import {AssertionError} from 'assertion-error';
11
+ import {type} from '../utils/type-detect.js';
11
12
 
12
13
  /**
13
14
  * ### assert(expression, message)
@@ -553,41 +554,39 @@ assert.isDefined = function (val, msg) {
553
554
  };
554
555
 
555
556
  /**
556
- * ### .isFunction(value, [message])
557
+ * ### .isCallable(value, [message])
557
558
  *
558
- * Asserts that `value` is a function.
559
+ * Asserts that `value` is a callable function.
559
560
  *
560
561
  * function serveTea() { return 'cup of tea'; };
561
- * assert.isFunction(serveTea, 'great, we can have tea now');
562
+ * assert.isCallable(serveTea, 'great, we can have tea now');
562
563
  *
563
- * @name isFunction
564
+ * @name isCallable
564
565
  * @param {Mixed} value
565
566
  * @param {String} message
566
567
  * @namespace Assert
567
568
  * @api public
568
569
  */
569
-
570
- assert.isFunction = function (val, msg) {
571
- new Assertion(val, msg, assert.isFunction, true).to.be.a('function');
572
- };
570
+ assert.isCallable = function (val, msg) {
571
+ new Assertion(val, msg, assert.isCallable, true).is.callable;
572
+ }
573
573
 
574
574
  /**
575
- * ### .isNotFunction(value, [message])
575
+ * ### .isNotCallable(value, [message])
576
576
  *
577
- * Asserts that `value` is _not_ a function.
577
+ * Asserts that `value` is _not_ a callable function.
578
578
  *
579
579
  * var serveTea = [ 'heat', 'pour', 'sip' ];
580
- * assert.isNotFunction(serveTea, 'great, we have listed the steps');
580
+ * assert.isNotCallable(serveTea, 'great, we have listed the steps');
581
581
  *
582
- * @name isNotFunction
582
+ * @name isNotCallable
583
583
  * @param {Mixed} value
584
584
  * @param {String} message
585
585
  * @namespace Assert
586
586
  * @api public
587
587
  */
588
-
589
- assert.isNotFunction = function (val, msg) {
590
- new Assertion(val, msg, assert.isNotFunction, true).to.not.be.a('function');
588
+ assert.isNotCallable = function (val, msg) {
589
+ new Assertion(val, msg, assert.isNotCallable, true).is.not.callable;
591
590
  };
592
591
 
593
592
  /**
@@ -3104,4 +3103,6 @@ assert.isNotEmpty = function(val, msg) {
3104
3103
  ('isFrozen', 'frozen')
3105
3104
  ('isNotFrozen', 'notFrozen')
3106
3105
  ('isEmpty', 'empty')
3107
- ('isNotEmpty', 'notEmpty');
3106
+ ('isNotEmpty', 'notEmpty')
3107
+ ('isCallable', 'isFunction')
3108
+ ('isNotCallable', 'isNotFunction')
package/package.json CHANGED
@@ -18,7 +18,7 @@
18
18
  "Veselin Todorov <hi@vesln.com>",
19
19
  "John Firebaugh <john.firebaugh@gmail.com>"
20
20
  ],
21
- "version": "5.0.0",
21
+ "version": "5.0.3",
22
22
  "repository": {
23
23
  "type": "git",
24
24
  "url": "https://github.com/chaijs/chai"
@@ -44,16 +44,15 @@
44
44
  "assertion-error": "^2.0.1",
45
45
  "check-error": "^2.0.0",
46
46
  "deep-eql": "^5.0.1",
47
- "loupe": "^3.0.0",
47
+ "loupe": "^3.1.0",
48
48
  "pathval": "^2.0.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@rollup/plugin-commonjs": "^25.0.7",
52
- "@web/dev-server-rollup": "^0.5.4",
53
- "@web/test-runner": "^0.17.2",
54
- "@web/test-runner-playwright": "^0.10.2",
55
- "bump-cli": "^1.1.3",
56
- "esbuild": "^0.17.3",
57
- "mocha": "^8.3.0"
52
+ "@web/dev-server-rollup": "^0.6.1",
53
+ "@web/test-runner": "^0.18.0",
54
+ "@web/test-runner-playwright": "^0.11.0",
55
+ "esbuild": "^0.19.10",
56
+ "mocha": "^10.2.0"
58
57
  }
59
58
  }
package/bower.json DELETED
@@ -1,26 +0,0 @@
1
- {
2
- "name": "chai",
3
- "description": "BDD/TDD assertion library for node.js and the browser. Test framework agnostic.",
4
- "license": "MIT",
5
- "keywords": [
6
- "test",
7
- "assertion",
8
- "assert",
9
- "testing",
10
- "chai"
11
- ],
12
- "main": "chai.js",
13
- "ignore": [
14
- "build",
15
- "components",
16
- "lib",
17
- "node_modules",
18
- "support",
19
- "test",
20
- "index.js",
21
- "Makefile",
22
- ".*"
23
- ],
24
- "dependencies": {},
25
- "devDependencies": {}
26
- }
package/karma.conf.cjs DELETED
@@ -1,35 +0,0 @@
1
- module.exports = function(config) {
2
- config.set({
3
- frameworks: [ 'mocha' ]
4
- , files: [
5
- { pattern: 'chai.js', type: 'module', included: false, served: true }
6
- , { pattern: 'test/bootstrap/index.js', type: 'module'}
7
- , { pattern: 'test/*.js', type: 'module' }
8
- , { pattern: 'test/type-detect/*.js', type: 'module' }
9
- ]
10
- , reporters: [ 'progress' ]
11
- , colors: true
12
- , logLevel: config.LOG_INFO
13
- , autoWatch: false
14
- , browsers: [ 'HeadlessChrome' ]
15
- , customLaunchers: {
16
- HeadlessChrome: {
17
- base: 'ChromeHeadless'
18
- , flags: [ '--no-sandbox',]
19
- , }
20
- , }
21
- , browserDisconnectTimeout: 10000
22
- , browserDisconnectTolerance: 2
23
- , browserNoActivityTimeout: 20000
24
- , singleRun: true
25
- });
26
-
27
- switch (process.env.CHAI_TEST_ENV) {
28
- case 'sauce':
29
- require('./karma.sauce')(config);
30
- break;
31
- default:
32
- // ...
33
- break;
34
- };
35
- };
package/karma.sauce.js DELETED
@@ -1,41 +0,0 @@
1
- var version = require('./package.json').version;
2
- var ts = new Date().getTime();
3
-
4
- module.exports = function(config) {
5
- var auth;
6
-
7
- try {
8
- auth = require('./test/auth/index');
9
- } catch(ex) {
10
- auth = {};
11
- auth.SAUCE_USERNAME = process.env.SAUCE_USERNAME || null;
12
- auth.SAUCE_ACCESS_KEY = process.env.SAUCE_ACCESS_KEY || null;
13
- }
14
-
15
- if (!auth.SAUCE_USERNAME || !auth.SAUCE_ACCESS_KEY) return;
16
- if (process.env.SKIP_SAUCE) return;
17
-
18
- var branch = process.env.TRAVIS_BRANCH || 'local'
19
- var browserConfig = require('./sauce.browsers');
20
- var browsers = Object.keys(browserConfig);
21
- var tags = [ 'chaijs_' + version, auth.SAUCE_USERNAME + '@' + branch ];
22
- var tunnel = process.env.TRAVIS_JOB_NUMBER || ts;
23
-
24
- if (process.env.TRAVIS_JOB_NUMBER) {
25
- tags.push('travis@' + process.env.TRAVIS_JOB_NUMBER);
26
- }
27
-
28
- config.browsers = config.browsers.concat(browsers);
29
- Object.assign(config.customLaunchers, browserConfig);
30
- config.reporters.push('saucelabs');
31
- config.captureTimeout = 300000;
32
-
33
- config.sauceLabs = {
34
- username: auth.SAUCE_USERNAME
35
- , accessKey: auth.SAUCE_ACCESS_KEY
36
- , startConnect: ('TRAVIS' in process.env) === false
37
- , tags: tags
38
- , testName: 'ChaiJS'
39
- , tunnelIdentifier: tunnel
40
- };
41
- };
package/sauce.browsers.js DELETED
@@ -1,106 +0,0 @@
1
-
2
- /*!
3
- * Chrome
4
- */
5
-
6
- exports['SL_Chrome'] = {
7
- base: 'SauceLabs'
8
- , browserName: 'chrome'
9
- };
10
-
11
- /*!
12
- * Firefox
13
- */
14
-
15
- exports['SL_Firefox'] = {
16
- base: 'SauceLabs'
17
- , browserName: 'firefox'
18
- };
19
-
20
- exports['SL_Firefox_ESR'] = {
21
- base: 'SauceLabs'
22
- , browserName: 'firefox'
23
- , version: 38
24
- };
25
-
26
- /*!
27
- * Internet Explorer
28
- */
29
-
30
- exports['SL_IE'] = {
31
- base: 'SauceLabs'
32
- , browserName: 'internet explorer'
33
- };
34
-
35
- /*!
36
- * TODO: fails because of Uint8Array support
37
- *
38
- exports['SL_IE_Old'] = {
39
- base: 'SauceLabs'
40
- , browserName: 'internet explorer'
41
- , version: 10
42
- };
43
- */
44
-
45
- exports['SL_Edge'] = {
46
- base: 'SauceLabs'
47
- , browserName: 'microsoftedge'
48
- };
49
-
50
- /*!
51
- * Safari
52
- */
53
-
54
- exports['SL_Safari'] = {
55
- base: 'SauceLabs'
56
- , browserName: 'safari'
57
- , platform: 'Mac 10.11'
58
- };
59
-
60
- /*!
61
- * iPhone
62
- */
63
-
64
- /*!
65
- * TODO: These take forever to boot or shut down. Causes timeout.
66
- *
67
-
68
- exports['SL_iPhone_6'] = {
69
- base: 'SauceLabs'
70
- , browserName: 'iphone'
71
- , platform: 'Mac 10.8'
72
- , version: '6'
73
- };
74
-
75
- exports['SL_iPhone_5-1'] = {
76
- base: 'SauceLabs'
77
- , browserName: 'iphone'
78
- , platform: 'Mac 10.8'
79
- , version: '5.1'
80
- };
81
-
82
- exports['SL_iPhone_5'] = {
83
- base: 'SauceLabs'
84
- , browserName: 'iphone'
85
- , platform: 'Mac 10.6'
86
- , version: '5'
87
- };
88
-
89
- */
90
-
91
- /*!
92
- * Android
93
- */
94
-
95
- /*!
96
- * TODO: fails because of error serialization
97
- *
98
-
99
- exports['SL_Android_4'] = {
100
- base: 'SauceLabs'
101
- , browserName: 'android'
102
- , platform: 'Linux'
103
- , version: '4'
104
- };
105
-
106
- */