chai 4.1.0 → 4.3.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.
@@ -33,16 +33,17 @@ module.exports = function (chai, _) {
33
33
  * - same
34
34
  * - but
35
35
  * - does
36
+ * - still
36
37
  *
37
38
  * @name language chains
38
39
  * @namespace BDD
39
40
  * @api public
40
41
  */
41
42
 
42
- [ 'to', 'be', 'been'
43
- , 'is', 'and', 'has', 'have'
44
- , 'with', 'that', 'which', 'at'
45
- , 'of', 'same', 'but', 'does' ].forEach(function (chain) {
43
+ [ 'to', 'be', 'been', 'is'
44
+ , 'and', 'has', 'have', 'with'
45
+ , 'that', 'which', 'at', 'of'
46
+ , 'same', 'but', 'does', 'still' ].forEach(function (chain) {
46
47
  Assertion.addProperty(chain);
47
48
  });
48
49
 
@@ -149,7 +150,8 @@ module.exports = function (chai, _) {
149
150
  * Object.prototype.b = 2;
150
151
  *
151
152
  * expect({a: 1}).to.have.own.property('a');
152
- * expect({a: 1}).to.have.property('b').but.not.own.property('b');
153
+ * expect({a: 1}).to.have.property('b');
154
+ * expect({a: 1}).to.not.have.own.property('b');
153
155
  *
154
156
  * expect({a: 1}).to.own.include({a: 1});
155
157
  * expect({a: 1}).to.include({b: 2}).but.not.own.include({b: 2});
@@ -210,7 +212,6 @@ module.exports = function (chai, _) {
210
212
  flag(this, 'all', false);
211
213
  });
212
214
 
213
-
214
215
  /**
215
216
  * ### .all
216
217
  *
@@ -282,7 +283,7 @@ module.exports = function (chai, _) {
282
283
  * expect(1, 'nooo why fail??').to.be.a('string');
283
284
  *
284
285
  * `.a` can also be used as a language chain to improve the readability of
285
- * your assertions.
286
+ * your assertions.
286
287
  *
287
288
  * expect({b: 2}).to.have.a.property('b');
288
289
  *
@@ -396,7 +397,7 @@ module.exports = function (chai, _) {
396
397
  *
397
398
  * expect('foobar').to.not.include('taco');
398
399
  * expect([1, 2, 3]).to.not.include(4);
399
- *
400
+ *
400
401
  * However, it's dangerous to negate `.include` when the target is an object.
401
402
  * The problem is that it creates uncertain expectations by asserting that the
402
403
  * target object doesn't have all of `val`'s key/value pairs but may or may
@@ -470,52 +471,17 @@ module.exports = function (chai, _) {
470
471
  function include (val, msg) {
471
472
  if (msg) flag(this, 'message', msg);
472
473
 
473
- _.expectTypes(this, [
474
- 'array', 'object', 'string',
475
- 'map', 'set', 'weakset',
476
- ]);
477
-
478
474
  var obj = flag(this, 'object')
479
- , objType = _.type(obj).toLowerCase();
480
-
481
- // This block is for asserting a subset of properties in an object.
482
- if (objType === 'object') {
483
- var props = Object.keys(val)
484
- , negate = flag(this, 'negate')
485
- , firstErr = null
486
- , numErrs = 0;
487
-
488
- props.forEach(function (prop) {
489
- var propAssertion = new Assertion(obj);
490
- _.transferFlags(this, propAssertion, true);
491
- flag(propAssertion, 'lockSsfi', true);
492
-
493
- if (!negate || props.length === 1) {
494
- propAssertion.property(prop, val[prop]);
495
- return;
496
- }
497
-
498
- try {
499
- propAssertion.property(prop, val[prop]);
500
- } catch (err) {
501
- if (!_.checkError.compatibleConstructor(err, AssertionError)) throw err;
502
- if (firstErr === null) firstErr = err;
503
- numErrs++;
504
- }
505
- }, this);
506
-
507
- // When validating .not.include with multiple properties, we only want
508
- // to throw an assertion error if all of the properties are included,
509
- // in which case we throw the first property assertion error that we
510
- // encountered.
511
- if (negate && props.length > 1 && numErrs === props.length) throw firstErr;
475
+ , objType = _.type(obj).toLowerCase()
476
+ , flagMsg = flag(this, 'message')
477
+ , negate = flag(this, 'negate')
478
+ , ssfi = flag(this, 'ssfi')
479
+ , isDeep = flag(this, 'deep')
480
+ , descriptor = isDeep ? 'deep ' : '';
512
481
 
513
- return;
514
- }
482
+ flagMsg = flagMsg ? flagMsg + ': ' : '';
515
483
 
516
- var isDeep = flag(this, 'deep')
517
- , descriptor = isDeep ? 'deep ' : ''
518
- , included = false;
484
+ var included = false;
519
485
 
520
486
  switch (objType) {
521
487
  case 'string':
@@ -524,10 +490,6 @@ module.exports = function (chai, _) {
524
490
 
525
491
  case 'weakset':
526
492
  if (isDeep) {
527
- var flagMsg = flag(this, 'message')
528
- , ssfi = flag(this, 'ssfi');
529
- flagMsg = flagMsg ? flagMsg + ': ' : '';
530
-
531
493
  throw new AssertionError(
532
494
  flagMsg + 'unable to use .deep.include with WeakSet',
533
495
  undefined,
@@ -564,6 +526,58 @@ module.exports = function (chai, _) {
564
526
  included = obj.indexOf(val) !== -1;
565
527
  }
566
528
  break;
529
+
530
+ default:
531
+ // This block is for asserting a subset of properties in an object.
532
+ // `_.expectTypes` isn't used here because `.include` should work with
533
+ // objects with a custom `@@toStringTag`.
534
+ if (val !== Object(val)) {
535
+ throw new AssertionError(
536
+ flagMsg + 'the given combination of arguments ('
537
+ + objType + ' and '
538
+ + _.type(val).toLowerCase() + ')'
539
+ + ' is invalid for this assertion. '
540
+ + 'You can use an array, a map, an object, a set, a string, '
541
+ + 'or a weakset instead of a '
542
+ + _.type(val).toLowerCase(),
543
+ undefined,
544
+ ssfi
545
+ );
546
+ }
547
+
548
+ var props = Object.keys(val)
549
+ , firstErr = null
550
+ , numErrs = 0;
551
+
552
+ props.forEach(function (prop) {
553
+ var propAssertion = new Assertion(obj);
554
+ _.transferFlags(this, propAssertion, true);
555
+ flag(propAssertion, 'lockSsfi', true);
556
+
557
+ if (!negate || props.length === 1) {
558
+ propAssertion.property(prop, val[prop]);
559
+ return;
560
+ }
561
+
562
+ try {
563
+ propAssertion.property(prop, val[prop]);
564
+ } catch (err) {
565
+ if (!_.checkError.compatibleConstructor(err, AssertionError)) {
566
+ throw err;
567
+ }
568
+ if (firstErr === null) firstErr = err;
569
+ numErrs++;
570
+ }
571
+ }, this);
572
+
573
+ // When validating .not.include with multiple properties, we only want
574
+ // to throw an assertion error if all of the properties are included,
575
+ // in which case we throw the first property assertion error that we
576
+ // encountered.
577
+ if (negate && props.length > 1 && numErrs === props.length) {
578
+ throw firstErr;
579
+ }
580
+ return;
567
581
  }
568
582
 
569
583
  // Assert inclusion in collection or substring in a string.
@@ -581,9 +595,9 @@ module.exports = function (chai, _) {
581
595
  /**
582
596
  * ### .ok
583
597
  *
584
- * Asserts that the target is loosely (`==`) equal to `true`. However, it's
585
- * often best to assert that the target is strictly (`===`) or deeply equal to
586
- * its expected value.
598
+ * Asserts that the target is a truthy value (considered `true` in boolean context).
599
+ * However, it's often best to assert that the target is strictly (`===`) or
600
+ * deeply equal to its expected value.
587
601
  *
588
602
  * expect(1).to.equal(1); // Recommended
589
603
  * expect(1).to.be.ok; // Not recommended
@@ -930,7 +944,7 @@ module.exports = function (chai, _) {
930
944
  *
931
945
  * Add `.not` earlier in the chain to negate `.arguments`. However, it's often
932
946
  * best to assert which type the target is expected to be, rather than
933
- * asserting that its not an `arguments` object.
947
+ * asserting that it’s not an `arguments` object.
934
948
  *
935
949
  * expect('foo').to.be.a('string'); // Recommended
936
950
  * expect('foo').to.not.be.arguments; // Not recommended
@@ -967,7 +981,7 @@ module.exports = function (chai, _) {
967
981
  *
968
982
  * expect(1).to.equal(1);
969
983
  * expect('foo').to.equal('foo');
970
- *
984
+ *
971
985
  * Add `.deep` earlier in the chain to use deep equality instead. See the
972
986
  * `deep-eql` project page for info on the deep equality algorithm:
973
987
  * https://github.com/chaijs/deep-eql.
@@ -1009,7 +1023,10 @@ module.exports = function (chai, _) {
1009
1023
  if (msg) flag(this, 'message', msg);
1010
1024
  var obj = flag(this, 'object');
1011
1025
  if (flag(this, 'deep')) {
1012
- return this.eql(val);
1026
+ var prevLockSsfi = flag(this, 'lockSsfi');
1027
+ flag(this, 'lockSsfi', true);
1028
+ this.eql(val);
1029
+ flag(this, 'lockSsfi', prevLockSsfi);
1013
1030
  } else {
1014
1031
  this.assert(
1015
1032
  val === obj
@@ -1092,8 +1109,8 @@ module.exports = function (chai, _) {
1092
1109
  * expect(2).to.equal(2); // Recommended
1093
1110
  * expect(2).to.be.above(1); // Not recommended
1094
1111
  *
1095
- * Add `.lengthOf` earlier in the chain to assert that the value of the
1096
- * target's `length` property is greater than the given number `n`.
1112
+ * Add `.lengthOf` earlier in the chain to assert that the target's `length`
1113
+ * or `size` is greater than the given number `n`.
1097
1114
  *
1098
1115
  * expect('foo').to.have.lengthOf(3); // Recommended
1099
1116
  * expect('foo').to.have.lengthOf.above(2); // Not recommended
@@ -1134,12 +1151,13 @@ module.exports = function (chai, _) {
1134
1151
  , ssfi = flag(this, 'ssfi')
1135
1152
  , objType = _.type(obj).toLowerCase()
1136
1153
  , nType = _.type(n).toLowerCase()
1154
+ , errorMessage
1137
1155
  , shouldThrow = true;
1138
1156
 
1139
- if (doLength) {
1157
+ if (doLength && objType !== 'map' && objType !== 'set') {
1140
1158
  new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
1141
1159
  }
1142
-
1160
+
1143
1161
  if (!doLength && (objType === 'date' && nType !== 'date')) {
1144
1162
  errorMessage = msgPrefix + 'the argument to above must be a date';
1145
1163
  } else if (nType !== 'number' && (doLength || objType === 'number')) {
@@ -1156,13 +1174,20 @@ module.exports = function (chai, _) {
1156
1174
  }
1157
1175
 
1158
1176
  if (doLength) {
1159
- var len = obj.length;
1177
+ var descriptor = 'length'
1178
+ , itemsCount;
1179
+ if (objType === 'map' || objType === 'set') {
1180
+ descriptor = 'size';
1181
+ itemsCount = obj.size;
1182
+ } else {
1183
+ itemsCount = obj.length;
1184
+ }
1160
1185
  this.assert(
1161
- len > n
1162
- , 'expected #{this} to have a length above #{exp} but got #{act}'
1163
- , 'expected #{this} to not have a length above #{exp}'
1186
+ itemsCount > n
1187
+ , 'expected #{this} to have a ' + descriptor + ' above #{exp} but got #{act}'
1188
+ , 'expected #{this} to not have a ' + descriptor + ' above #{exp}'
1164
1189
  , n
1165
- , len
1190
+ , itemsCount
1166
1191
  );
1167
1192
  } else {
1168
1193
  this.assert(
@@ -1189,9 +1214,8 @@ module.exports = function (chai, _) {
1189
1214
  * expect(2).to.be.at.least(1); // Not recommended
1190
1215
  * expect(2).to.be.at.least(2); // Not recommended
1191
1216
  *
1192
- * Add `.lengthOf` earlier in the chain to assert that the value of the
1193
- * target's `length` property is greater than or equal to the given number
1194
- * `n`.
1217
+ * Add `.lengthOf` earlier in the chain to assert that the target's `length`
1218
+ * or `size` is greater than or equal to the given number `n`.
1195
1219
  *
1196
1220
  * expect('foo').to.have.lengthOf(3); // Recommended
1197
1221
  * expect('foo').to.have.lengthOf.at.least(2); // Not recommended
@@ -1230,9 +1254,10 @@ module.exports = function (chai, _) {
1230
1254
  , ssfi = flag(this, 'ssfi')
1231
1255
  , objType = _.type(obj).toLowerCase()
1232
1256
  , nType = _.type(n).toLowerCase()
1257
+ , errorMessage
1233
1258
  , shouldThrow = true;
1234
1259
 
1235
- if (doLength) {
1260
+ if (doLength && objType !== 'map' && objType !== 'set') {
1236
1261
  new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
1237
1262
  }
1238
1263
 
@@ -1252,13 +1277,20 @@ module.exports = function (chai, _) {
1252
1277
  }
1253
1278
 
1254
1279
  if (doLength) {
1255
- var len = obj.length;
1280
+ var descriptor = 'length'
1281
+ , itemsCount;
1282
+ if (objType === 'map' || objType === 'set') {
1283
+ descriptor = 'size';
1284
+ itemsCount = obj.size;
1285
+ } else {
1286
+ itemsCount = obj.length;
1287
+ }
1256
1288
  this.assert(
1257
- len >= n
1258
- , 'expected #{this} to have a length at least #{exp} but got #{act}'
1259
- , 'expected #{this} to have a length below #{exp}'
1289
+ itemsCount >= n
1290
+ , 'expected #{this} to have a ' + descriptor + ' at least #{exp} but got #{act}'
1291
+ , 'expected #{this} to have a ' + descriptor + ' below #{exp}'
1260
1292
  , n
1261
- , len
1293
+ , itemsCount
1262
1294
  );
1263
1295
  } else {
1264
1296
  this.assert(
@@ -1283,8 +1315,8 @@ module.exports = function (chai, _) {
1283
1315
  * expect(1).to.equal(1); // Recommended
1284
1316
  * expect(1).to.be.below(2); // Not recommended
1285
1317
  *
1286
- * Add `.lengthOf` earlier in the chain to assert that the value of the
1287
- * target's `length` property is less than the given number `n`.
1318
+ * Add `.lengthOf` earlier in the chain to assert that the target's `length`
1319
+ * or `size` is less than the given number `n`.
1288
1320
  *
1289
1321
  * expect('foo').to.have.lengthOf(3); // Recommended
1290
1322
  * expect('foo').to.have.lengthOf.below(4); // Not recommended
@@ -1325,9 +1357,10 @@ module.exports = function (chai, _) {
1325
1357
  , ssfi = flag(this, 'ssfi')
1326
1358
  , objType = _.type(obj).toLowerCase()
1327
1359
  , nType = _.type(n).toLowerCase()
1360
+ , errorMessage
1328
1361
  , shouldThrow = true;
1329
1362
 
1330
- if (doLength) {
1363
+ if (doLength && objType !== 'map' && objType !== 'set') {
1331
1364
  new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
1332
1365
  }
1333
1366
 
@@ -1347,13 +1380,20 @@ module.exports = function (chai, _) {
1347
1380
  }
1348
1381
 
1349
1382
  if (doLength) {
1350
- var len = obj.length;
1383
+ var descriptor = 'length'
1384
+ , itemsCount;
1385
+ if (objType === 'map' || objType === 'set') {
1386
+ descriptor = 'size';
1387
+ itemsCount = obj.size;
1388
+ } else {
1389
+ itemsCount = obj.length;
1390
+ }
1351
1391
  this.assert(
1352
- len < n
1353
- , 'expected #{this} to have a length below #{exp} but got #{act}'
1354
- , 'expected #{this} to not have a length below #{exp}'
1392
+ itemsCount < n
1393
+ , 'expected #{this} to have a ' + descriptor + ' below #{exp} but got #{act}'
1394
+ , 'expected #{this} to not have a ' + descriptor + ' below #{exp}'
1355
1395
  , n
1356
- , len
1396
+ , itemsCount
1357
1397
  );
1358
1398
  } else {
1359
1399
  this.assert(
@@ -1380,8 +1420,8 @@ module.exports = function (chai, _) {
1380
1420
  * expect(1).to.be.at.most(2); // Not recommended
1381
1421
  * expect(1).to.be.at.most(1); // Not recommended
1382
1422
  *
1383
- * Add `.lengthOf` earlier in the chain to assert that the value of the
1384
- * target's `length` property is less than or equal to the given number `n`.
1423
+ * Add `.lengthOf` earlier in the chain to assert that the target's `length`
1424
+ * or `size` is less than or equal to the given number `n`.
1385
1425
  *
1386
1426
  * expect('foo').to.have.lengthOf(3); // Recommended
1387
1427
  * expect('foo').to.have.lengthOf.at.most(4); // Not recommended
@@ -1420,12 +1460,13 @@ module.exports = function (chai, _) {
1420
1460
  , ssfi = flag(this, 'ssfi')
1421
1461
  , objType = _.type(obj).toLowerCase()
1422
1462
  , nType = _.type(n).toLowerCase()
1463
+ , errorMessage
1423
1464
  , shouldThrow = true;
1424
1465
 
1425
- if (doLength) {
1466
+ if (doLength && objType !== 'map' && objType !== 'set') {
1426
1467
  new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
1427
1468
  }
1428
-
1469
+
1429
1470
  if (!doLength && (objType === 'date' && nType !== 'date')) {
1430
1471
  errorMessage = msgPrefix + 'the argument to most must be a date';
1431
1472
  } else if (nType !== 'number' && (doLength || objType === 'number')) {
@@ -1442,13 +1483,20 @@ module.exports = function (chai, _) {
1442
1483
  }
1443
1484
 
1444
1485
  if (doLength) {
1445
- var len = obj.length;
1486
+ var descriptor = 'length'
1487
+ , itemsCount;
1488
+ if (objType === 'map' || objType === 'set') {
1489
+ descriptor = 'size';
1490
+ itemsCount = obj.size;
1491
+ } else {
1492
+ itemsCount = obj.length;
1493
+ }
1446
1494
  this.assert(
1447
- len <= n
1448
- , 'expected #{this} to have a length at most #{exp} but got #{act}'
1449
- , 'expected #{this} to have a length above #{exp}'
1495
+ itemsCount <= n
1496
+ , 'expected #{this} to have a ' + descriptor + ' at most #{exp} but got #{act}'
1497
+ , 'expected #{this} to have a ' + descriptor + ' above #{exp}'
1450
1498
  , n
1451
- , len
1499
+ , itemsCount
1452
1500
  );
1453
1501
  } else {
1454
1502
  this.assert(
@@ -1476,9 +1524,9 @@ module.exports = function (chai, _) {
1476
1524
  * expect(2).to.be.within(2, 3); // Not recommended
1477
1525
  * expect(2).to.be.within(1, 2); // Not recommended
1478
1526
  *
1479
- * Add `.lengthOf` earlier in the chain to assert that the value of the
1480
- * target's `length` property is greater than or equal to the given number
1481
- * `start`, and less than or equal to the given number `finish`.
1527
+ * Add `.lengthOf` earlier in the chain to assert that the target's `length`
1528
+ * or `size` is greater than or equal to the given number `start`, and less
1529
+ * than or equal to the given number `finish`.
1482
1530
  *
1483
1531
  * expect('foo').to.have.lengthOf(3); // Recommended
1484
1532
  * expect('foo').to.have.lengthOf.within(2, 4); // Not recommended
@@ -1516,12 +1564,13 @@ module.exports = function (chai, _) {
1516
1564
  , objType = _.type(obj).toLowerCase()
1517
1565
  , startType = _.type(start).toLowerCase()
1518
1566
  , finishType = _.type(finish).toLowerCase()
1567
+ , errorMessage
1519
1568
  , shouldThrow = true
1520
1569
  , range = (startType === 'date' && finishType === 'date')
1521
1570
  ? start.toUTCString() + '..' + finish.toUTCString()
1522
1571
  : start + '..' + finish;
1523
1572
 
1524
- if (doLength) {
1573
+ if (doLength && objType !== 'map' && objType !== 'set') {
1525
1574
  new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
1526
1575
  }
1527
1576
 
@@ -1541,11 +1590,18 @@ module.exports = function (chai, _) {
1541
1590
  }
1542
1591
 
1543
1592
  if (doLength) {
1544
- var len = obj.length;
1593
+ var descriptor = 'length'
1594
+ , itemsCount;
1595
+ if (objType === 'map' || objType === 'set') {
1596
+ descriptor = 'size';
1597
+ itemsCount = obj.size;
1598
+ } else {
1599
+ itemsCount = obj.length;
1600
+ }
1545
1601
  this.assert(
1546
- len >= start && len <= finish
1547
- , 'expected #{this} to have a length within ' + range
1548
- , 'expected #{this} to not have a length within ' + range
1602
+ itemsCount >= start && itemsCount <= finish
1603
+ , 'expected #{this} to have a ' + descriptor + ' within ' + range
1604
+ , 'expected #{this} to not have a ' + descriptor + ' within ' + range
1549
1605
  );
1550
1606
  } else {
1551
1607
  this.assert(
@@ -1601,28 +1657,25 @@ module.exports = function (chai, _) {
1601
1657
  var target = flag(this, 'object')
1602
1658
  var ssfi = flag(this, 'ssfi');
1603
1659
  var flagMsg = flag(this, 'message');
1604
- var validInstanceOfTarget = constructor === Object(constructor) && (
1605
- typeof constructor === 'function' ||
1606
- (typeof Symbol !== 'undefined' &&
1607
- typeof Symbol.hasInstance !== 'undefined' &&
1608
- Symbol.hasInstance in constructor)
1609
- );
1610
1660
 
1611
- if (!validInstanceOfTarget) {
1612
- flagMsg = flagMsg ? flagMsg + ': ' : '';
1613
- var constructorType = constructor === null ? 'null' : typeof constructor;
1614
- throw new AssertionError(
1615
- flagMsg + 'The instanceof assertion needs a constructor but ' + constructorType + ' was given.',
1616
- undefined,
1617
- ssfi
1618
- );
1661
+ try {
1662
+ var isInstanceOf = target instanceof constructor;
1663
+ } catch (err) {
1664
+ if (err instanceof TypeError) {
1665
+ flagMsg = flagMsg ? flagMsg + ': ' : '';
1666
+ throw new AssertionError(
1667
+ flagMsg + 'The instanceof assertion needs a constructor but '
1668
+ + _.type(constructor) + ' was given.',
1669
+ undefined,
1670
+ ssfi
1671
+ );
1672
+ }
1673
+ throw err;
1619
1674
  }
1620
1675
 
1621
- var isInstanceOf = target instanceof constructor
1622
-
1623
1676
  var name = _.getName(constructor);
1624
1677
  if (name === null) {
1625
- name = 'an unnamed constructor';
1678
+ name = 'an unnamed constructor';
1626
1679
  }
1627
1680
 
1628
1681
  this.assert(
@@ -1664,7 +1717,8 @@ module.exports = function (chai, _) {
1664
1717
  *
1665
1718
  * expect({a: 1}).to.have.own.property('a');
1666
1719
  * expect({a: 1}).to.have.own.property('a', 1);
1667
- * expect({a: 1}).to.have.property('b').but.not.own.property('b');
1720
+ * expect({a: 1}).to.have.property('b');
1721
+ * expect({a: 1}).to.not.have.own.property('b');
1668
1722
  *
1669
1723
  * `.deep` and `.own` can be combined.
1670
1724
  *
@@ -1691,7 +1745,7 @@ module.exports = function (chai, _) {
1691
1745
  * Add `.not` earlier in the chain to negate `.property`.
1692
1746
  *
1693
1747
  * expect({a: 1}).to.not.have.property('b');
1694
- *
1748
+ *
1695
1749
  * However, it's dangerous to negate `.property` when providing `val`. The
1696
1750
  * problem is that it creates uncertain expectations by asserting that the
1697
1751
  * target either doesn't have a property with the given key `name`, or that it
@@ -1729,7 +1783,7 @@ module.exports = function (chai, _) {
1729
1783
  *
1730
1784
  * // Not recommended
1731
1785
  * expect({a: 1}).to.have.property('b', undefined, 'nooo why fail??');
1732
- *
1786
+ *
1733
1787
  * The above assertion isn't the same thing as not providing `val`. Instead,
1734
1788
  * it's asserting that the target object has a `b` property that's equal to
1735
1789
  * `undefined`.
@@ -1753,10 +1807,30 @@ module.exports = function (chai, _) {
1753
1807
  , isOwn = flag(this, 'own')
1754
1808
  , flagMsg = flag(this, 'message')
1755
1809
  , obj = flag(this, 'object')
1756
- , ssfi = flag(this, 'ssfi');
1810
+ , ssfi = flag(this, 'ssfi')
1811
+ , nameType = typeof name;
1812
+
1813
+ flagMsg = flagMsg ? flagMsg + ': ' : '';
1814
+
1815
+ if (isNested) {
1816
+ if (nameType !== 'string') {
1817
+ throw new AssertionError(
1818
+ flagMsg + 'the argument to property must be a string when using nested syntax',
1819
+ undefined,
1820
+ ssfi
1821
+ );
1822
+ }
1823
+ } else {
1824
+ if (nameType !== 'string' && nameType !== 'number' && nameType !== 'symbol') {
1825
+ throw new AssertionError(
1826
+ flagMsg + 'the argument to property must be a string, number, or symbol',
1827
+ undefined,
1828
+ ssfi
1829
+ );
1830
+ }
1831
+ }
1757
1832
 
1758
1833
  if (isNested && isOwn) {
1759
- flagMsg = flagMsg ? flagMsg + ': ' : '';
1760
1834
  throw new AssertionError(
1761
1835
  flagMsg + 'The "nested" and "own" flags cannot be combined.',
1762
1836
  undefined,
@@ -1765,7 +1839,6 @@ module.exports = function (chai, _) {
1765
1839
  }
1766
1840
 
1767
1841
  if (obj === null || obj === undefined) {
1768
- flagMsg = flagMsg ? flagMsg + ': ' : '';
1769
1842
  throw new AssertionError(
1770
1843
  flagMsg + 'Target cannot be null or undefined.',
1771
1844
  undefined,
@@ -1848,12 +1921,12 @@ module.exports = function (chai, _) {
1848
1921
  * Add `.not` earlier in the chain to negate `.ownPropertyDescriptor`.
1849
1922
  *
1850
1923
  * expect({a: 1}).to.not.have.ownPropertyDescriptor('b');
1851
- *
1924
+ *
1852
1925
  * However, it's dangerous to negate `.ownPropertyDescriptor` when providing
1853
1926
  * a `descriptor`. The problem is that it creates uncertain expectations by
1854
1927
  * asserting that the target either doesn't have a property descriptor with
1855
1928
  * the given key `name`, or that it does have a property descriptor with the
1856
- * given key `name` but its not deeply equal to the given `descriptor`. It's
1929
+ * given key `name` but it’s not deeply equal to the given `descriptor`. It's
1857
1930
  * often best to identify the exact output that's expected, and then write an
1858
1931
  * assertion that only accepts that exact output.
1859
1932
  *
@@ -1919,7 +1992,7 @@ module.exports = function (chai, _) {
1919
1992
  * writable: true,
1920
1993
  * value: 2,
1921
1994
  * });
1922
- *
1995
+ *
1923
1996
  * // Recommended
1924
1997
  * expect({a: 1}, 'nooo why fail??').to.have.ownPropertyDescriptor('b');
1925
1998
  *
@@ -1976,11 +2049,13 @@ module.exports = function (chai, _) {
1976
2049
  /**
1977
2050
  * ### .lengthOf(n[, msg])
1978
2051
  *
1979
- * Asserts that the target's `length` property is equal to the given number
2052
+ * Asserts that the target's `length` or `size` is equal to the given number
1980
2053
  * `n`.
1981
2054
  *
1982
2055
  * expect([1, 2, 3]).to.have.lengthOf(3);
1983
2056
  * expect('foo').to.have.lengthOf(3);
2057
+ * expect(new Set([1, 2, 3])).to.have.lengthOf(3);
2058
+ * expect(new Map([['a', 1], ['b', 2], ['c', 3]])).to.have.lengthOf(3);
1984
2059
  *
1985
2060
  * Add `.not` earlier in the chain to negate `.lengthOf`. However, it's often
1986
2061
  * best to assert that the target's `length` property is equal to its expected
@@ -2036,17 +2111,29 @@ module.exports = function (chai, _) {
2036
2111
  function assertLength (n, msg) {
2037
2112
  if (msg) flag(this, 'message', msg);
2038
2113
  var obj = flag(this, 'object')
2114
+ , objType = _.type(obj).toLowerCase()
2039
2115
  , flagMsg = flag(this, 'message')
2040
- , ssfi = flag(this, 'ssfi');
2041
- new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
2042
- var len = obj.length;
2116
+ , ssfi = flag(this, 'ssfi')
2117
+ , descriptor = 'length'
2118
+ , itemsCount;
2119
+
2120
+ switch (objType) {
2121
+ case 'map':
2122
+ case 'set':
2123
+ descriptor = 'size';
2124
+ itemsCount = obj.size;
2125
+ break;
2126
+ default:
2127
+ new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
2128
+ itemsCount = obj.length;
2129
+ }
2043
2130
 
2044
2131
  this.assert(
2045
- len == n
2046
- , 'expected #{this} to have a length of #{exp} but got #{act}'
2047
- , 'expected #{this} to not have a length of #{act}'
2132
+ itemsCount == n
2133
+ , 'expected #{this} to have a ' + descriptor + ' of #{exp} but got #{act}'
2134
+ , 'expected #{this} to not have a ' + descriptor + ' of #{act}'
2048
2135
  , n
2049
- , len
2136
+ , itemsCount
2050
2137
  );
2051
2138
  }
2052
2139
 
@@ -2108,8 +2195,8 @@ module.exports = function (chai, _) {
2108
2195
  * message to show when the assertion fails. The message can also be given as
2109
2196
  * the second argument to `expect`.
2110
2197
  *
2111
- * expect('foobar').to.have.string(/taco/, 'nooo why fail??');
2112
- * expect('foobar', 'nooo why fail??').to.have.string(/taco/);
2198
+ * expect('foobar').to.have.string('taco', 'nooo why fail??');
2199
+ * expect('foobar', 'nooo why fail??').to.have.string('taco');
2113
2200
  *
2114
2201
  * @name string
2115
2202
  * @param {String} str
@@ -2136,7 +2223,7 @@ module.exports = function (chai, _) {
2136
2223
  * ### .keys(key1[, key2[, ...]])
2137
2224
  *
2138
2225
  * Asserts that the target object, array, map, or set has the given keys. Only
2139
- * the target's own inherited properties are included in the search.
2226
+ * the target's own inherited properties are included in the search.
2140
2227
  *
2141
2228
  * When the target is an object or array, keys can be provided as one or more
2142
2229
  * string arguments, a single array argument, or a single object argument. In
@@ -2244,6 +2331,7 @@ module.exports = function (chai, _) {
2244
2331
  , isDeep = flag(this, 'deep')
2245
2332
  , str
2246
2333
  , deepStr = ''
2334
+ , actual
2247
2335
  , ok = true
2248
2336
  , flagMsg = flag(this, 'message');
2249
2337
 
@@ -2260,7 +2348,6 @@ module.exports = function (chai, _) {
2260
2348
  if (keysType !== 'Array') {
2261
2349
  keys = Array.prototype.slice.call(arguments);
2262
2350
  }
2263
-
2264
2351
  } else {
2265
2352
  actual = _.getOwnEnumerableProperties(obj);
2266
2353
 
@@ -2293,8 +2380,7 @@ module.exports = function (chai, _) {
2293
2380
  var len = keys.length
2294
2381
  , any = flag(this, 'any')
2295
2382
  , all = flag(this, 'all')
2296
- , expected = keys
2297
- , actual;
2383
+ , expected = keys;
2298
2384
 
2299
2385
  if (!any && !all) {
2300
2386
  all = true;
@@ -2371,7 +2457,7 @@ module.exports = function (chai, _) {
2371
2457
  *
2372
2458
  * When no arguments are provided, `.throw` invokes the target function and
2373
2459
  * asserts that an error is thrown.
2374
- *
2460
+ *
2375
2461
  * var badFn = function () { throw new TypeError('Illegal salmon!'); };
2376
2462
  *
2377
2463
  * expect(badFn).to.throw();
@@ -2423,11 +2509,11 @@ module.exports = function (chai, _) {
2423
2509
  * expect(badFn).to.throw(err, /salmon/);
2424
2510
  *
2425
2511
  * Add `.not` earlier in the chain to negate `.throw`.
2426
- *
2512
+ *
2427
2513
  * var goodFn = function () {};
2428
2514
  *
2429
2515
  * expect(goodFn).to.not.throw();
2430
- *
2516
+ *
2431
2517
  * However, it's dangerous to negate `.throw` when providing any arguments.
2432
2518
  * The problem is that it creates uncertain expectations by asserting that the
2433
2519
  * target either doesn't throw an error, or that it throws an error but of a
@@ -2775,7 +2861,7 @@ module.exports = function (chai, _) {
2775
2861
  * first argument, and asserts that the value returned is truthy.
2776
2862
  *
2777
2863
  * expect(1).to.satisfy(function(num) {
2778
- * return num > 0;
2864
+ * return num > 0;
2779
2865
  * });
2780
2866
  *
2781
2867
  * Add `.not` earlier in the chain to negate `.satisfy`.
@@ -2869,8 +2955,9 @@ module.exports = function (chai, _) {
2869
2955
  new Assertion(obj, flagMsg, ssfi, true).is.a('number');
2870
2956
  if (typeof expected !== 'number' || typeof delta !== 'number') {
2871
2957
  flagMsg = flagMsg ? flagMsg + ': ' : '';
2958
+ var deltaMessage = delta === undefined ? ", and a delta is required" : "";
2872
2959
  throw new AssertionError(
2873
- flagMsg + 'the arguments to closeTo or approximately must be numbers',
2960
+ flagMsg + 'the arguments to closeTo or approximately must be numbers' + deltaMessage,
2874
2961
  undefined,
2875
2962
  ssfi
2876
2963
  );
@@ -2996,7 +3083,7 @@ module.exports = function (chai, _) {
2996
3083
  var contains = flag(this, 'contains');
2997
3084
  var ordered = flag(this, 'ordered');
2998
3085
 
2999
- var subject, failMsg, failNegateMsg, lengthCheck;
3086
+ var subject, failMsg, failNegateMsg;
3000
3087
 
3001
3088
  if (contains) {
3002
3089
  subject = ordered ? 'an ordered superset' : 'a superset';
@@ -3036,6 +3123,14 @@ module.exports = function (chai, _) {
3036
3123
  * expect(1).to.equal(1); // Recommended
3037
3124
  * expect(1).to.not.be.oneOf([2, 3, 4]); // Not recommended
3038
3125
  *
3126
+ * It can also be chained with `.contain` or `.include`, which will work with
3127
+ * both arrays and strings:
3128
+ *
3129
+ * expect('Today is sunny').to.contain.oneOf(['sunny', 'cloudy'])
3130
+ * expect('Today is rainy').to.not.contain.oneOf(['sunny', 'cloudy'])
3131
+ * expect([1,2,3]).to.contain.oneOf([3,4,5])
3132
+ * expect([1,2,3]).to.not.contain.oneOf([4,5,6])
3133
+ *
3039
3134
  * `.oneOf` accepts an optional `msg` argument which is a custom error message
3040
3135
  * to show when the assertion fails. The message can also be given as the
3041
3136
  * second argument to `expect`.
@@ -3054,21 +3149,31 @@ module.exports = function (chai, _) {
3054
3149
  if (msg) flag(this, 'message', msg);
3055
3150
  var expected = flag(this, 'object')
3056
3151
  , flagMsg = flag(this, 'message')
3057
- , ssfi = flag(this, 'ssfi');
3152
+ , ssfi = flag(this, 'ssfi')
3153
+ , contains = flag(this, 'contains');
3058
3154
  new Assertion(list, flagMsg, ssfi, true).to.be.an('array');
3059
3155
 
3060
- this.assert(
3156
+ if (contains) {
3157
+ this.assert(
3158
+ list.some(function(possibility) { return expected.indexOf(possibility) > -1 })
3159
+ , 'expected #{this} to contain one of #{exp}'
3160
+ , 'expected #{this} to not contain one of #{exp}'
3161
+ , list
3162
+ , expected
3163
+ );
3164
+ } else {
3165
+ this.assert(
3061
3166
  list.indexOf(expected) > -1
3062
- , 'expected #{this} to be one of #{exp}'
3063
- , 'expected #{this} to not be one of #{exp}'
3064
- , list
3065
- , expected
3066
- );
3167
+ , 'expected #{this} to be one of #{exp}'
3168
+ , 'expected #{this} to not be one of #{exp}'
3169
+ , list
3170
+ , expected
3171
+ );
3172
+ }
3067
3173
  }
3068
3174
 
3069
3175
  Assertion.addMethod('oneOf', oneOf);
3070
3176
 
3071
-
3072
3177
  /**
3073
3178
  * ### .change(subject[, prop[, msg]])
3074
3179
  *
@@ -3244,7 +3349,7 @@ module.exports = function (chai, _) {
3244
3349
  *
3245
3350
  * expect(subtractTwo).to.decrease(myObj, 'val').by(2); // Recommended
3246
3351
  * expect(subtractTwo).to.not.increase(myObj, 'val'); // Not recommended
3247
- *
3352
+ *
3248
3353
  * When the subject is expected to stay the same, it's often best to assert
3249
3354
  * exactly that.
3250
3355
  *
@@ -3341,7 +3446,7 @@ module.exports = function (chai, _) {
3341
3446
  *
3342
3447
  * When two arguments are provided, `.decrease` asserts that the value of the
3343
3448
  * given object `subject`'s `prop` property is lesser after invoking the
3344
- * target function compared to beforehand.
3449
+ * target function compared to beforehand.
3345
3450
  *
3346
3451
  * var myObj = {val: 1}
3347
3452
  * , subtractTwo = function () { myObj.val -= 2; };
@@ -3363,7 +3468,7 @@ module.exports = function (chai, _) {
3363
3468
  *
3364
3469
  * expect(addTwo).to.increase(myObj, 'val').by(2); // Recommended
3365
3470
  * expect(addTwo).to.not.decrease(myObj, 'val'); // Not recommended
3366
- *
3471
+ *
3367
3472
  * When the subject is expected to stay the same, it's often best to assert
3368
3473
  * exactly that.
3369
3474
  *
@@ -3716,7 +3821,7 @@ module.exports = function (chai, _) {
3716
3821
  var obj = flag(this, 'object');
3717
3822
 
3718
3823
  this.assert(
3719
- typeof obj === "number" && isFinite(obj)
3824
+ typeof obj === 'number' && isFinite(obj)
3720
3825
  , 'expected #{this} to be a finite number'
3721
3826
  , 'expected #{this} to not be a finite number'
3722
3827
  );