chai 4.0.0-canary.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.
@@ -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
 
@@ -1012,6 +1012,197 @@ module.exports = function (chai, util) {
1012
1012
  new Assertion(exp, msg, assert.notDeepInclude, true).not.deep.include(inc);
1013
1013
  };
1014
1014
 
1015
+ /**
1016
+ * ### .nestedInclude(haystack, needle, [message])
1017
+ *
1018
+ * Asserts that 'haystack' includes 'needle'.
1019
+ * Can be used to assert the inclusion of a subset of properties in an
1020
+ * object.
1021
+ * Enables the use of dot- and bracket-notation for referencing nested
1022
+ * properties.
1023
+ * '[]' and '.' in property names can be escaped using double backslashes.
1024
+ *
1025
+ * assert.nestedInclude({'.a': {'b': 'x'}}, {'\\.a.[b]': 'x'});
1026
+ * assert.nestedInclude({'a': {'[b]': 'x'}}, {'a.\\[b\\]': 'x'});
1027
+ *
1028
+ * @name nestedInclude
1029
+ * @param {Object} haystack
1030
+ * @param {Object} needle
1031
+ * @param {String} message
1032
+ * @namespace Assert
1033
+ * @api public
1034
+ */
1035
+
1036
+ assert.nestedInclude = function (exp, inc, msg) {
1037
+ new Assertion(exp, msg, assert.nestedInclude, true).nested.include(inc);
1038
+ };
1039
+
1040
+ /**
1041
+ * ### .notNestedInclude(haystack, needle, [message])
1042
+ *
1043
+ * Asserts that 'haystack' does not include 'needle'.
1044
+ * Can be used to assert the absence of a subset of properties in an
1045
+ * object.
1046
+ * Enables the use of dot- and bracket-notation for referencing nested
1047
+ * properties.
1048
+ * '[]' and '.' in property names can be escaped using double backslashes.
1049
+ *
1050
+ * assert.notNestedInclude({'.a': {'b': 'x'}}, {'\\.a.b': 'y'});
1051
+ * assert.notNestedInclude({'a': {'[b]': 'x'}}, {'a.\\[b\\]': 'y'});
1052
+ *
1053
+ * @name notNestedInclude
1054
+ * @param {Object} haystack
1055
+ * @param {Object} needle
1056
+ * @param {String} message
1057
+ * @namespace Assert
1058
+ * @api public
1059
+ */
1060
+
1061
+ assert.notNestedInclude = function (exp, inc, msg) {
1062
+ new Assertion(exp, msg, assert.notNestedInclude, true)
1063
+ .not.nested.include(inc);
1064
+ };
1065
+
1066
+ /**
1067
+ * ### .deepNestedInclude(haystack, needle, [message])
1068
+ *
1069
+ * Asserts that 'haystack' includes 'needle'.
1070
+ * Can be used to assert the inclusion of a subset of properties in an
1071
+ * object while checking for deep equality.
1072
+ * Enables the use of dot- and bracket-notation for referencing nested
1073
+ * properties.
1074
+ * '[]' and '.' in property names can be escaped using double backslashes.
1075
+ *
1076
+ * assert.deepNestedInclude({a: {b: [{x: 1}]}}, {'a.b[0]': {x: 1}});
1077
+ * assert.deepNestedInclude({'.a': {'[b]': {x: 1}}}, {'\\.a.\\[b\\]': {x: 1}});
1078
+ *
1079
+ * @name deepNestedInclude
1080
+ * @param {Object} haystack
1081
+ * @param {Object} needle
1082
+ * @param {String} message
1083
+ * @namespace Assert
1084
+ * @api public
1085
+ */
1086
+
1087
+ assert.deepNestedInclude = function(exp, inc, msg) {
1088
+ new Assertion(exp, msg, assert.deepNestedInclude, true)
1089
+ .deep.nested.include(inc);
1090
+ };
1091
+
1092
+ /**
1093
+ * ### .notDeepNestedInclude(haystack, needle, [message])
1094
+ *
1095
+ * Asserts that 'haystack' does not include 'needle'.
1096
+ * Can be used to assert the absence of a subset of properties in an
1097
+ * object while checking for deep equality.
1098
+ * Enables the use of dot- and bracket-notation for referencing nested
1099
+ * properties.
1100
+ * '[]' and '.' in property names can be escaped using double backslashes.
1101
+ *
1102
+ * assert.notDeepNestedInclude({a: {b: [{x: 1}]}}, {'a.b[0]': {y: 1}})
1103
+ * assert.notDeepNestedInclude({'.a': {'[b]': {x: 1}}}, {'\\.a.\\[b\\]': {y: 2}});
1104
+ *
1105
+ * @name notDeepNestedInclude
1106
+ * @param {Object} haystack
1107
+ * @param {Object} needle
1108
+ * @param {String} message
1109
+ * @namespace Assert
1110
+ * @api public
1111
+ */
1112
+
1113
+ assert.notDeepNestedInclude = function(exp, inc, msg) {
1114
+ new Assertion(exp, msg, assert.notDeepNestedInclude, true)
1115
+ .not.deep.nested.include(inc);
1116
+ };
1117
+
1118
+ /**
1119
+ * ### .ownInclude(haystack, needle, [message])
1120
+ *
1121
+ * Asserts that 'haystack' includes 'needle'.
1122
+ * Can be used to assert the inclusion of a subset of properties in an
1123
+ * object while ignoring inherited properties.
1124
+ *
1125
+ * assert.ownInclude({ a: 1 }, { a: 1 });
1126
+ *
1127
+ * @name ownInclude
1128
+ * @param {Object} haystack
1129
+ * @param {Object} needle
1130
+ * @param {String} message
1131
+ * @namespace Assert
1132
+ * @api public
1133
+ */
1134
+
1135
+ assert.ownInclude = function(exp, inc, msg) {
1136
+ new Assertion(exp, msg, assert.ownInclude, true).own.include(inc);
1137
+ };
1138
+
1139
+ /**
1140
+ * ### .notOwnInclude(haystack, needle, [message])
1141
+ *
1142
+ * Asserts that 'haystack' includes 'needle'.
1143
+ * Can be used to assert the absence of a subset of properties in an
1144
+ * object while ignoring inherited properties.
1145
+ *
1146
+ * Object.prototype.b = 2;
1147
+ *
1148
+ * assert.notOwnInclude({ a: 1 }, { b: 2 });
1149
+ *
1150
+ * @name notOwnInclude
1151
+ * @param {Object} haystack
1152
+ * @param {Object} needle
1153
+ * @param {String} message
1154
+ * @namespace Assert
1155
+ * @api public
1156
+ */
1157
+
1158
+ assert.notOwnInclude = function(exp, inc, msg) {
1159
+ new Assertion(exp, msg, assert.notOwnInclude, true).not.own.include(inc);
1160
+ };
1161
+
1162
+ /**
1163
+ * ### .deepOwnInclude(haystack, needle, [message])
1164
+ *
1165
+ * Asserts that 'haystack' includes 'needle'.
1166
+ * Can be used to assert the inclusion of a subset of properties in an
1167
+ * object while ignoring inherited properties and checking for deep equality.
1168
+ *
1169
+ * assert.deepOwnInclude({a: {b: 2}}, {a: {b: 2}});
1170
+ *
1171
+ * @name deepOwnInclude
1172
+ * @param {Object} haystack
1173
+ * @param {Object} needle
1174
+ * @param {String} message
1175
+ * @namespace Assert
1176
+ * @api public
1177
+ */
1178
+
1179
+ assert.deepOwnInclude = function(exp, inc, msg) {
1180
+ new Assertion(exp, msg, assert.deepOwnInclude, true)
1181
+ .deep.own.include(inc);
1182
+ };
1183
+
1184
+ /**
1185
+ * ### .notDeepOwnInclude(haystack, needle, [message])
1186
+ *
1187
+ * Asserts that 'haystack' includes 'needle'.
1188
+ * Can be used to assert the absence of a subset of properties in an
1189
+ * object while ignoring inherited properties and checking for deep equality.
1190
+ *
1191
+ * assert.notDeepOwnInclude({a: {b: 2}}, {a: {c: 3}});
1192
+ *
1193
+ * @name notDeepOwnInclude
1194
+ * @param {Object} haystack
1195
+ * @param {Object} needle
1196
+ * @param {String} message
1197
+ * @namespace Assert
1198
+ * @api public
1199
+ */
1200
+
1201
+ assert.notDeepOwnInclude = function(exp, inc, msg) {
1202
+ new Assertion(exp, msg, assert.notDeepOwnInclude, true)
1203
+ .not.deep.own.include(inc);
1204
+ };
1205
+
1015
1206
  /**
1016
1207
  * ### .match(value, regexp, [message])
1017
1208
  *
@@ -25,7 +25,16 @@ var canSetPrototype = typeof Object.setPrototypeOf === 'function';
25
25
  // However, some of functions' own props are not configurable and should be skipped.
26
26
  var testFn = function() {};
27
27
  var excludeNames = Object.getOwnPropertyNames(testFn).filter(function(name) {
28
- return !Object.getOwnPropertyDescriptor(testFn, name).configurable;
28
+ var propDesc = Object.getOwnPropertyDescriptor(testFn, name);
29
+
30
+ // Note: PhantomJS 1.x includes `callee` as one of `testFn`'s own properties,
31
+ // but then returns `undefined` as the property descriptor for `callee`. As a
32
+ // workaround, we perform an otherwise unnecessary type-check for `propDesc`,
33
+ // and then filter it out if it's not an object as it should be.
34
+ if (typeof propDesc !== 'object')
35
+ return true;
36
+
37
+ return !propDesc.configurable;
29
38
  });
30
39
 
31
40
  // Cache `Function` properties