utils.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true
  4. });
  5. exports.emptyObject = emptyObject;
  6. exports.isOneline = exports.isError = exports.partition = exports.sparseArrayEquality = exports.typeEquality = exports.subsetEquality = exports.iterableEquality = exports.getObjectSubset = exports.getPath = exports.hasOwnProperty = void 0;
  7. var _jestGetType = require('jest-get-type');
  8. var _jasmineUtils = require('./jasmineUtils');
  9. var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol;
  10. // Return whether object instance inherits getter from its class.
  11. const hasGetterFromConstructor = (object, key) => {
  12. const constructor = object.constructor;
  13. if (constructor === Object) {
  14. // A literal object has Object as constructor.
  15. // Therefore, it cannot inherit application-specific getters.
  16. // Furthermore, Object has __proto__ getter which is not relevant.
  17. // Array, Boolean, Number, String constructors don’t have any getters.
  18. return false;
  19. }
  20. if (typeof constructor !== 'function') {
  21. // Object.create(null) constructs object with no constructor nor prototype.
  22. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Custom_and_Null_objects
  23. return false;
  24. }
  25. const descriptor = Object.getOwnPropertyDescriptor(
  26. constructor.prototype,
  27. key
  28. );
  29. return descriptor !== undefined && typeof descriptor.get === 'function';
  30. };
  31. const hasOwnProperty = (object, key) =>
  32. Object.prototype.hasOwnProperty.call(object, key) ||
  33. hasGetterFromConstructor(object, key);
  34. exports.hasOwnProperty = hasOwnProperty;
  35. const getPath = (object, propertyPath) => {
  36. if (!Array.isArray(propertyPath)) {
  37. propertyPath = propertyPath.split('.');
  38. }
  39. if (propertyPath.length) {
  40. const lastProp = propertyPath.length === 1;
  41. const prop = propertyPath[0];
  42. const newObject = object[prop];
  43. if (!lastProp && (newObject === null || newObject === undefined)) {
  44. // This is not the last prop in the chain. If we keep recursing it will
  45. // hit a `can't access property X of undefined | null`. At this point we
  46. // know that the chain has broken and we can return right away.
  47. return {
  48. hasEndProp: false,
  49. lastTraversedObject: object,
  50. traversedPath: []
  51. };
  52. }
  53. const result = getPath(newObject, propertyPath.slice(1));
  54. if (result.lastTraversedObject === null) {
  55. result.lastTraversedObject = object;
  56. }
  57. result.traversedPath.unshift(prop);
  58. if (lastProp) {
  59. // Does object have the property with an undefined value?
  60. // Although primitive values support bracket notation (above)
  61. // they would throw TypeError for in operator (below).
  62. result.hasEndProp =
  63. newObject !== undefined ||
  64. (!(0, _jestGetType.isPrimitive)(object) && prop in object);
  65. if (!result.hasEndProp) {
  66. result.traversedPath.shift();
  67. }
  68. }
  69. return result;
  70. }
  71. return {
  72. lastTraversedObject: null,
  73. traversedPath: [],
  74. value: object
  75. };
  76. }; // Strip properties from object that are not present in the subset. Useful for
  77. // printing the diff for toMatchObject() without adding unrelated noise.
  78. exports.getPath = getPath;
  79. const getObjectSubset = (object, subset, seenReferences = new WeakMap()) => {
  80. if (Array.isArray(object)) {
  81. if (Array.isArray(subset) && subset.length === object.length) {
  82. return subset.map((sub, i) => getObjectSubset(object[i], sub));
  83. }
  84. } else if (object instanceof Date) {
  85. return object;
  86. } else if (isObject(object) && isObject(subset)) {
  87. const trimmed = {};
  88. seenReferences.set(object, trimmed);
  89. Object.keys(object)
  90. .filter(key => hasOwnProperty(subset, key))
  91. .forEach(key => {
  92. trimmed[key] = seenReferences.has(object[key])
  93. ? seenReferences.get(object[key])
  94. : getObjectSubset(object[key], subset[key], seenReferences);
  95. });
  96. if (Object.keys(trimmed).length > 0) {
  97. return trimmed;
  98. }
  99. }
  100. return object;
  101. };
  102. exports.getObjectSubset = getObjectSubset;
  103. const IteratorSymbol = Symbol.iterator;
  104. const hasIterator = object => !!(object != null && object[IteratorSymbol]);
  105. const iterableEquality = (a, b, aStack = [], bStack = []) => {
  106. if (
  107. typeof a !== 'object' ||
  108. typeof b !== 'object' ||
  109. Array.isArray(a) ||
  110. Array.isArray(b) ||
  111. !hasIterator(a) ||
  112. !hasIterator(b)
  113. ) {
  114. return undefined;
  115. }
  116. if (a.constructor !== b.constructor) {
  117. return false;
  118. }
  119. let length = aStack.length;
  120. while (length--) {
  121. // Linear search. Performance is inversely proportional to the number of
  122. // unique nested structures.
  123. // circular references at same depth are equal
  124. // circular reference is not equal to non-circular one
  125. if (aStack[length] === a) {
  126. return bStack[length] === b;
  127. }
  128. }
  129. aStack.push(a);
  130. bStack.push(b);
  131. const iterableEqualityWithStack = (a, b) =>
  132. iterableEquality(a, b, [...aStack], [...bStack]);
  133. if (a.size !== undefined) {
  134. if (a.size !== b.size) {
  135. return false;
  136. } else if (
  137. (0, _jasmineUtils.isA)('Set', a) ||
  138. (0, _jasmineUtils.isImmutableUnorderedSet)(a)
  139. ) {
  140. let allFound = true;
  141. var _iteratorNormalCompletion = true;
  142. var _didIteratorError = false;
  143. var _iteratorError = undefined;
  144. try {
  145. for (
  146. var _iterator = a[Symbol.iterator](), _step;
  147. !(_iteratorNormalCompletion = (_step = _iterator.next()).done);
  148. _iteratorNormalCompletion = true
  149. ) {
  150. const aValue = _step.value;
  151. if (!b.has(aValue)) {
  152. let has = false;
  153. var _iteratorNormalCompletion2 = true;
  154. var _didIteratorError2 = false;
  155. var _iteratorError2 = undefined;
  156. try {
  157. for (
  158. var _iterator2 = b[Symbol.iterator](), _step2;
  159. !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next())
  160. .done);
  161. _iteratorNormalCompletion2 = true
  162. ) {
  163. const bValue = _step2.value;
  164. const isEqual = (0, _jasmineUtils.equals)(aValue, bValue, [
  165. iterableEqualityWithStack
  166. ]);
  167. if (isEqual === true) {
  168. has = true;
  169. }
  170. }
  171. } catch (err) {
  172. _didIteratorError2 = true;
  173. _iteratorError2 = err;
  174. } finally {
  175. try {
  176. if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
  177. _iterator2.return();
  178. }
  179. } finally {
  180. if (_didIteratorError2) {
  181. throw _iteratorError2;
  182. }
  183. }
  184. }
  185. if (has === false) {
  186. allFound = false;
  187. break;
  188. }
  189. }
  190. } // Remove the first value from the stack of traversed values.
  191. } catch (err) {
  192. _didIteratorError = true;
  193. _iteratorError = err;
  194. } finally {
  195. try {
  196. if (!_iteratorNormalCompletion && _iterator.return != null) {
  197. _iterator.return();
  198. }
  199. } finally {
  200. if (_didIteratorError) {
  201. throw _iteratorError;
  202. }
  203. }
  204. }
  205. aStack.pop();
  206. bStack.pop();
  207. return allFound;
  208. } else if (
  209. (0, _jasmineUtils.isA)('Map', a) ||
  210. (0, _jasmineUtils.isImmutableUnorderedKeyed)(a)
  211. ) {
  212. let allFound = true;
  213. var _iteratorNormalCompletion3 = true;
  214. var _didIteratorError3 = false;
  215. var _iteratorError3 = undefined;
  216. try {
  217. for (
  218. var _iterator3 = a[Symbol.iterator](), _step3;
  219. !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done);
  220. _iteratorNormalCompletion3 = true
  221. ) {
  222. const aEntry = _step3.value;
  223. if (
  224. !b.has(aEntry[0]) ||
  225. !(0, _jasmineUtils.equals)(aEntry[1], b.get(aEntry[0]), [
  226. iterableEqualityWithStack
  227. ])
  228. ) {
  229. let has = false;
  230. var _iteratorNormalCompletion4 = true;
  231. var _didIteratorError4 = false;
  232. var _iteratorError4 = undefined;
  233. try {
  234. for (
  235. var _iterator4 = b[Symbol.iterator](), _step4;
  236. !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next())
  237. .done);
  238. _iteratorNormalCompletion4 = true
  239. ) {
  240. const bEntry = _step4.value;
  241. const matchedKey = (0, _jasmineUtils.equals)(
  242. aEntry[0],
  243. bEntry[0],
  244. [iterableEqualityWithStack]
  245. );
  246. let matchedValue = false;
  247. if (matchedKey === true) {
  248. matchedValue = (0, _jasmineUtils.equals)(
  249. aEntry[1],
  250. bEntry[1],
  251. [iterableEqualityWithStack]
  252. );
  253. }
  254. if (matchedValue === true) {
  255. has = true;
  256. }
  257. }
  258. } catch (err) {
  259. _didIteratorError4 = true;
  260. _iteratorError4 = err;
  261. } finally {
  262. try {
  263. if (!_iteratorNormalCompletion4 && _iterator4.return != null) {
  264. _iterator4.return();
  265. }
  266. } finally {
  267. if (_didIteratorError4) {
  268. throw _iteratorError4;
  269. }
  270. }
  271. }
  272. if (has === false) {
  273. allFound = false;
  274. break;
  275. }
  276. }
  277. } // Remove the first value from the stack of traversed values.
  278. } catch (err) {
  279. _didIteratorError3 = true;
  280. _iteratorError3 = err;
  281. } finally {
  282. try {
  283. if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
  284. _iterator3.return();
  285. }
  286. } finally {
  287. if (_didIteratorError3) {
  288. throw _iteratorError3;
  289. }
  290. }
  291. }
  292. aStack.pop();
  293. bStack.pop();
  294. return allFound;
  295. }
  296. }
  297. const bIterator = b[IteratorSymbol]();
  298. var _iteratorNormalCompletion5 = true;
  299. var _didIteratorError5 = false;
  300. var _iteratorError5 = undefined;
  301. try {
  302. for (
  303. var _iterator5 = a[Symbol.iterator](), _step5;
  304. !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done);
  305. _iteratorNormalCompletion5 = true
  306. ) {
  307. const aValue = _step5.value;
  308. const nextB = bIterator.next();
  309. if (
  310. nextB.done ||
  311. !(0, _jasmineUtils.equals)(aValue, nextB.value, [
  312. iterableEqualityWithStack
  313. ])
  314. ) {
  315. return false;
  316. }
  317. }
  318. } catch (err) {
  319. _didIteratorError5 = true;
  320. _iteratorError5 = err;
  321. } finally {
  322. try {
  323. if (!_iteratorNormalCompletion5 && _iterator5.return != null) {
  324. _iterator5.return();
  325. }
  326. } finally {
  327. if (_didIteratorError5) {
  328. throw _iteratorError5;
  329. }
  330. }
  331. }
  332. if (!bIterator.next().done) {
  333. return false;
  334. } // Remove the first value from the stack of traversed values.
  335. aStack.pop();
  336. bStack.pop();
  337. return true;
  338. };
  339. exports.iterableEquality = iterableEquality;
  340. const isObject = a => a !== null && typeof a === 'object';
  341. const isObjectWithKeys = a =>
  342. isObject(a) &&
  343. !(a instanceof Error) &&
  344. !(a instanceof Array) &&
  345. !(a instanceof Date);
  346. const subsetEquality = (object, subset) => {
  347. // subsetEquality needs to keep track of the references
  348. // it has already visited to avoid infinite loops in case
  349. // there are circular references in the subset passed to it.
  350. const subsetEqualityWithContext = (seenReferences = new WeakMap()) => (
  351. object,
  352. subset
  353. ) => {
  354. if (!isObjectWithKeys(subset)) {
  355. return undefined;
  356. }
  357. return Object.keys(subset).every(key => {
  358. if (isObjectWithKeys(subset[key])) {
  359. if (seenReferences.get(subset[key])) {
  360. return (0, _jasmineUtils.equals)(object[key], subset[key], [
  361. iterableEquality
  362. ]);
  363. }
  364. seenReferences.set(subset[key], true);
  365. }
  366. return (
  367. object != null &&
  368. hasOwnProperty(object, key) &&
  369. (0, _jasmineUtils.equals)(object[key], subset[key], [
  370. iterableEquality,
  371. subsetEqualityWithContext(seenReferences)
  372. ])
  373. );
  374. });
  375. };
  376. return subsetEqualityWithContext()(object, subset);
  377. };
  378. exports.subsetEquality = subsetEquality;
  379. const typeEquality = (a, b) => {
  380. if (a == null || b == null || a.constructor === b.constructor) {
  381. return undefined;
  382. }
  383. return false;
  384. };
  385. exports.typeEquality = typeEquality;
  386. const sparseArrayEquality = (a, b) => {
  387. if (!Array.isArray(a) || !Array.isArray(b)) {
  388. return undefined;
  389. } // A sparse array [, , 1] will have keys ["2"] whereas [undefined, undefined, 1] will have keys ["0", "1", "2"]
  390. const aKeys = Object.keys(a);
  391. const bKeys = Object.keys(b);
  392. return (
  393. (0, _jasmineUtils.equals)(a, b, [iterableEquality, typeEquality], true) &&
  394. (0, _jasmineUtils.equals)(aKeys, bKeys)
  395. );
  396. };
  397. exports.sparseArrayEquality = sparseArrayEquality;
  398. const partition = (items, predicate) => {
  399. const result = [[], []];
  400. items.forEach(item => result[predicate(item) ? 0 : 1].push(item));
  401. return result;
  402. }; // Copied from https://github.com/graingert/angular.js/blob/a43574052e9775cbc1d7dd8a086752c979b0f020/src/Angular.js#L685-L693
  403. exports.partition = partition;
  404. const isError = value => {
  405. switch (Object.prototype.toString.call(value)) {
  406. case '[object Error]':
  407. return true;
  408. case '[object Exception]':
  409. return true;
  410. case '[object DOMException]':
  411. return true;
  412. default:
  413. return value instanceof Error;
  414. }
  415. };
  416. exports.isError = isError;
  417. function emptyObject(obj) {
  418. return obj && typeof obj === 'object' ? !Object.keys(obj).length : false;
  419. }
  420. const MULTILINE_REGEXP = /[\r\n]/;
  421. const isOneline = (expected, received) =>
  422. typeof expected === 'string' &&
  423. typeof received === 'string' &&
  424. (!MULTILINE_REGEXP.test(expected) || !MULTILINE_REGEXP.test(received));
  425. exports.isOneline = isOneline;