index.js 25 KB

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