index.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945
  1. 'use strict';
  2. function _defineProperty(obj, key, value) {
  3. if (key in obj) {
  4. Object.defineProperty(obj, key, {
  5. value: value,
  6. enumerable: true,
  7. configurable: true,
  8. writable: true
  9. });
  10. } else {
  11. obj[key] = value;
  12. }
  13. return obj;
  14. }
  15. /**
  16. * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
  17. *
  18. * This source code is licensed under the MIT license found in the
  19. * LICENSE file in the root directory of this source tree.
  20. */
  21. /* eslint-disable local/ban-types-eventually, local/prefer-rest-params-eventually */
  22. // | Window – add once TS improves typings;
  23. /**
  24. * Possible types of a MockFunctionResult.
  25. * 'return': The call completed by returning normally.
  26. * 'throw': The call completed by throwing a value.
  27. * 'incomplete': The call has not completed yet. This is possible if you read
  28. * the mock function result from within the mock function itself
  29. * (or a function called by the mock function).
  30. */
  31. /**
  32. * Represents the result of a single call to a mock function.
  33. */
  34. // see https://github.com/Microsoft/TypeScript/issues/25215
  35. const MOCK_CONSTRUCTOR_NAME = 'mockConstructor';
  36. const FUNCTION_NAME_RESERVED_PATTERN = /[\s!-\/:-@\[-`{-~]/;
  37. const FUNCTION_NAME_RESERVED_REPLACE = new RegExp(
  38. FUNCTION_NAME_RESERVED_PATTERN.source,
  39. 'g'
  40. );
  41. const RESERVED_KEYWORDS = new Set([
  42. 'arguments',
  43. 'await',
  44. 'break',
  45. 'case',
  46. 'catch',
  47. 'class',
  48. 'const',
  49. 'continue',
  50. 'debugger',
  51. 'default',
  52. 'delete',
  53. 'do',
  54. 'else',
  55. 'enum',
  56. 'eval',
  57. 'export',
  58. 'extends',
  59. 'false',
  60. 'finally',
  61. 'for',
  62. 'function',
  63. 'if',
  64. 'implements',
  65. 'import',
  66. 'in',
  67. 'instanceof',
  68. 'interface',
  69. 'let',
  70. 'new',
  71. 'null',
  72. 'package',
  73. 'private',
  74. 'protected',
  75. 'public',
  76. 'return',
  77. 'static',
  78. 'super',
  79. 'switch',
  80. 'this',
  81. 'throw',
  82. 'true',
  83. 'try',
  84. 'typeof',
  85. 'var',
  86. 'void',
  87. 'while',
  88. 'with',
  89. 'yield'
  90. ]);
  91. function matchArity(fn, length) {
  92. let mockConstructor;
  93. switch (length) {
  94. case 1:
  95. mockConstructor = function (_a) {
  96. return fn.apply(this, arguments);
  97. };
  98. break;
  99. case 2:
  100. mockConstructor = function (_a, _b) {
  101. return fn.apply(this, arguments);
  102. };
  103. break;
  104. case 3:
  105. mockConstructor = function (_a, _b, _c) {
  106. return fn.apply(this, arguments);
  107. };
  108. break;
  109. case 4:
  110. mockConstructor = function (_a, _b, _c, _d) {
  111. return fn.apply(this, arguments);
  112. };
  113. break;
  114. case 5:
  115. mockConstructor = function (_a, _b, _c, _d, _e) {
  116. return fn.apply(this, arguments);
  117. };
  118. break;
  119. case 6:
  120. mockConstructor = function (_a, _b, _c, _d, _e, _f) {
  121. return fn.apply(this, arguments);
  122. };
  123. break;
  124. case 7:
  125. mockConstructor = function (_a, _b, _c, _d, _e, _f, _g) {
  126. return fn.apply(this, arguments);
  127. };
  128. break;
  129. case 8:
  130. mockConstructor = function (_a, _b, _c, _d, _e, _f, _g, _h) {
  131. return fn.apply(this, arguments);
  132. };
  133. break;
  134. case 9:
  135. mockConstructor = function (_a, _b, _c, _d, _e, _f, _g, _h, _i) {
  136. return fn.apply(this, arguments);
  137. };
  138. break;
  139. default:
  140. mockConstructor = function () {
  141. return fn.apply(this, arguments);
  142. };
  143. break;
  144. }
  145. return mockConstructor;
  146. }
  147. function getObjectType(value) {
  148. return Object.prototype.toString.apply(value).slice(8, -1);
  149. }
  150. function getType(ref) {
  151. const typeName = getObjectType(ref);
  152. if (
  153. typeName === 'Function' ||
  154. typeName === 'AsyncFunction' ||
  155. typeName === 'GeneratorFunction'
  156. ) {
  157. return 'function';
  158. } else if (Array.isArray(ref)) {
  159. return 'array';
  160. } else if (typeName === 'Object') {
  161. return 'object';
  162. } else if (
  163. typeName === 'Number' ||
  164. typeName === 'String' ||
  165. typeName === 'Boolean' ||
  166. typeName === 'Symbol'
  167. ) {
  168. return 'constant';
  169. } else if (
  170. typeName === 'Map' ||
  171. typeName === 'WeakMap' ||
  172. typeName === 'Set'
  173. ) {
  174. return 'collection';
  175. } else if (typeName === 'RegExp') {
  176. return 'regexp';
  177. } else if (ref === undefined) {
  178. return 'undefined';
  179. } else if (ref === null) {
  180. return 'null';
  181. } else {
  182. return null;
  183. }
  184. }
  185. function isReadonlyProp(object, prop) {
  186. if (
  187. prop === 'arguments' ||
  188. prop === 'caller' ||
  189. prop === 'callee' ||
  190. prop === 'name' ||
  191. prop === 'length'
  192. ) {
  193. const typeName = getObjectType(object);
  194. return (
  195. typeName === 'Function' ||
  196. typeName === 'AsyncFunction' ||
  197. typeName === 'GeneratorFunction'
  198. );
  199. }
  200. if (
  201. prop === 'source' ||
  202. prop === 'global' ||
  203. prop === 'ignoreCase' ||
  204. prop === 'multiline'
  205. ) {
  206. return getObjectType(object) === 'RegExp';
  207. }
  208. return false;
  209. }
  210. class ModuleMockerClass {
  211. /**
  212. * @see README.md
  213. * @param global Global object of the test environment, used to create
  214. * mocks
  215. */
  216. constructor(global) {
  217. _defineProperty(this, '_environmentGlobal', void 0);
  218. _defineProperty(this, '_mockState', void 0);
  219. _defineProperty(this, '_mockConfigRegistry', void 0);
  220. _defineProperty(this, '_spyState', void 0);
  221. _defineProperty(this, '_invocationCallCounter', void 0);
  222. _defineProperty(this, 'ModuleMocker', void 0);
  223. this._environmentGlobal = global;
  224. this._mockState = new WeakMap();
  225. this._mockConfigRegistry = new WeakMap();
  226. this._spyState = new Set();
  227. this.ModuleMocker = ModuleMockerClass;
  228. this._invocationCallCounter = 1;
  229. }
  230. _getSlots(object) {
  231. if (!object) {
  232. return [];
  233. }
  234. const slots = new Set();
  235. const EnvObjectProto = this._environmentGlobal.Object.prototype;
  236. const EnvFunctionProto = this._environmentGlobal.Function.prototype;
  237. const EnvRegExpProto = this._environmentGlobal.RegExp.prototype; // Also check the builtins in the current context as they leak through
  238. // core node modules.
  239. const ObjectProto = Object.prototype;
  240. const FunctionProto = Function.prototype;
  241. const RegExpProto = RegExp.prototype; // Properties of Object.prototype, Function.prototype and RegExp.prototype
  242. // are never reported as slots
  243. while (
  244. object != null &&
  245. object !== EnvObjectProto &&
  246. object !== EnvFunctionProto &&
  247. object !== EnvRegExpProto &&
  248. object !== ObjectProto &&
  249. object !== FunctionProto &&
  250. object !== RegExpProto
  251. ) {
  252. const ownNames = Object.getOwnPropertyNames(object);
  253. for (let i = 0; i < ownNames.length; i++) {
  254. const prop = ownNames[i];
  255. if (!isReadonlyProp(object, prop)) {
  256. const propDesc = Object.getOwnPropertyDescriptor(object, prop);
  257. if ((propDesc !== undefined && !propDesc.get) || object.__esModule) {
  258. slots.add(prop);
  259. }
  260. }
  261. }
  262. object = Object.getPrototypeOf(object);
  263. }
  264. return Array.from(slots);
  265. }
  266. _ensureMockConfig(f) {
  267. let config = this._mockConfigRegistry.get(f);
  268. if (!config) {
  269. config = this._defaultMockConfig();
  270. this._mockConfigRegistry.set(f, config);
  271. }
  272. return config;
  273. }
  274. _ensureMockState(f) {
  275. let state = this._mockState.get(f);
  276. if (!state) {
  277. state = this._defaultMockState();
  278. this._mockState.set(f, state);
  279. }
  280. return state;
  281. }
  282. _defaultMockConfig() {
  283. return {
  284. mockImpl: undefined,
  285. mockName: 'jest.fn()',
  286. specificMockImpls: [],
  287. specificReturnValues: []
  288. };
  289. }
  290. _defaultMockState() {
  291. return {
  292. calls: [],
  293. instances: [],
  294. invocationCallOrder: [],
  295. results: []
  296. };
  297. }
  298. _makeComponent(metadata, restore) {
  299. if (metadata.type === 'object') {
  300. return new this._environmentGlobal.Object();
  301. } else if (metadata.type === 'array') {
  302. return new this._environmentGlobal.Array();
  303. } else if (metadata.type === 'regexp') {
  304. return new this._environmentGlobal.RegExp('');
  305. } else if (
  306. metadata.type === 'constant' ||
  307. metadata.type === 'collection' ||
  308. metadata.type === 'null' ||
  309. metadata.type === 'undefined'
  310. ) {
  311. return metadata.value;
  312. } else if (metadata.type === 'function') {
  313. const prototype =
  314. (metadata.members &&
  315. metadata.members.prototype &&
  316. metadata.members.prototype.members) ||
  317. {};
  318. const prototypeSlots = this._getSlots(prototype);
  319. const mocker = this;
  320. const mockConstructor = matchArity(function (...args) {
  321. const mockState = mocker._ensureMockState(f);
  322. const mockConfig = mocker._ensureMockConfig(f);
  323. mockState.instances.push(this);
  324. mockState.calls.push(args); // Create and record an "incomplete" mock result immediately upon
  325. // calling rather than waiting for the mock to return. This avoids
  326. // issues caused by recursion where results can be recorded in the
  327. // wrong order.
  328. const mockResult = {
  329. type: 'incomplete',
  330. value: undefined
  331. };
  332. mockState.results.push(mockResult);
  333. mockState.invocationCallOrder.push(mocker._invocationCallCounter++); // Will be set to the return value of the mock if an error is not thrown
  334. let finalReturnValue; // Will be set to the error that is thrown by the mock (if it throws)
  335. let thrownError; // Will be set to true if the mock throws an error. The presence of a
  336. // value in `thrownError` is not a 100% reliable indicator because a
  337. // function could throw a value of undefined.
  338. let callDidThrowError = false;
  339. try {
  340. // The bulk of the implementation is wrapped in an immediately
  341. // executed arrow function so the return value of the mock function
  342. // can be easily captured and recorded, despite the many separate
  343. // return points within the logic.
  344. finalReturnValue = (() => {
  345. if (this instanceof f) {
  346. // This is probably being called as a constructor
  347. prototypeSlots.forEach(slot => {
  348. // Copy prototype methods to the instance to make
  349. // it easier to interact with mock instance call and
  350. // return values
  351. if (prototype[slot].type === 'function') {
  352. // @ts-expect-error no index signature
  353. const protoImpl = this[slot]; // @ts-expect-error no index signature
  354. this[slot] = mocker.generateFromMetadata(prototype[slot]); // @ts-expect-error no index signature
  355. this[slot]._protoImpl = protoImpl;
  356. }
  357. }); // Run the mock constructor implementation
  358. const mockImpl = mockConfig.specificMockImpls.length
  359. ? mockConfig.specificMockImpls.shift()
  360. : mockConfig.mockImpl;
  361. return mockImpl && mockImpl.apply(this, arguments);
  362. } // If mockImplementationOnce()/mockImplementation() is last set,
  363. // implementation use the mock
  364. let specificMockImpl = mockConfig.specificMockImpls.shift();
  365. if (specificMockImpl === undefined) {
  366. specificMockImpl = mockConfig.mockImpl;
  367. }
  368. if (specificMockImpl) {
  369. return specificMockImpl.apply(this, arguments);
  370. } // Otherwise use prototype implementation
  371. if (f._protoImpl) {
  372. return f._protoImpl.apply(this, arguments);
  373. }
  374. return undefined;
  375. })();
  376. } catch (error) {
  377. // Store the thrown error so we can record it, then re-throw it.
  378. thrownError = error;
  379. callDidThrowError = true;
  380. throw error;
  381. } finally {
  382. // Record the result of the function.
  383. // NOTE: Intentionally NOT pushing/indexing into the array of mock
  384. // results here to avoid corrupting results data if mockClear()
  385. // is called during the execution of the mock.
  386. mockResult.type = callDidThrowError ? 'throw' : 'return';
  387. mockResult.value = callDidThrowError ? thrownError : finalReturnValue;
  388. }
  389. return finalReturnValue;
  390. }, metadata.length || 0);
  391. const f = this._createMockFunction(metadata, mockConstructor);
  392. f._isMockFunction = true;
  393. f.getMockImplementation = () => this._ensureMockConfig(f).mockImpl;
  394. if (typeof restore === 'function') {
  395. this._spyState.add(restore);
  396. }
  397. this._mockState.set(f, this._defaultMockState());
  398. this._mockConfigRegistry.set(f, this._defaultMockConfig());
  399. Object.defineProperty(f, 'mock', {
  400. configurable: false,
  401. enumerable: true,
  402. get: () => this._ensureMockState(f),
  403. set: val => this._mockState.set(f, val)
  404. });
  405. f.mockClear = () => {
  406. this._mockState.delete(f);
  407. return f;
  408. };
  409. f.mockReset = () => {
  410. f.mockClear();
  411. this._mockConfigRegistry.delete(f);
  412. return f;
  413. };
  414. f.mockRestore = () => {
  415. f.mockReset();
  416. return restore ? restore() : undefined;
  417. };
  418. f.mockReturnValueOnce = (
  419. value // next function call will return this value or default return value
  420. ) => f.mockImplementationOnce(() => value);
  421. f.mockResolvedValueOnce = value =>
  422. f.mockImplementationOnce(() => Promise.resolve(value));
  423. f.mockRejectedValueOnce = value =>
  424. f.mockImplementationOnce(() => Promise.reject(value));
  425. f.mockReturnValue = (
  426. value // next function call will return specified return value or this one
  427. ) => f.mockImplementation(() => value);
  428. f.mockResolvedValue = value =>
  429. f.mockImplementation(() => Promise.resolve(value));
  430. f.mockRejectedValue = value =>
  431. f.mockImplementation(() => Promise.reject(value));
  432. f.mockImplementationOnce = fn => {
  433. // next function call will use this mock implementation return value
  434. // or default mock implementation return value
  435. const mockConfig = this._ensureMockConfig(f);
  436. mockConfig.specificMockImpls.push(fn);
  437. return f;
  438. };
  439. f.mockImplementation = fn => {
  440. // next function call will use mock implementation return value
  441. const mockConfig = this._ensureMockConfig(f);
  442. mockConfig.mockImpl = fn;
  443. return f;
  444. };
  445. f.mockReturnThis = () =>
  446. f.mockImplementation(function () {
  447. return this;
  448. });
  449. f.mockName = name => {
  450. if (name) {
  451. const mockConfig = this._ensureMockConfig(f);
  452. mockConfig.mockName = name;
  453. }
  454. return f;
  455. };
  456. f.getMockName = () => {
  457. const mockConfig = this._ensureMockConfig(f);
  458. return mockConfig.mockName || 'jest.fn()';
  459. };
  460. if (metadata.mockImpl) {
  461. f.mockImplementation(metadata.mockImpl);
  462. }
  463. return f;
  464. } else {
  465. const unknownType = metadata.type || 'undefined type';
  466. throw new Error('Unrecognized type ' + unknownType);
  467. }
  468. }
  469. _createMockFunction(metadata, mockConstructor) {
  470. let name = metadata.name;
  471. if (!name) {
  472. return mockConstructor;
  473. } // Preserve `name` property of mocked function.
  474. const boundFunctionPrefix = 'bound ';
  475. let bindCall = ''; // if-do-while for perf reasons. The common case is for the if to fail.
  476. if (name && name.startsWith(boundFunctionPrefix)) {
  477. do {
  478. name = name.substring(boundFunctionPrefix.length); // Call bind() just to alter the function name.
  479. bindCall = '.bind(null)';
  480. } while (name && name.startsWith(boundFunctionPrefix));
  481. } // Special case functions named `mockConstructor` to guard for infinite
  482. // loops.
  483. if (name === MOCK_CONSTRUCTOR_NAME) {
  484. return mockConstructor;
  485. }
  486. if (
  487. // It's a syntax error to define functions with a reserved keyword
  488. // as name.
  489. RESERVED_KEYWORDS.has(name) || // It's also a syntax error to define functions with a name that starts with a number
  490. /^\d/.test(name)
  491. ) {
  492. name = '$' + name;
  493. } // It's also a syntax error to define a function with a reserved character
  494. // as part of it's name.
  495. if (FUNCTION_NAME_RESERVED_PATTERN.test(name)) {
  496. name = name.replace(FUNCTION_NAME_RESERVED_REPLACE, '$');
  497. }
  498. const body =
  499. 'return function ' +
  500. name +
  501. '() {' +
  502. 'return ' +
  503. MOCK_CONSTRUCTOR_NAME +
  504. '.apply(this,arguments);' +
  505. '}' +
  506. bindCall;
  507. const createConstructor = new this._environmentGlobal.Function(
  508. MOCK_CONSTRUCTOR_NAME,
  509. body
  510. );
  511. return createConstructor(mockConstructor);
  512. }
  513. _generateMock(metadata, callbacks, refs) {
  514. // metadata not compatible but it's the same type, maybe problem with
  515. // overloading of _makeComponent and not _generateMock?
  516. // @ts-expect-error
  517. const mock = this._makeComponent(metadata);
  518. if (metadata.refID != null) {
  519. refs[metadata.refID] = mock;
  520. }
  521. this._getSlots(metadata.members).forEach(slot => {
  522. const slotMetadata = (metadata.members && metadata.members[slot]) || {};
  523. if (slotMetadata.ref != null) {
  524. callbacks.push(
  525. (function (ref) {
  526. return () => (mock[slot] = refs[ref]);
  527. })(slotMetadata.ref)
  528. );
  529. } else {
  530. mock[slot] = this._generateMock(slotMetadata, callbacks, refs);
  531. }
  532. });
  533. if (
  534. metadata.type !== 'undefined' &&
  535. metadata.type !== 'null' &&
  536. mock.prototype &&
  537. typeof mock.prototype === 'object'
  538. ) {
  539. mock.prototype.constructor = mock;
  540. }
  541. return mock;
  542. }
  543. /**
  544. * @see README.md
  545. * @param _metadata Metadata for the mock in the schema returned by the
  546. * getMetadata method of this module.
  547. */
  548. generateFromMetadata(_metadata) {
  549. const callbacks = [];
  550. const refs = {};
  551. const mock = this._generateMock(_metadata, callbacks, refs);
  552. callbacks.forEach(setter => setter());
  553. return mock;
  554. }
  555. /**
  556. * @see README.md
  557. * @param component The component for which to retrieve metadata.
  558. */
  559. getMetadata(component, _refs) {
  560. const refs = _refs || new Map();
  561. const ref = refs.get(component);
  562. if (ref != null) {
  563. return {
  564. ref
  565. };
  566. }
  567. const type = getType(component);
  568. if (!type) {
  569. return null;
  570. }
  571. const metadata = {
  572. type
  573. };
  574. if (
  575. type === 'constant' ||
  576. type === 'collection' ||
  577. type === 'undefined' ||
  578. type === 'null'
  579. ) {
  580. metadata.value = component;
  581. return metadata;
  582. } else if (type === 'function') {
  583. // @ts-expect-error this is a function so it has a name
  584. metadata.name = component.name; // @ts-expect-error may be a mock
  585. if (component._isMockFunction === true) {
  586. // @ts-expect-error may be a mock
  587. metadata.mockImpl = component.getMockImplementation();
  588. }
  589. }
  590. metadata.refID = refs.size;
  591. refs.set(component, metadata.refID);
  592. let members = null; // Leave arrays alone
  593. if (type !== 'array') {
  594. this._getSlots(component).forEach(slot => {
  595. if (
  596. type === 'function' && // @ts-expect-error may be a mock
  597. component._isMockFunction === true &&
  598. slot.match(/^mock/)
  599. ) {
  600. return;
  601. } // @ts-expect-error no index signature
  602. const slotMetadata = this.getMetadata(component[slot], refs);
  603. if (slotMetadata) {
  604. if (!members) {
  605. members = {};
  606. }
  607. members[slot] = slotMetadata;
  608. }
  609. });
  610. }
  611. if (members) {
  612. metadata.members = members;
  613. }
  614. return metadata;
  615. }
  616. isMockFunction(fn) {
  617. return !!fn && fn._isMockFunction === true;
  618. }
  619. fn(implementation) {
  620. const length = implementation ? implementation.length : 0;
  621. const fn = this._makeComponent({
  622. length,
  623. type: 'function'
  624. });
  625. if (implementation) {
  626. fn.mockImplementation(implementation);
  627. }
  628. return fn;
  629. }
  630. spyOn(object, methodName, accessType) {
  631. if (accessType) {
  632. return this._spyOnProperty(object, methodName, accessType);
  633. }
  634. if (typeof object !== 'object' && typeof object !== 'function') {
  635. throw new Error(
  636. 'Cannot spyOn on a primitive value; ' + this._typeOf(object) + ' given'
  637. );
  638. }
  639. const original = object[methodName];
  640. if (!this.isMockFunction(original)) {
  641. if (typeof original !== 'function') {
  642. throw new Error(
  643. 'Cannot spy the ' +
  644. methodName +
  645. ' property because it is not a function; ' +
  646. this._typeOf(original) +
  647. ' given instead'
  648. );
  649. }
  650. const isMethodOwner = object.hasOwnProperty(methodName);
  651. let descriptor = Object.getOwnPropertyDescriptor(object, methodName);
  652. let proto = Object.getPrototypeOf(object);
  653. while (!descriptor && proto !== null) {
  654. descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
  655. proto = Object.getPrototypeOf(proto);
  656. }
  657. let mock;
  658. if (descriptor && descriptor.get) {
  659. const originalGet = descriptor.get;
  660. mock = this._makeComponent(
  661. {
  662. type: 'function'
  663. },
  664. () => {
  665. descriptor.get = originalGet;
  666. Object.defineProperty(object, methodName, descriptor);
  667. }
  668. );
  669. descriptor.get = () => mock;
  670. Object.defineProperty(object, methodName, descriptor);
  671. } else {
  672. mock = this._makeComponent(
  673. {
  674. type: 'function'
  675. },
  676. () => {
  677. if (isMethodOwner) {
  678. object[methodName] = original;
  679. } else {
  680. delete object[methodName];
  681. }
  682. }
  683. ); // @ts-expect-error overriding original method with a Mock
  684. object[methodName] = mock;
  685. }
  686. mock.mockImplementation(function () {
  687. return original.apply(this, arguments);
  688. });
  689. }
  690. return object[methodName];
  691. }
  692. _spyOnProperty(obj, propertyName, accessType = 'get') {
  693. if (typeof obj !== 'object' && typeof obj !== 'function') {
  694. throw new Error(
  695. 'Cannot spyOn on a primitive value; ' + this._typeOf(obj) + ' given'
  696. );
  697. }
  698. if (!obj) {
  699. throw new Error(
  700. 'spyOn could not find an object to spy upon for ' + propertyName + ''
  701. );
  702. }
  703. if (!propertyName) {
  704. throw new Error('No property name supplied');
  705. }
  706. let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
  707. let proto = Object.getPrototypeOf(obj);
  708. while (!descriptor && proto !== null) {
  709. descriptor = Object.getOwnPropertyDescriptor(proto, propertyName);
  710. proto = Object.getPrototypeOf(proto);
  711. }
  712. if (!descriptor) {
  713. throw new Error(propertyName + ' property does not exist');
  714. }
  715. if (!descriptor.configurable) {
  716. throw new Error(propertyName + ' is not declared configurable');
  717. }
  718. if (!descriptor[accessType]) {
  719. throw new Error(
  720. 'Property ' + propertyName + ' does not have access type ' + accessType
  721. );
  722. }
  723. const original = descriptor[accessType];
  724. if (!this.isMockFunction(original)) {
  725. if (typeof original !== 'function') {
  726. throw new Error(
  727. 'Cannot spy the ' +
  728. propertyName +
  729. ' property because it is not a function; ' +
  730. this._typeOf(original) +
  731. ' given instead'
  732. );
  733. } // @ts-expect-error: mock is assignable
  734. descriptor[accessType] = this._makeComponent(
  735. {
  736. type: 'function'
  737. },
  738. () => {
  739. // @ts-expect-error: mock is assignable
  740. descriptor[accessType] = original;
  741. Object.defineProperty(obj, propertyName, descriptor);
  742. }
  743. );
  744. descriptor[accessType].mockImplementation(function () {
  745. // @ts-expect-error
  746. return original.apply(this, arguments);
  747. });
  748. }
  749. Object.defineProperty(obj, propertyName, descriptor);
  750. return descriptor[accessType];
  751. }
  752. clearAllMocks() {
  753. this._mockState = new WeakMap();
  754. }
  755. resetAllMocks() {
  756. this._mockConfigRegistry = new WeakMap();
  757. this._mockState = new WeakMap();
  758. }
  759. restoreAllMocks() {
  760. this._spyState.forEach(restore => restore());
  761. this._spyState = new Set();
  762. }
  763. _typeOf(value) {
  764. return value == null ? '' + value : typeof value;
  765. }
  766. }
  767. const JestMock = new ModuleMockerClass(global);
  768. module.exports = JestMock;