index.cjs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. "use strict";
  2. var __defProp = Object.defineProperty;
  3. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  4. var __getOwnPropNames = Object.getOwnPropertyNames;
  5. var __hasOwnProp = Object.prototype.hasOwnProperty;
  6. var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
  7. var __export = (target, all) => {
  8. for (var name in all)
  9. __defProp(target, name, { get: all[name], enumerable: true });
  10. };
  11. var __copyProps = (to, from, except, desc) => {
  12. if (from && typeof from === "object" || typeof from === "function") {
  13. for (let key of __getOwnPropNames(from))
  14. if (!__hasOwnProp.call(to, key) && key !== except)
  15. __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  16. }
  17. return to;
  18. };
  19. var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  20. // src/index.js
  21. var index_exports = {};
  22. __export(index_exports, {
  23. SpiesGroup: () => SpiesGroup,
  24. chaiSpies: () => chaiSpies,
  25. createSpiesGroup: () => createSpiesGroup,
  26. createSpy: () => createSpy
  27. });
  28. module.exports = __toCommonJS(index_exports);
  29. // src/chai/chai-spies.js
  30. function chaiSpies(chai, _) {
  31. const Assertion = chai.Assertion;
  32. Assertion.addProperty("spy", function() {
  33. this.assert(
  34. this._obj.__isSpy === true,
  35. "expected " + this._obj + " to be a spy",
  36. "expected " + this._obj + " to not be a spy"
  37. );
  38. return this;
  39. });
  40. function assertCalled(n) {
  41. new Assertion(this._obj).to.be.spy;
  42. const spy = this._obj;
  43. if (n != void 0) {
  44. this.assert(
  45. spy.calls.length === n,
  46. "expected " + this._obj + " to have been called #{exp} but got #{act}",
  47. "expected " + this._obj + " to have not been called #{exp}",
  48. n,
  49. spy.calls.length
  50. );
  51. } else {
  52. this.assert(
  53. spy.called === true,
  54. "expected " + this._obj + " to have been called",
  55. "expected " + this._obj + " to not have been called"
  56. );
  57. }
  58. }
  59. __name(assertCalled, "assertCalled");
  60. function assertCalledChain() {
  61. new Assertion(this._obj).to.be.spy;
  62. }
  63. __name(assertCalledChain, "assertCalledChain");
  64. Assertion.addChainableMethod("called", assertCalled, assertCalledChain);
  65. Assertion.addProperty("once", function() {
  66. new Assertion(this._obj).to.be.spy;
  67. this.assert(
  68. this._obj.calls.length === 1,
  69. "expected " + this._obj + " to have been called once but got #{act}",
  70. "expected " + this._obj + " to not have been called once",
  71. 1,
  72. this._obj.calls.length
  73. );
  74. });
  75. Assertion.addProperty("twice", function() {
  76. new Assertion(this._obj).to.be.spy;
  77. this.assert(
  78. this._obj.calls.length === 2,
  79. "expected " + this._obj + " to have been called twice but got #{act}",
  80. "expected " + this._obj + " to not have been called twice",
  81. 2,
  82. this._obj.calls.length
  83. );
  84. });
  85. function nthCallWith(spy, n, expArgs) {
  86. if (spy.calls.length <= n) return false;
  87. const actArgs = spy.calls[n].args;
  88. if (actArgs.length !== expArgs.length) return false;
  89. for (let i = 0; i < expArgs.length; i++) {
  90. if (!_.eql(actArgs[i], expArgs[i])) {
  91. return false;
  92. }
  93. }
  94. return true;
  95. }
  96. __name(nthCallWith, "nthCallWith");
  97. function numberOfCallsWith(spy, expArgs) {
  98. let found = 0;
  99. const calls = spy.calls;
  100. for (let i = 0; i < calls.length; i++) {
  101. if (nthCallWith(spy, i, expArgs)) {
  102. found++;
  103. }
  104. }
  105. return found;
  106. }
  107. __name(numberOfCallsWith, "numberOfCallsWith");
  108. Assertion.addProperty("first", function() {
  109. if (this._obj.__isSpy) {
  110. _.flag(this, "spy nth call with", 1);
  111. }
  112. });
  113. Assertion.addProperty("second", function() {
  114. if (this._obj.__isSpy) {
  115. _.flag(this, "spy nth call with", 2);
  116. }
  117. });
  118. Assertion.addProperty("third", function() {
  119. if (this._obj.__isSpy) {
  120. _.flag(this, "spy nth call with", 3);
  121. }
  122. });
  123. Assertion.addProperty("on");
  124. Assertion.addChainableMethod("nth", function(n) {
  125. if (this._obj.__isSpy) {
  126. _.flag(this, "spy nth call with", n);
  127. }
  128. });
  129. function generateOrdinalNumber(n) {
  130. if (n === 1) return "first";
  131. if (n === 2) return "second";
  132. if (n === 3) return "third";
  133. return n + "th";
  134. }
  135. __name(generateOrdinalNumber, "generateOrdinalNumber");
  136. function assertWith() {
  137. new Assertion(this._obj).to.be.spy;
  138. const expArgs = [].slice.call(arguments, 0), spy = this._obj, calls = spy.calls, always = _.flag(this, "spy always"), nthCall = _.flag(this, "spy nth call with");
  139. if (always) {
  140. const passed = numberOfCallsWith(spy, expArgs);
  141. this.assert(
  142. arguments.length ? calls.length && passed === calls.length : calls.length === 0,
  143. "expected " + this._obj + " to have been always called with #{exp} but got " + passed + " out of " + calls.length,
  144. "expected " + this._obj + " to have not always been called with #{exp}",
  145. expArgs
  146. );
  147. } else if (nthCall) {
  148. const ordinalNumber = generateOrdinalNumber(nthCall), actArgs = calls[nthCall - 1];
  149. new Assertion(this._obj).to.be.have.been.called.min(nthCall);
  150. this.assert(
  151. nthCallWith(spy, nthCall - 1, expArgs),
  152. "expected " + this._obj + " to have been called at the " + ordinalNumber + " time with #{exp} but got #{act}",
  153. "expected " + this._obj + " to have not been called at the " + ordinalNumber + " time with #{exp}",
  154. expArgs,
  155. actArgs
  156. );
  157. } else {
  158. const passed = numberOfCallsWith(spy, expArgs);
  159. this.assert(
  160. passed > 0,
  161. "expected " + this._obj + " to have been called with #{exp}",
  162. "expected " + this._obj + " to have not been called with #{exp} but got " + passed + " times",
  163. expArgs
  164. );
  165. }
  166. }
  167. __name(assertWith, "assertWith");
  168. function assertWithChain() {
  169. if (this._obj.__isSpy) {
  170. _.flag(this, "spy with", true);
  171. }
  172. }
  173. __name(assertWithChain, "assertWithChain");
  174. Assertion.addChainableMethod("with", assertWith, assertWithChain);
  175. Assertion.addProperty("always", function() {
  176. if (this._obj.__isSpy) {
  177. _.flag(this, "spy always", true);
  178. }
  179. });
  180. Assertion.addMethod("exactly", function() {
  181. new Assertion(this._obj).to.be.spy;
  182. const args = [].slice.call(arguments, 0);
  183. this.assert(
  184. this._obj.calls.length === args[0],
  185. "expected " + this._obj + " to have been called #{exp} times but got #{act}",
  186. "expected " + this._obj + " to not have been called #{exp} times",
  187. args[0],
  188. this._obj.calls.length
  189. );
  190. });
  191. function above(_super) {
  192. return function(n) {
  193. if (this._obj.__isSpy) {
  194. new Assertion(this._obj).to.be.spy;
  195. this.assert(
  196. this._obj.calls.length > n,
  197. "expected " + this._obj + " to have been called more than #{exp} times but got #{act}",
  198. "expected " + this._obj + " to have been called at most #{exp} times but got #{act}",
  199. n,
  200. this._obj.calls.length
  201. );
  202. } else {
  203. _super.apply(this, arguments);
  204. }
  205. };
  206. }
  207. __name(above, "above");
  208. Assertion.overwriteMethod("above", above);
  209. Assertion.overwriteMethod("gt", above);
  210. function below(_super) {
  211. return function(n) {
  212. if (this._obj.__isSpy) {
  213. new Assertion(this._obj).to.be.spy;
  214. this.assert(
  215. this._obj.calls.length < n,
  216. "expected " + this._obj + " to have been called fewer than #{exp} times but got #{act}",
  217. "expected " + this._obj + " to have been called at least #{exp} times but got #{act}",
  218. n,
  219. this._obj.calls.length
  220. );
  221. } else {
  222. _super.apply(this, arguments);
  223. }
  224. };
  225. }
  226. __name(below, "below");
  227. Assertion.overwriteMethod("below", below);
  228. Assertion.overwriteMethod("lt", below);
  229. function min(_super) {
  230. return function(n) {
  231. if (this._obj.__isSpy) {
  232. new Assertion(this._obj).to.be.spy;
  233. this.assert(
  234. this._obj.calls.length >= n,
  235. "expected " + this._obj + " to have been called at least #{exp} times but got #{act}",
  236. "expected " + this._obj + " to have been called fewer than #{exp} times but got #{act}",
  237. n,
  238. this._obj.calls.length
  239. );
  240. } else {
  241. _super.apply(this, arguments);
  242. }
  243. };
  244. }
  245. __name(min, "min");
  246. Assertion.overwriteMethod("min", min);
  247. Assertion.overwriteMethod("least", min);
  248. function max(_super) {
  249. return function(n) {
  250. if (this._obj.__isSpy) {
  251. new Assertion(this._obj).to.be.spy;
  252. this.assert(
  253. this._obj.calls.length <= n,
  254. "expected " + this._obj + " to have been called at most #{exp} times but got #{act}",
  255. "expected " + this._obj + " to have been called more than #{exp} times but got #{act}",
  256. n,
  257. this._obj.calls.length
  258. );
  259. } else {
  260. _super.apply(this, arguments);
  261. }
  262. };
  263. }
  264. __name(max, "max");
  265. Assertion.overwriteMethod("max", max);
  266. Assertion.overwriteMethod("most", max);
  267. }
  268. __name(chaiSpies, "chaiSpies");
  269. // src/create-spy.js
  270. function _parseSpyArgs(target, methodNameOrImpl, customImplForMethod) {
  271. let originalFn;
  272. let customImplementation;
  273. let isMethodSpy = false;
  274. let objToSpyOn;
  275. let methodName;
  276. let hasOwnMethod = false;
  277. const isLikelyFunctionSpy = typeof target === "function" && customImplForMethod === void 0;
  278. const isLikelyMethodSpy = typeof target === "object" && target !== null && typeof methodNameOrImpl === "string";
  279. if (isLikelyFunctionSpy) {
  280. originalFn = target;
  281. if (methodNameOrImpl !== void 0) {
  282. if (typeof methodNameOrImpl !== "function") {
  283. throw new TypeError(
  284. "When spying on a function, the second argument (custom implementation) must be a function if provided."
  285. );
  286. }
  287. customImplementation = methodNameOrImpl;
  288. }
  289. } else if (isLikelyMethodSpy) {
  290. methodName = methodNameOrImpl;
  291. objToSpyOn = target;
  292. isMethodSpy = true;
  293. hasOwnMethod = Object.prototype.hasOwnProperty.call(objToSpyOn, methodName);
  294. if (!(methodName in target)) {
  295. throw new TypeError(
  296. `Attempted to spy on a non-existent property: "${methodName}"`
  297. );
  298. }
  299. const propertyToSpyOn = target[methodName];
  300. if (typeof propertyToSpyOn !== "function") {
  301. throw new TypeError(
  302. `Attempted to spy on "${methodName}" which is not a function. It is a "${typeof propertyToSpyOn}".`
  303. );
  304. }
  305. originalFn = propertyToSpyOn;
  306. if (customImplForMethod !== void 0) {
  307. if (typeof customImplForMethod !== "function") {
  308. throw new TypeError(
  309. "When spying on a method, the third argument (custom implementation) must be a function if provided."
  310. );
  311. }
  312. customImplementation = customImplForMethod;
  313. }
  314. } else {
  315. if (target === null && methodNameOrImpl === void 0 && customImplForMethod === void 0) {
  316. throw new TypeError("Attempted to spy on null.");
  317. }
  318. if (methodNameOrImpl === void 0 && typeof target !== "function") {
  319. throw new TypeError(
  320. "Attempted to spy on a non-function value. To spy on an object method, you must provide the method name as the second argument."
  321. );
  322. }
  323. throw new Error(
  324. "Invalid arguments. Valid signatures:\n createSpy(function, [customImplementationFunction])\n createSpy(object, methodNameString, [customImplementationFunction])"
  325. );
  326. }
  327. return {
  328. originalFn,
  329. // определение функции для выполнения шпионом: либо
  330. // пользовательская реализация, либо оригинальная функция
  331. fnToExecute: customImplementation || originalFn,
  332. isMethodSpy,
  333. objToSpyOn,
  334. methodName,
  335. hasOwnMethod
  336. };
  337. }
  338. __name(_parseSpyArgs, "_parseSpyArgs");
  339. function createSpy(target = void 0, methodNameOrImpl = void 0, customImplForMethod = void 0) {
  340. if (typeof target === "undefined" && typeof methodNameOrImpl === "undefined" && typeof customImplForMethod === "undefined") {
  341. target = /* @__PURE__ */ __name(function() {
  342. }, "target");
  343. }
  344. const {
  345. originalFn,
  346. fnToExecute,
  347. isMethodSpy,
  348. objToSpyOn,
  349. methodName,
  350. hasOwnMethod
  351. } = _parseSpyArgs(target, methodNameOrImpl, customImplForMethod);
  352. const callLog = {
  353. count: 0,
  354. calls: []
  355. };
  356. const spy = /* @__PURE__ */ __name(function(...args) {
  357. callLog.count++;
  358. const callInfo = {
  359. // сохранение аргументов, с которыми
  360. // был вызван шпион
  361. args: [...args],
  362. // сохранение контекста (this)
  363. // вызова шпиона
  364. thisArg: this,
  365. returnValue: void 0,
  366. error: void 0
  367. };
  368. try {
  369. callInfo.returnValue = fnToExecute.apply(this, args);
  370. callLog.calls.push(callInfo);
  371. return callInfo.returnValue;
  372. } catch (e) {
  373. callInfo.error = e;
  374. callLog.calls.push(callInfo);
  375. throw e;
  376. }
  377. }, "spy");
  378. Object.defineProperty(spy, "calls", {
  379. get: /* @__PURE__ */ __name(() => callLog.calls, "get"),
  380. enumerable: true,
  381. configurable: false
  382. });
  383. Object.defineProperty(spy, "callCount", {
  384. get: /* @__PURE__ */ __name(() => callLog.count, "get"),
  385. enumerable: true,
  386. configurable: false
  387. });
  388. Object.defineProperty(spy, "called", {
  389. get: /* @__PURE__ */ __name(() => callLog.count > 0, "get"),
  390. enumerable: true,
  391. configurable: false
  392. });
  393. spy.restore = () => {
  394. if (isMethodSpy && objToSpyOn) {
  395. if (originalFn !== void 0) {
  396. if (hasOwnMethod) {
  397. objToSpyOn[methodName] = originalFn;
  398. } else {
  399. delete objToSpyOn[methodName];
  400. }
  401. }
  402. }
  403. callLog.count = 0;
  404. callLog.calls = [];
  405. };
  406. if (isMethodSpy && objToSpyOn) {
  407. objToSpyOn[methodName] = spy;
  408. }
  409. spy.__isSpy = true;
  410. return spy;
  411. }
  412. __name(createSpy, "createSpy");
  413. // src/create-spies-group.js
  414. var _SpiesGroup = class _SpiesGroup {
  415. /**
  416. * Constructor.
  417. */
  418. constructor() {
  419. this.spies = [];
  420. }
  421. /**
  422. * Создает шпиона для отдельной функции
  423. * или метода объекта и добавляет его в группу.
  424. *
  425. * @param {Function|object} [target]
  426. * @param {Function|string} [methodNameOrImpl]
  427. * @param {Function} [customImplForMethod]
  428. * @returns {Function}
  429. */
  430. spy(target, methodNameOrImpl, customImplForMethod) {
  431. const spy = createSpy(target, methodNameOrImpl, customImplForMethod);
  432. this.spies.push(spy);
  433. return spy;
  434. }
  435. /**
  436. * Восстановление всех оригинальных методов объектов,
  437. * для которых были созданы шпионы в этой группе,
  438. * и сброс истории вызовов для всех шпионов в группе.
  439. * Очищает внутренний список шпионов.
  440. *
  441. * @returns {this}
  442. */
  443. restore() {
  444. this.spies.forEach((spy) => spy.restore());
  445. this.spies = [];
  446. return this;
  447. }
  448. };
  449. __name(_SpiesGroup, "SpiesGroup");
  450. var SpiesGroup = _SpiesGroup;
  451. function createSpiesGroup() {
  452. return new SpiesGroup();
  453. }
  454. __name(createSpiesGroup, "createSpiesGroup");
  455. // Annotate the CommonJS export names for ESM import in node:
  456. 0 && (module.exports = {
  457. SpiesGroup,
  458. chaiSpies,
  459. createSpiesGroup,
  460. createSpy
  461. });