contain.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. /*
  2. * Should
  3. * Copyright(c) 2010-2014 TJ Holowaychuk <tj@vision-media.ca>
  4. * MIT Licensed
  5. */
  6. var util = require('../util');
  7. var eql = require('should-equal');
  8. module.exports = function(should, Assertion) {
  9. var i = should.format;
  10. /**
  11. * Assert that given object contain something that equal to `other`. It uses `should-equal` for equality checks.
  12. * If given object is array it search that one of elements was equal to `other`.
  13. * If given object is string it checks if `other` is a substring - expected that `other` is a string.
  14. * If given object is Object it checks that `other` is a subobject - expected that `other` is a object.
  15. *
  16. * @name containEql
  17. * @memberOf Assertion
  18. * @category assertion contain
  19. * @param {*} other Nested object
  20. * @example
  21. *
  22. * [1, 2, 3].should.containEql(1);
  23. * [{ a: 1 }, 'a', 10].should.containEql({ a: 1 });
  24. *
  25. * 'abc'.should.containEql('b');
  26. * 'ab1c'.should.containEql(1);
  27. *
  28. * ({ a: 10, c: { d: 10 }}).should.containEql({ a: 10 });
  29. * ({ a: 10, c: { d: 10 }}).should.containEql({ c: { d: 10 }});
  30. * ({ a: 10, c: { d: 10 }}).should.containEql({ b: 10 });
  31. * // throws AssertionError: expected { a: 10, c: { d: 10 } } to contain { b: 10 }
  32. * // expected { a: 10, c: { d: 10 } } to have property b
  33. */
  34. Assertion.add('containEql', function(other) {
  35. this.params = {operator: 'to contain ' + i(other)};
  36. this.is.not.null().and.not.undefined();
  37. var obj = this.obj;
  38. if(typeof obj == 'string') {
  39. this.assert(obj.indexOf(String(other)) >= 0);
  40. } else if(util.isIndexable(obj)) {
  41. this.assert(util.some(obj, function(v) {
  42. return eql(v, other).result;
  43. }));
  44. } else {
  45. this.have.properties(other);
  46. }
  47. });
  48. /**
  49. * Assert that given object is contain equally structured object on the same depth level.
  50. * If given object is an array and `other` is an array it checks that the eql elements is going in the same sequence in given array (recursive)
  51. * If given object is an object it checks that the same keys contain deep equal values (recursive)
  52. * On other cases it try to check with `.eql`
  53. *
  54. * @name containDeepOrdered
  55. * @memberOf Assertion
  56. * @category assertion contain
  57. * @param {*} other Nested object
  58. * @example
  59. *
  60. * [ 1, 2, 3].should.containDeepOrdered([1, 2]);
  61. * [ 1, 2, [ 1, 2, 3 ]].should.containDeepOrdered([ 1, [ 2, 3 ]]);
  62. *
  63. * ({ a: 10, b: { c: 10, d: [1, 2, 3] }}).should.containDeepOrdered({a: 10});
  64. * ({ a: 10, b: { c: 10, d: [1, 2, 3] }}).should.containDeepOrdered({b: {c: 10}});
  65. * ({ a: 10, b: { c: 10, d: [1, 2, 3] }}).should.containDeepOrdered({b: {d: [1, 3]}});
  66. */
  67. Assertion.add('containDeepOrdered', function(other) {
  68. this.params = {operator: 'to contain ' + i(other)};
  69. var obj = this.obj;
  70. if(typeof obj == 'string') {// expect other to be string
  71. this.is.equal(String(other));
  72. } else if(util.isIndexable(obj) && util.isIndexable(other)) {
  73. for(var objIdx = 0, otherIdx = 0, objLength = util.length(obj), otherLength = util.length(other); objIdx < objLength && otherIdx < otherLength; objIdx++) {
  74. try {
  75. should(obj[objIdx]).containDeepOrdered(other[otherIdx]);
  76. otherIdx++;
  77. } catch(e) {
  78. if(e instanceof should.AssertionError) {
  79. continue;
  80. }
  81. throw e;
  82. }
  83. }
  84. this.assert(otherIdx === otherLength);
  85. } else if(obj != null && other != null && typeof obj == 'object' && typeof other == 'object') {// object contains object case
  86. util.forEach(other, function(value, key) {
  87. should(obj[key]).containDeepOrdered(value);
  88. });
  89. // if both objects is empty means we finish traversing - and we need to compare for hidden values
  90. if(util.isEmptyObject(other)) {
  91. this.eql(other);
  92. }
  93. } else {
  94. this.eql(other);
  95. }
  96. });
  97. /**
  98. * The same like `Assertion#containDeepOrdered` but all checks on arrays without order.
  99. *
  100. * @name containDeep
  101. * @memberOf Assertion
  102. * @category assertion contain
  103. * @param {*} other Nested object
  104. * @example
  105. *
  106. * [ 1, 2, 3].should.containDeep([2, 1]);
  107. * [ 1, 2, [ 1, 2, 3 ]].should.containDeep([ 1, [ 3, 1 ]]);
  108. */
  109. Assertion.add('containDeep', function(other) {
  110. this.params = {operator: 'to contain ' + i(other)};
  111. var obj = this.obj;
  112. if(typeof obj == 'string') {// expect other to be string
  113. this.is.equal(String(other));
  114. } else if(util.isIndexable(obj) && util.isIndexable(other)) {
  115. var usedKeys = {};
  116. util.forEach(other, function(otherItem) {
  117. this.assert(util.some(obj, function(item, index) {
  118. if(index in usedKeys) return false;
  119. try {
  120. should(item).containDeep(otherItem);
  121. usedKeys[index] = true;
  122. return true;
  123. } catch(e) {
  124. if(e instanceof should.AssertionError) {
  125. return false;
  126. }
  127. throw e;
  128. }
  129. }));
  130. }, this);
  131. } else if(obj != null && other != null && typeof obj == 'object' && typeof other == 'object') {// object contains object case
  132. util.forEach(other, function(value, key) {
  133. should(obj[key]).containDeep(value);
  134. });
  135. // if both objects is empty means we finish traversing - and we need to compare for hidden values
  136. if(util.isEmptyObject(other)) {
  137. this.eql(other);
  138. }
  139. } else {
  140. this.eql(other);
  141. }
  142. });
  143. };