index.js 25 KB

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