chai 4.0.2 → 4.1.0

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/CODEOWNERS ADDED
@@ -0,0 +1 @@
1
+ * @chaijs/chai
package/chai.js CHANGED
@@ -14,7 +14,7 @@ var used = [];
14
14
  * Chai version
15
15
  */
16
16
 
17
- exports.version = '4.0.2';
17
+ exports.version = '4.1.0';
18
18
 
19
19
  /*!
20
20
  * Assertion Error
@@ -691,6 +691,16 @@ module.exports = function (chai, _) {
691
691
  *
692
692
  * expect({a: 1, b: 2, c: 3}).to.include({a: 1, b: 2});
693
693
  *
694
+ * When the target is a Set or WeakSet, `.include` asserts that the given `val` is a
695
+ * member of the target. SameValueZero equality algorithm is used.
696
+ *
697
+ * expect(new Set([1, 2])).to.include(2);
698
+ *
699
+ * When the target is a Map, `.include` asserts that the given `val` is one of
700
+ * the values of the target. SameValueZero equality algorithm is used.
701
+ *
702
+ * expect(new Map([['a', 1], ['b', 2]])).to.include(2);
703
+ *
694
704
  * Because `.include` does different things based on the target's type, it's
695
705
  * important to check the target's type before using `.include`. See the `.a`
696
706
  * doc for info on testing a target's type.
@@ -699,8 +709,8 @@ module.exports = function (chai, _) {
699
709
  *
700
710
  * By default, strict (`===`) equality is used to compare array members and
701
711
  * object properties. Add `.deep` earlier in the chain to use deep equality
702
- * instead. See the `deep-eql` project page for info on the deep equality
703
- * algorithm: https://github.com/chaijs/deep-eql.
712
+ * instead (WeakSet targets are not supported). See the `deep-eql` project
713
+ * page for info on the deep equality algorithm: https://github.com/chaijs/deep-eql.
704
714
  *
705
715
  * // Target array deeply (but not strictly) includes `{a: 1}`
706
716
  * expect([{a: 1}]).to.deep.include({a: 1});
@@ -810,25 +820,24 @@ module.exports = function (chai, _) {
810
820
  * @api public
811
821
  */
812
822
 
813
- function includeChainingBehavior () {
814
- flag(this, 'contains', true);
823
+ function SameValueZero(a, b) {
824
+ return (_.isNaN(a) && _.isNaN(b)) || a === b;
815
825
  }
816
826
 
817
- function isDeepIncluded (arr, val) {
818
- return arr.some(function (arrVal) {
819
- return _.eql(arrVal, val);
820
- });
827
+ function includeChainingBehavior () {
828
+ flag(this, 'contains', true);
821
829
  }
822
830
 
823
831
  function include (val, msg) {
824
832
  if (msg) flag(this, 'message', msg);
825
833
 
826
- _.expectTypes(this, ['array', 'object', 'string'], flag(this, 'ssfi'));
834
+ _.expectTypes(this, [
835
+ 'array', 'object', 'string',
836
+ 'map', 'set', 'weakset',
837
+ ]);
827
838
 
828
839
  var obj = flag(this, 'object')
829
- , objType = _.type(obj).toLowerCase()
830
- , isDeep = flag(this, 'deep')
831
- , descriptor = isDeep ? 'deep ' : '';
840
+ , objType = _.type(obj).toLowerCase();
832
841
 
833
842
  // This block is for asserting a subset of properties in an object.
834
843
  if (objType === 'object') {
@@ -865,10 +874,62 @@ module.exports = function (chai, _) {
865
874
  return;
866
875
  }
867
876
 
868
- // Assert inclusion in an array or substring in a string.
877
+ var isDeep = flag(this, 'deep')
878
+ , descriptor = isDeep ? 'deep ' : ''
879
+ , included = false;
880
+
881
+ switch (objType) {
882
+ case 'string':
883
+ included = obj.indexOf(val) !== -1;
884
+ break;
885
+
886
+ case 'weakset':
887
+ if (isDeep) {
888
+ var flagMsg = flag(this, 'message')
889
+ , ssfi = flag(this, 'ssfi');
890
+ flagMsg = flagMsg ? flagMsg + ': ' : '';
891
+
892
+ throw new AssertionError(
893
+ flagMsg + 'unable to use .deep.include with WeakSet',
894
+ undefined,
895
+ ssfi
896
+ );
897
+ }
898
+
899
+ included = obj.has(val);
900
+ break;
901
+
902
+ case 'map':
903
+ var isEql = isDeep ? _.eql : SameValueZero;
904
+ obj.forEach(function (item) {
905
+ included = included || isEql(item, val);
906
+ });
907
+ break;
908
+
909
+ case 'set':
910
+ if (isDeep) {
911
+ obj.forEach(function (item) {
912
+ included = included || _.eql(item, val);
913
+ });
914
+ } else {
915
+ included = obj.has(val);
916
+ }
917
+ break;
918
+
919
+ case 'array':
920
+ if (isDeep) {
921
+ included = obj.some(function (item) {
922
+ return _.eql(item, val);
923
+ })
924
+ } else {
925
+ included = obj.indexOf(val) !== -1;
926
+ }
927
+ break;
928
+ }
929
+
930
+ // Assert inclusion in collection or substring in a string.
869
931
  this.assert(
870
- objType === 'string' || !isDeep ? ~obj.indexOf(val)
871
- : isDeepIncluded(obj, val)
932
+ included
872
933
  , 'expected #{this} to ' + descriptor + 'include ' + _.inspect(val)
873
934
  , 'expected #{this} to not ' + descriptor + 'include ' + _.inspect(val));
874
935
  }
@@ -1385,7 +1446,7 @@ module.exports = function (chai, _) {
1385
1446
  /**
1386
1447
  * ### .above(n[, msg])
1387
1448
  *
1388
- * Asserts that the target is a number greater than the given number `n`.
1449
+ * Asserts that the target is a number or a date greater than the given number or date `n` respectively.
1389
1450
  * However, it's often best to assert that the target is equal to its expected
1390
1451
  * value.
1391
1452
  *
@@ -1430,21 +1491,29 @@ module.exports = function (chai, _) {
1430
1491
  var obj = flag(this, 'object')
1431
1492
  , doLength = flag(this, 'doLength')
1432
1493
  , flagMsg = flag(this, 'message')
1433
- , ssfi = flag(this, 'ssfi');
1494
+ , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '')
1495
+ , ssfi = flag(this, 'ssfi')
1496
+ , objType = _.type(obj).toLowerCase()
1497
+ , nType = _.type(n).toLowerCase()
1498
+ , shouldThrow = true;
1434
1499
 
1435
1500
  if (doLength) {
1436
1501
  new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
1502
+ }
1503
+
1504
+ if (!doLength && (objType === 'date' && nType !== 'date')) {
1505
+ errorMessage = msgPrefix + 'the argument to above must be a date';
1506
+ } else if (nType !== 'number' && (doLength || objType === 'number')) {
1507
+ errorMessage = msgPrefix + 'the argument to above must be a number';
1508
+ } else if (!doLength && (objType !== 'date' && objType !== 'number')) {
1509
+ var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
1510
+ errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
1437
1511
  } else {
1438
- new Assertion(obj, flagMsg, ssfi, true).is.a('number');
1512
+ shouldThrow = false;
1439
1513
  }
1440
1514
 
1441
- if (typeof n !== 'number') {
1442
- flagMsg = flagMsg ? flagMsg + ': ' : '';
1443
- throw new AssertionError(
1444
- flagMsg + 'the argument to above must be a number',
1445
- undefined,
1446
- ssfi
1447
- );
1515
+ if (shouldThrow) {
1516
+ throw new AssertionError(errorMessage, undefined, ssfi);
1448
1517
  }
1449
1518
 
1450
1519
  if (doLength) {
@@ -1459,8 +1528,9 @@ module.exports = function (chai, _) {
1459
1528
  } else {
1460
1529
  this.assert(
1461
1530
  obj > n
1462
- , 'expected #{this} to be above ' + n
1463
- , 'expected #{this} to be at most ' + n
1531
+ , 'expected #{this} to be above #{exp}'
1532
+ , 'expected #{this} to be at most #{exp}'
1533
+ , n
1464
1534
  );
1465
1535
  }
1466
1536
  }
@@ -1472,8 +1542,8 @@ module.exports = function (chai, _) {
1472
1542
  /**
1473
1543
  * ### .least(n[, msg])
1474
1544
  *
1475
- * Asserts that the target is a number greater than or equal to the given
1476
- * number `n`. However, it's often best to assert that the target is equal to
1545
+ * Asserts that the target is a number or a date greater than or equal to the given
1546
+ * number or date `n` respectively. However, it's often best to assert that the target is equal to
1477
1547
  * its expected value.
1478
1548
  *
1479
1549
  * expect(2).to.equal(2); // Recommended
@@ -1517,21 +1587,29 @@ module.exports = function (chai, _) {
1517
1587
  var obj = flag(this, 'object')
1518
1588
  , doLength = flag(this, 'doLength')
1519
1589
  , flagMsg = flag(this, 'message')
1520
- , ssfi = flag(this, 'ssfi');
1590
+ , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '')
1591
+ , ssfi = flag(this, 'ssfi')
1592
+ , objType = _.type(obj).toLowerCase()
1593
+ , nType = _.type(n).toLowerCase()
1594
+ , shouldThrow = true;
1521
1595
 
1522
1596
  if (doLength) {
1523
1597
  new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
1598
+ }
1599
+
1600
+ if (!doLength && (objType === 'date' && nType !== 'date')) {
1601
+ errorMessage = msgPrefix + 'the argument to least must be a date';
1602
+ } else if (nType !== 'number' && (doLength || objType === 'number')) {
1603
+ errorMessage = msgPrefix + 'the argument to least must be a number';
1604
+ } else if (!doLength && (objType !== 'date' && objType !== 'number')) {
1605
+ var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
1606
+ errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
1524
1607
  } else {
1525
- new Assertion(obj, flagMsg, ssfi, true).is.a('number');
1608
+ shouldThrow = false;
1526
1609
  }
1527
1610
 
1528
- if (typeof n !== 'number') {
1529
- flagMsg = flagMsg ? flagMsg + ': ' : '';
1530
- throw new AssertionError(
1531
- flagMsg + 'the argument to least must be a number',
1532
- undefined,
1533
- ssfi
1534
- );
1611
+ if (shouldThrow) {
1612
+ throw new AssertionError(errorMessage, undefined, ssfi);
1535
1613
  }
1536
1614
 
1537
1615
  if (doLength) {
@@ -1546,8 +1624,9 @@ module.exports = function (chai, _) {
1546
1624
  } else {
1547
1625
  this.assert(
1548
1626
  obj >= n
1549
- , 'expected #{this} to be at least ' + n
1550
- , 'expected #{this} to be below ' + n
1627
+ , 'expected #{this} to be at least #{exp}'
1628
+ , 'expected #{this} to be below #{exp}'
1629
+ , n
1551
1630
  );
1552
1631
  }
1553
1632
  }
@@ -1558,7 +1637,7 @@ module.exports = function (chai, _) {
1558
1637
  /**
1559
1638
  * ### .below(n[, msg])
1560
1639
  *
1561
- * Asserts that the target is a number less than the given number `n`.
1640
+ * Asserts that the target is a number or a date less than the given number or date `n` respectively.
1562
1641
  * However, it's often best to assert that the target is equal to its expected
1563
1642
  * value.
1564
1643
  *
@@ -1603,21 +1682,29 @@ module.exports = function (chai, _) {
1603
1682
  var obj = flag(this, 'object')
1604
1683
  , doLength = flag(this, 'doLength')
1605
1684
  , flagMsg = flag(this, 'message')
1606
- , ssfi = flag(this, 'ssfi');
1685
+ , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '')
1686
+ , ssfi = flag(this, 'ssfi')
1687
+ , objType = _.type(obj).toLowerCase()
1688
+ , nType = _.type(n).toLowerCase()
1689
+ , shouldThrow = true;
1607
1690
 
1608
1691
  if (doLength) {
1609
1692
  new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
1693
+ }
1694
+
1695
+ if (!doLength && (objType === 'date' && nType !== 'date')) {
1696
+ errorMessage = msgPrefix + 'the argument to below must be a date';
1697
+ } else if (nType !== 'number' && (doLength || objType === 'number')) {
1698
+ errorMessage = msgPrefix + 'the argument to below must be a number';
1699
+ } else if (!doLength && (objType !== 'date' && objType !== 'number')) {
1700
+ var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
1701
+ errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
1610
1702
  } else {
1611
- new Assertion(obj, flagMsg, ssfi, true).is.a('number');
1703
+ shouldThrow = false;
1612
1704
  }
1613
1705
 
1614
- if (typeof n !== 'number') {
1615
- flagMsg = flagMsg ? flagMsg + ': ' : '';
1616
- throw new AssertionError(
1617
- flagMsg + 'the argument to below must be a number',
1618
- undefined,
1619
- ssfi
1620
- );
1706
+ if (shouldThrow) {
1707
+ throw new AssertionError(errorMessage, undefined, ssfi);
1621
1708
  }
1622
1709
 
1623
1710
  if (doLength) {
@@ -1632,8 +1719,9 @@ module.exports = function (chai, _) {
1632
1719
  } else {
1633
1720
  this.assert(
1634
1721
  obj < n
1635
- , 'expected #{this} to be below ' + n
1636
- , 'expected #{this} to be at least ' + n
1722
+ , 'expected #{this} to be below #{exp}'
1723
+ , 'expected #{this} to be at least #{exp}'
1724
+ , n
1637
1725
  );
1638
1726
  }
1639
1727
  }
@@ -1645,8 +1733,8 @@ module.exports = function (chai, _) {
1645
1733
  /**
1646
1734
  * ### .most(n[, msg])
1647
1735
  *
1648
- * Asserts that the target is a number less than or equal to the given number
1649
- * `n`. However, it's often best to assert that the target is equal to its
1736
+ * Asserts that the target is a number or a date less than or equal to the given number
1737
+ * or date `n` respectively. However, it's often best to assert that the target is equal to its
1650
1738
  * expected value.
1651
1739
  *
1652
1740
  * expect(1).to.equal(1); // Recommended
@@ -1689,21 +1777,29 @@ module.exports = function (chai, _) {
1689
1777
  var obj = flag(this, 'object')
1690
1778
  , doLength = flag(this, 'doLength')
1691
1779
  , flagMsg = flag(this, 'message')
1692
- , ssfi = flag(this, 'ssfi');
1780
+ , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '')
1781
+ , ssfi = flag(this, 'ssfi')
1782
+ , objType = _.type(obj).toLowerCase()
1783
+ , nType = _.type(n).toLowerCase()
1784
+ , shouldThrow = true;
1693
1785
 
1694
1786
  if (doLength) {
1695
1787
  new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
1788
+ }
1789
+
1790
+ if (!doLength && (objType === 'date' && nType !== 'date')) {
1791
+ errorMessage = msgPrefix + 'the argument to most must be a date';
1792
+ } else if (nType !== 'number' && (doLength || objType === 'number')) {
1793
+ errorMessage = msgPrefix + 'the argument to most must be a number';
1794
+ } else if (!doLength && (objType !== 'date' && objType !== 'number')) {
1795
+ var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
1796
+ errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
1696
1797
  } else {
1697
- new Assertion(obj, flagMsg, ssfi, true).is.a('number');
1798
+ shouldThrow = false;
1698
1799
  }
1699
1800
 
1700
- if (typeof n !== 'number') {
1701
- flagMsg = flagMsg ? flagMsg + ': ' : '';
1702
- throw new AssertionError(
1703
- flagMsg + 'the argument to most must be a number',
1704
- undefined,
1705
- ssfi
1706
- );
1801
+ if (shouldThrow) {
1802
+ throw new AssertionError(errorMessage, undefined, ssfi);
1707
1803
  }
1708
1804
 
1709
1805
  if (doLength) {
@@ -1718,8 +1814,9 @@ module.exports = function (chai, _) {
1718
1814
  } else {
1719
1815
  this.assert(
1720
1816
  obj <= n
1721
- , 'expected #{this} to be at most ' + n
1722
- , 'expected #{this} to be above ' + n
1817
+ , 'expected #{this} to be at most #{exp}'
1818
+ , 'expected #{this} to be above #{exp}'
1819
+ , n
1723
1820
  );
1724
1821
  }
1725
1822
  }
@@ -1730,8 +1827,8 @@ module.exports = function (chai, _) {
1730
1827
  /**
1731
1828
  * ### .within(start, finish[, msg])
1732
1829
  *
1733
- * Asserts that the target is a number greater than or equal to the given
1734
- * number `start`, and less than or equal to the given number `finish`.
1830
+ * Asserts that the target is a number or a date greater than or equal to the given
1831
+ * number or date `start`, and less than or equal to the given number or date `finish` respectively.
1735
1832
  * However, it's often best to assert that the target is equal to its expected
1736
1833
  * value.
1737
1834
  *
@@ -1773,24 +1870,35 @@ module.exports = function (chai, _) {
1773
1870
  Assertion.addMethod('within', function (start, finish, msg) {
1774
1871
  if (msg) flag(this, 'message', msg);
1775
1872
  var obj = flag(this, 'object')
1776
- , range = start + '..' + finish
1777
1873
  , doLength = flag(this, 'doLength')
1778
1874
  , flagMsg = flag(this, 'message')
1779
- , ssfi = flag(this, 'ssfi');
1875
+ , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '')
1876
+ , ssfi = flag(this, 'ssfi')
1877
+ , objType = _.type(obj).toLowerCase()
1878
+ , startType = _.type(start).toLowerCase()
1879
+ , finishType = _.type(finish).toLowerCase()
1880
+ , shouldThrow = true
1881
+ , range = (startType === 'date' && finishType === 'date')
1882
+ ? start.toUTCString() + '..' + finish.toUTCString()
1883
+ : start + '..' + finish;
1780
1884
 
1781
1885
  if (doLength) {
1782
1886
  new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
1887
+ }
1888
+
1889
+ if (!doLength && (objType === 'date' && (startType !== 'date' || finishType !== 'date'))) {
1890
+ errorMessage = msgPrefix + 'the arguments to within must be dates';
1891
+ } else if ((startType !== 'number' || finishType !== 'number') && (doLength || objType === 'number')) {
1892
+ errorMessage = msgPrefix + 'the arguments to within must be numbers';
1893
+ } else if (!doLength && (objType !== 'date' && objType !== 'number')) {
1894
+ var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
1895
+ errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
1783
1896
  } else {
1784
- new Assertion(obj, flagMsg, ssfi, true).is.a('number');
1897
+ shouldThrow = false;
1785
1898
  }
1786
1899
 
1787
- if (typeof start !== 'number' || typeof finish !== 'number') {
1788
- flagMsg = flagMsg ? flagMsg + ': ' : '';
1789
- throw new AssertionError(
1790
- flagMsg + 'the arguments to within must be numbers',
1791
- undefined,
1792
- ssfi
1793
- );
1900
+ if (shouldThrow) {
1901
+ throw new AssertionError(errorMessage, undefined, ssfi);
1794
1902
  }
1795
1903
 
1796
1904
  if (doLength) {
@@ -2005,6 +2113,7 @@ module.exports = function (chai, _) {
2005
2113
  var isNested = flag(this, 'nested')
2006
2114
  , isOwn = flag(this, 'own')
2007
2115
  , flagMsg = flag(this, 'message')
2116
+ , obj = flag(this, 'object')
2008
2117
  , ssfi = flag(this, 'ssfi');
2009
2118
 
2010
2119
  if (isNested && isOwn) {
@@ -2016,9 +2125,17 @@ module.exports = function (chai, _) {
2016
2125
  );
2017
2126
  }
2018
2127
 
2128
+ if (obj === null || obj === undefined) {
2129
+ flagMsg = flagMsg ? flagMsg + ': ' : '';
2130
+ throw new AssertionError(
2131
+ flagMsg + 'Target cannot be null or undefined.',
2132
+ undefined,
2133
+ ssfi
2134
+ );
2135
+ }
2136
+
2019
2137
  var isDeep = flag(this, 'deep')
2020
2138
  , negate = flag(this, 'negate')
2021
- , obj = flag(this, 'object')
2022
2139
  , pathInfo = isNested ? _.getPathInfo(obj, name) : null
2023
2140
  , value = isNested ? pathInfo.value : obj[name];
2024
2141
 
@@ -7720,8 +7837,6 @@ module.exports = function compareByInspect(a, b) {
7720
7837
  *
7721
7838
  * @param {Mixed} obj constructed Assertion
7722
7839
  * @param {Array} type A list of allowed types for this assertion
7723
- * @param {Function} ssfi starting point for removing implementation frames from
7724
- * stack trace of AssertionError
7725
7840
  * @namespace Utils
7726
7841
  * @name expectTypes
7727
7842
  * @api public
@@ -7731,8 +7846,9 @@ var AssertionError = require('assertion-error');
7731
7846
  var flag = require('./flag');
7732
7847
  var type = require('type-detect');
7733
7848
 
7734
- module.exports = function expectTypes(obj, types, ssfi) {
7849
+ module.exports = function expectTypes(obj, types) {
7735
7850
  var flagMsg = flag(obj, 'message');
7851
+ var ssfi = flag(obj, 'ssfi');
7736
7852
 
7737
7853
  flagMsg = flagMsg ? flagMsg + ': ' : '';
7738
7854
 
@@ -7740,7 +7856,7 @@ module.exports = function expectTypes(obj, types, ssfi) {
7740
7856
  types = types.map(function (t) { return t.toLowerCase(); });
7741
7857
  types.sort();
7742
7858
 
7743
- // Transforms ['lorem', 'ipsum'] into 'a lirum, or an ipsum'
7859
+ // Transforms ['lorem', 'ipsum'] into 'a lorem, or an ipsum'
7744
7860
  var str = types.map(function (t, index) {
7745
7861
  var art = ~[ 'a', 'e', 'i', 'o', 'u' ].indexOf(t.charAt(0)) ? 'an' : 'a';
7746
7862
  var or = types.length > 1 && index === types.length - 1 ? 'or ' : '';
@@ -8257,7 +8373,7 @@ function formatValue(ctx, value, recurseTimes) {
8257
8373
  var container = document.createElementNS(ns, '_');
8258
8374
 
8259
8375
  container.appendChild(value.cloneNode(false));
8260
- html = container.innerHTML
8376
+ var html = container.innerHTML
8261
8377
  .replace('><', '>' + value.innerHTML + '<');
8262
8378
  container.innerHTML = '';
8263
8379
  return html;
@@ -330,6 +330,16 @@ module.exports = function (chai, _) {
330
330
  *
331
331
  * expect({a: 1, b: 2, c: 3}).to.include({a: 1, b: 2});
332
332
  *
333
+ * When the target is a Set or WeakSet, `.include` asserts that the given `val` is a
334
+ * member of the target. SameValueZero equality algorithm is used.
335
+ *
336
+ * expect(new Set([1, 2])).to.include(2);
337
+ *
338
+ * When the target is a Map, `.include` asserts that the given `val` is one of
339
+ * the values of the target. SameValueZero equality algorithm is used.
340
+ *
341
+ * expect(new Map([['a', 1], ['b', 2]])).to.include(2);
342
+ *
333
343
  * Because `.include` does different things based on the target's type, it's
334
344
  * important to check the target's type before using `.include`. See the `.a`
335
345
  * doc for info on testing a target's type.
@@ -338,8 +348,8 @@ module.exports = function (chai, _) {
338
348
  *
339
349
  * By default, strict (`===`) equality is used to compare array members and
340
350
  * object properties. Add `.deep` earlier in the chain to use deep equality
341
- * instead. See the `deep-eql` project page for info on the deep equality
342
- * algorithm: https://github.com/chaijs/deep-eql.
351
+ * instead (WeakSet targets are not supported). See the `deep-eql` project
352
+ * page for info on the deep equality algorithm: https://github.com/chaijs/deep-eql.
343
353
  *
344
354
  * // Target array deeply (but not strictly) includes `{a: 1}`
345
355
  * expect([{a: 1}]).to.deep.include({a: 1});
@@ -449,25 +459,24 @@ module.exports = function (chai, _) {
449
459
  * @api public
450
460
  */
451
461
 
452
- function includeChainingBehavior () {
453
- flag(this, 'contains', true);
462
+ function SameValueZero(a, b) {
463
+ return (_.isNaN(a) && _.isNaN(b)) || a === b;
454
464
  }
455
465
 
456
- function isDeepIncluded (arr, val) {
457
- return arr.some(function (arrVal) {
458
- return _.eql(arrVal, val);
459
- });
466
+ function includeChainingBehavior () {
467
+ flag(this, 'contains', true);
460
468
  }
461
469
 
462
470
  function include (val, msg) {
463
471
  if (msg) flag(this, 'message', msg);
464
472
 
465
- _.expectTypes(this, ['array', 'object', 'string'], flag(this, 'ssfi'));
473
+ _.expectTypes(this, [
474
+ 'array', 'object', 'string',
475
+ 'map', 'set', 'weakset',
476
+ ]);
466
477
 
467
478
  var obj = flag(this, 'object')
468
- , objType = _.type(obj).toLowerCase()
469
- , isDeep = flag(this, 'deep')
470
- , descriptor = isDeep ? 'deep ' : '';
479
+ , objType = _.type(obj).toLowerCase();
471
480
 
472
481
  // This block is for asserting a subset of properties in an object.
473
482
  if (objType === 'object') {
@@ -504,10 +513,62 @@ module.exports = function (chai, _) {
504
513
  return;
505
514
  }
506
515
 
507
- // Assert inclusion in an array or substring in a string.
516
+ var isDeep = flag(this, 'deep')
517
+ , descriptor = isDeep ? 'deep ' : ''
518
+ , included = false;
519
+
520
+ switch (objType) {
521
+ case 'string':
522
+ included = obj.indexOf(val) !== -1;
523
+ break;
524
+
525
+ case 'weakset':
526
+ if (isDeep) {
527
+ var flagMsg = flag(this, 'message')
528
+ , ssfi = flag(this, 'ssfi');
529
+ flagMsg = flagMsg ? flagMsg + ': ' : '';
530
+
531
+ throw new AssertionError(
532
+ flagMsg + 'unable to use .deep.include with WeakSet',
533
+ undefined,
534
+ ssfi
535
+ );
536
+ }
537
+
538
+ included = obj.has(val);
539
+ break;
540
+
541
+ case 'map':
542
+ var isEql = isDeep ? _.eql : SameValueZero;
543
+ obj.forEach(function (item) {
544
+ included = included || isEql(item, val);
545
+ });
546
+ break;
547
+
548
+ case 'set':
549
+ if (isDeep) {
550
+ obj.forEach(function (item) {
551
+ included = included || _.eql(item, val);
552
+ });
553
+ } else {
554
+ included = obj.has(val);
555
+ }
556
+ break;
557
+
558
+ case 'array':
559
+ if (isDeep) {
560
+ included = obj.some(function (item) {
561
+ return _.eql(item, val);
562
+ })
563
+ } else {
564
+ included = obj.indexOf(val) !== -1;
565
+ }
566
+ break;
567
+ }
568
+
569
+ // Assert inclusion in collection or substring in a string.
508
570
  this.assert(
509
- objType === 'string' || !isDeep ? ~obj.indexOf(val)
510
- : isDeepIncluded(obj, val)
571
+ included
511
572
  , 'expected #{this} to ' + descriptor + 'include ' + _.inspect(val)
512
573
  , 'expected #{this} to not ' + descriptor + 'include ' + _.inspect(val));
513
574
  }
@@ -1024,7 +1085,7 @@ module.exports = function (chai, _) {
1024
1085
  /**
1025
1086
  * ### .above(n[, msg])
1026
1087
  *
1027
- * Asserts that the target is a number greater than the given number `n`.
1088
+ * Asserts that the target is a number or a date greater than the given number or date `n` respectively.
1028
1089
  * However, it's often best to assert that the target is equal to its expected
1029
1090
  * value.
1030
1091
  *
@@ -1069,21 +1130,29 @@ module.exports = function (chai, _) {
1069
1130
  var obj = flag(this, 'object')
1070
1131
  , doLength = flag(this, 'doLength')
1071
1132
  , flagMsg = flag(this, 'message')
1072
- , ssfi = flag(this, 'ssfi');
1133
+ , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '')
1134
+ , ssfi = flag(this, 'ssfi')
1135
+ , objType = _.type(obj).toLowerCase()
1136
+ , nType = _.type(n).toLowerCase()
1137
+ , shouldThrow = true;
1073
1138
 
1074
1139
  if (doLength) {
1075
1140
  new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
1141
+ }
1142
+
1143
+ if (!doLength && (objType === 'date' && nType !== 'date')) {
1144
+ errorMessage = msgPrefix + 'the argument to above must be a date';
1145
+ } else if (nType !== 'number' && (doLength || objType === 'number')) {
1146
+ errorMessage = msgPrefix + 'the argument to above must be a number';
1147
+ } else if (!doLength && (objType !== 'date' && objType !== 'number')) {
1148
+ var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
1149
+ errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
1076
1150
  } else {
1077
- new Assertion(obj, flagMsg, ssfi, true).is.a('number');
1151
+ shouldThrow = false;
1078
1152
  }
1079
1153
 
1080
- if (typeof n !== 'number') {
1081
- flagMsg = flagMsg ? flagMsg + ': ' : '';
1082
- throw new AssertionError(
1083
- flagMsg + 'the argument to above must be a number',
1084
- undefined,
1085
- ssfi
1086
- );
1154
+ if (shouldThrow) {
1155
+ throw new AssertionError(errorMessage, undefined, ssfi);
1087
1156
  }
1088
1157
 
1089
1158
  if (doLength) {
@@ -1098,8 +1167,9 @@ module.exports = function (chai, _) {
1098
1167
  } else {
1099
1168
  this.assert(
1100
1169
  obj > n
1101
- , 'expected #{this} to be above ' + n
1102
- , 'expected #{this} to be at most ' + n
1170
+ , 'expected #{this} to be above #{exp}'
1171
+ , 'expected #{this} to be at most #{exp}'
1172
+ , n
1103
1173
  );
1104
1174
  }
1105
1175
  }
@@ -1111,8 +1181,8 @@ module.exports = function (chai, _) {
1111
1181
  /**
1112
1182
  * ### .least(n[, msg])
1113
1183
  *
1114
- * Asserts that the target is a number greater than or equal to the given
1115
- * number `n`. However, it's often best to assert that the target is equal to
1184
+ * Asserts that the target is a number or a date greater than or equal to the given
1185
+ * number or date `n` respectively. However, it's often best to assert that the target is equal to
1116
1186
  * its expected value.
1117
1187
  *
1118
1188
  * expect(2).to.equal(2); // Recommended
@@ -1156,21 +1226,29 @@ module.exports = function (chai, _) {
1156
1226
  var obj = flag(this, 'object')
1157
1227
  , doLength = flag(this, 'doLength')
1158
1228
  , flagMsg = flag(this, 'message')
1159
- , ssfi = flag(this, 'ssfi');
1229
+ , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '')
1230
+ , ssfi = flag(this, 'ssfi')
1231
+ , objType = _.type(obj).toLowerCase()
1232
+ , nType = _.type(n).toLowerCase()
1233
+ , shouldThrow = true;
1160
1234
 
1161
1235
  if (doLength) {
1162
1236
  new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
1237
+ }
1238
+
1239
+ if (!doLength && (objType === 'date' && nType !== 'date')) {
1240
+ errorMessage = msgPrefix + 'the argument to least must be a date';
1241
+ } else if (nType !== 'number' && (doLength || objType === 'number')) {
1242
+ errorMessage = msgPrefix + 'the argument to least must be a number';
1243
+ } else if (!doLength && (objType !== 'date' && objType !== 'number')) {
1244
+ var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
1245
+ errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
1163
1246
  } else {
1164
- new Assertion(obj, flagMsg, ssfi, true).is.a('number');
1247
+ shouldThrow = false;
1165
1248
  }
1166
1249
 
1167
- if (typeof n !== 'number') {
1168
- flagMsg = flagMsg ? flagMsg + ': ' : '';
1169
- throw new AssertionError(
1170
- flagMsg + 'the argument to least must be a number',
1171
- undefined,
1172
- ssfi
1173
- );
1250
+ if (shouldThrow) {
1251
+ throw new AssertionError(errorMessage, undefined, ssfi);
1174
1252
  }
1175
1253
 
1176
1254
  if (doLength) {
@@ -1185,8 +1263,9 @@ module.exports = function (chai, _) {
1185
1263
  } else {
1186
1264
  this.assert(
1187
1265
  obj >= n
1188
- , 'expected #{this} to be at least ' + n
1189
- , 'expected #{this} to be below ' + n
1266
+ , 'expected #{this} to be at least #{exp}'
1267
+ , 'expected #{this} to be below #{exp}'
1268
+ , n
1190
1269
  );
1191
1270
  }
1192
1271
  }
@@ -1197,7 +1276,7 @@ module.exports = function (chai, _) {
1197
1276
  /**
1198
1277
  * ### .below(n[, msg])
1199
1278
  *
1200
- * Asserts that the target is a number less than the given number `n`.
1279
+ * Asserts that the target is a number or a date less than the given number or date `n` respectively.
1201
1280
  * However, it's often best to assert that the target is equal to its expected
1202
1281
  * value.
1203
1282
  *
@@ -1242,21 +1321,29 @@ module.exports = function (chai, _) {
1242
1321
  var obj = flag(this, 'object')
1243
1322
  , doLength = flag(this, 'doLength')
1244
1323
  , flagMsg = flag(this, 'message')
1245
- , ssfi = flag(this, 'ssfi');
1324
+ , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '')
1325
+ , ssfi = flag(this, 'ssfi')
1326
+ , objType = _.type(obj).toLowerCase()
1327
+ , nType = _.type(n).toLowerCase()
1328
+ , shouldThrow = true;
1246
1329
 
1247
1330
  if (doLength) {
1248
1331
  new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
1332
+ }
1333
+
1334
+ if (!doLength && (objType === 'date' && nType !== 'date')) {
1335
+ errorMessage = msgPrefix + 'the argument to below must be a date';
1336
+ } else if (nType !== 'number' && (doLength || objType === 'number')) {
1337
+ errorMessage = msgPrefix + 'the argument to below must be a number';
1338
+ } else if (!doLength && (objType !== 'date' && objType !== 'number')) {
1339
+ var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
1340
+ errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
1249
1341
  } else {
1250
- new Assertion(obj, flagMsg, ssfi, true).is.a('number');
1342
+ shouldThrow = false;
1251
1343
  }
1252
1344
 
1253
- if (typeof n !== 'number') {
1254
- flagMsg = flagMsg ? flagMsg + ': ' : '';
1255
- throw new AssertionError(
1256
- flagMsg + 'the argument to below must be a number',
1257
- undefined,
1258
- ssfi
1259
- );
1345
+ if (shouldThrow) {
1346
+ throw new AssertionError(errorMessage, undefined, ssfi);
1260
1347
  }
1261
1348
 
1262
1349
  if (doLength) {
@@ -1271,8 +1358,9 @@ module.exports = function (chai, _) {
1271
1358
  } else {
1272
1359
  this.assert(
1273
1360
  obj < n
1274
- , 'expected #{this} to be below ' + n
1275
- , 'expected #{this} to be at least ' + n
1361
+ , 'expected #{this} to be below #{exp}'
1362
+ , 'expected #{this} to be at least #{exp}'
1363
+ , n
1276
1364
  );
1277
1365
  }
1278
1366
  }
@@ -1284,8 +1372,8 @@ module.exports = function (chai, _) {
1284
1372
  /**
1285
1373
  * ### .most(n[, msg])
1286
1374
  *
1287
- * Asserts that the target is a number less than or equal to the given number
1288
- * `n`. However, it's often best to assert that the target is equal to its
1375
+ * Asserts that the target is a number or a date less than or equal to the given number
1376
+ * or date `n` respectively. However, it's often best to assert that the target is equal to its
1289
1377
  * expected value.
1290
1378
  *
1291
1379
  * expect(1).to.equal(1); // Recommended
@@ -1328,21 +1416,29 @@ module.exports = function (chai, _) {
1328
1416
  var obj = flag(this, 'object')
1329
1417
  , doLength = flag(this, 'doLength')
1330
1418
  , flagMsg = flag(this, 'message')
1331
- , ssfi = flag(this, 'ssfi');
1419
+ , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '')
1420
+ , ssfi = flag(this, 'ssfi')
1421
+ , objType = _.type(obj).toLowerCase()
1422
+ , nType = _.type(n).toLowerCase()
1423
+ , shouldThrow = true;
1332
1424
 
1333
1425
  if (doLength) {
1334
1426
  new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
1427
+ }
1428
+
1429
+ if (!doLength && (objType === 'date' && nType !== 'date')) {
1430
+ errorMessage = msgPrefix + 'the argument to most must be a date';
1431
+ } else if (nType !== 'number' && (doLength || objType === 'number')) {
1432
+ errorMessage = msgPrefix + 'the argument to most must be a number';
1433
+ } else if (!doLength && (objType !== 'date' && objType !== 'number')) {
1434
+ var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
1435
+ errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
1335
1436
  } else {
1336
- new Assertion(obj, flagMsg, ssfi, true).is.a('number');
1437
+ shouldThrow = false;
1337
1438
  }
1338
1439
 
1339
- if (typeof n !== 'number') {
1340
- flagMsg = flagMsg ? flagMsg + ': ' : '';
1341
- throw new AssertionError(
1342
- flagMsg + 'the argument to most must be a number',
1343
- undefined,
1344
- ssfi
1345
- );
1440
+ if (shouldThrow) {
1441
+ throw new AssertionError(errorMessage, undefined, ssfi);
1346
1442
  }
1347
1443
 
1348
1444
  if (doLength) {
@@ -1357,8 +1453,9 @@ module.exports = function (chai, _) {
1357
1453
  } else {
1358
1454
  this.assert(
1359
1455
  obj <= n
1360
- , 'expected #{this} to be at most ' + n
1361
- , 'expected #{this} to be above ' + n
1456
+ , 'expected #{this} to be at most #{exp}'
1457
+ , 'expected #{this} to be above #{exp}'
1458
+ , n
1362
1459
  );
1363
1460
  }
1364
1461
  }
@@ -1369,8 +1466,8 @@ module.exports = function (chai, _) {
1369
1466
  /**
1370
1467
  * ### .within(start, finish[, msg])
1371
1468
  *
1372
- * Asserts that the target is a number greater than or equal to the given
1373
- * number `start`, and less than or equal to the given number `finish`.
1469
+ * Asserts that the target is a number or a date greater than or equal to the given
1470
+ * number or date `start`, and less than or equal to the given number or date `finish` respectively.
1374
1471
  * However, it's often best to assert that the target is equal to its expected
1375
1472
  * value.
1376
1473
  *
@@ -1412,24 +1509,35 @@ module.exports = function (chai, _) {
1412
1509
  Assertion.addMethod('within', function (start, finish, msg) {
1413
1510
  if (msg) flag(this, 'message', msg);
1414
1511
  var obj = flag(this, 'object')
1415
- , range = start + '..' + finish
1416
1512
  , doLength = flag(this, 'doLength')
1417
1513
  , flagMsg = flag(this, 'message')
1418
- , ssfi = flag(this, 'ssfi');
1514
+ , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '')
1515
+ , ssfi = flag(this, 'ssfi')
1516
+ , objType = _.type(obj).toLowerCase()
1517
+ , startType = _.type(start).toLowerCase()
1518
+ , finishType = _.type(finish).toLowerCase()
1519
+ , shouldThrow = true
1520
+ , range = (startType === 'date' && finishType === 'date')
1521
+ ? start.toUTCString() + '..' + finish.toUTCString()
1522
+ : start + '..' + finish;
1419
1523
 
1420
1524
  if (doLength) {
1421
1525
  new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
1526
+ }
1527
+
1528
+ if (!doLength && (objType === 'date' && (startType !== 'date' || finishType !== 'date'))) {
1529
+ errorMessage = msgPrefix + 'the arguments to within must be dates';
1530
+ } else if ((startType !== 'number' || finishType !== 'number') && (doLength || objType === 'number')) {
1531
+ errorMessage = msgPrefix + 'the arguments to within must be numbers';
1532
+ } else if (!doLength && (objType !== 'date' && objType !== 'number')) {
1533
+ var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
1534
+ errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
1422
1535
  } else {
1423
- new Assertion(obj, flagMsg, ssfi, true).is.a('number');
1536
+ shouldThrow = false;
1424
1537
  }
1425
1538
 
1426
- if (typeof start !== 'number' || typeof finish !== 'number') {
1427
- flagMsg = flagMsg ? flagMsg + ': ' : '';
1428
- throw new AssertionError(
1429
- flagMsg + 'the arguments to within must be numbers',
1430
- undefined,
1431
- ssfi
1432
- );
1539
+ if (shouldThrow) {
1540
+ throw new AssertionError(errorMessage, undefined, ssfi);
1433
1541
  }
1434
1542
 
1435
1543
  if (doLength) {
@@ -1644,6 +1752,7 @@ module.exports = function (chai, _) {
1644
1752
  var isNested = flag(this, 'nested')
1645
1753
  , isOwn = flag(this, 'own')
1646
1754
  , flagMsg = flag(this, 'message')
1755
+ , obj = flag(this, 'object')
1647
1756
  , ssfi = flag(this, 'ssfi');
1648
1757
 
1649
1758
  if (isNested && isOwn) {
@@ -1655,9 +1764,17 @@ module.exports = function (chai, _) {
1655
1764
  );
1656
1765
  }
1657
1766
 
1767
+ if (obj === null || obj === undefined) {
1768
+ flagMsg = flagMsg ? flagMsg + ': ' : '';
1769
+ throw new AssertionError(
1770
+ flagMsg + 'Target cannot be null or undefined.',
1771
+ undefined,
1772
+ ssfi
1773
+ );
1774
+ }
1775
+
1658
1776
  var isDeep = flag(this, 'deep')
1659
1777
  , negate = flag(this, 'negate')
1660
- , obj = flag(this, 'object')
1661
1778
  , pathInfo = isNested ? _.getPathInfo(obj, name) : null
1662
1779
  , value = isNested ? pathInfo.value : obj[name];
1663
1780
 
@@ -13,8 +13,6 @@
13
13
  *
14
14
  * @param {Mixed} obj constructed Assertion
15
15
  * @param {Array} type A list of allowed types for this assertion
16
- * @param {Function} ssfi starting point for removing implementation frames from
17
- * stack trace of AssertionError
18
16
  * @namespace Utils
19
17
  * @name expectTypes
20
18
  * @api public
@@ -24,8 +22,9 @@ var AssertionError = require('assertion-error');
24
22
  var flag = require('./flag');
25
23
  var type = require('type-detect');
26
24
 
27
- module.exports = function expectTypes(obj, types, ssfi) {
25
+ module.exports = function expectTypes(obj, types) {
28
26
  var flagMsg = flag(obj, 'message');
27
+ var ssfi = flag(obj, 'ssfi');
29
28
 
30
29
  flagMsg = flagMsg ? flagMsg + ': ' : '';
31
30
 
@@ -33,7 +32,7 @@ module.exports = function expectTypes(obj, types, ssfi) {
33
32
  types = types.map(function (t) { return t.toLowerCase(); });
34
33
  types.sort();
35
34
 
36
- // Transforms ['lorem', 'ipsum'] into 'a lirum, or an ipsum'
35
+ // Transforms ['lorem', 'ipsum'] into 'a lorem, or an ipsum'
37
36
  var str = types.map(function (t, index) {
38
37
  var art = ~[ 'a', 'e', 'i', 'o', 'u' ].indexOf(t.charAt(0)) ? 'an' : 'a';
39
38
  var or = types.length > 1 && index === types.length - 1 ? 'or ' : '';
@@ -86,7 +86,7 @@ function formatValue(ctx, value, recurseTimes) {
86
86
  var container = document.createElementNS(ns, '_');
87
87
 
88
88
  container.appendChild(value.cloneNode(false));
89
- html = container.innerHTML
89
+ var html = container.innerHTML
90
90
  .replace('><', '>' + value.innerHTML + '<');
91
91
  container.innerHTML = '';
92
92
  return html;
package/lib/chai.js CHANGED
@@ -10,7 +10,7 @@ var used = [];
10
10
  * Chai version
11
11
  */
12
12
 
13
- exports.version = '4.0.2';
13
+ exports.version = '4.1.0';
14
14
 
15
15
  /*!
16
16
  * Assertion Error
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "Veselin Todorov <hi@vesln.com>",
18
18
  "John Firebaugh <john.firebaugh@gmail.com>"
19
19
  ],
20
- "version": "4.0.2",
20
+ "version": "4.1.0",
21
21
  "repository": {
22
22
  "type": "git",
23
23
  "url": "https://github.com/chaijs/chai"
@@ -41,7 +41,7 @@
41
41
  "type-detect": "^4.0.0"
42
42
  },
43
43
  "devDependencies": {
44
- "browserify": "^13.0.1",
44
+ "browserify": "^14.4.0",
45
45
  "bump-cli": "^1.1.3",
46
46
  "istanbul": "^0.4.3",
47
47
  "karma": "^1.0.0",