index.cjs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  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. Sandbox: () => Sandbox,
  24. chaiSpies: () => chaiSpies,
  25. createSandbox: () => createSandbox,
  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(...expArgs) {
  137. new Assertion(this._obj).to.be.spy;
  138. const 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. expArgs.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.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(...args) {
  181. new Assertion(this._obj).to.be.spy;
  182. this.assert(
  183. this._obj.calls.length === args[0],
  184. "expected " + this._obj + " to have been called #{exp} times but got #{act}",
  185. "expected " + this._obj + " to not have been called #{exp} times",
  186. args[0],
  187. this._obj.calls.length
  188. );
  189. });
  190. function above(_super) {
  191. return function(n) {
  192. if (this._obj.__isSpy) {
  193. new Assertion(this._obj).to.be.spy;
  194. this.assert(
  195. this._obj.calls.length > n,
  196. "expected " + this._obj + " to have been called more than #{exp} times but got #{act}",
  197. "expected " + this._obj + " to have been called at most #{exp} times but got #{act}",
  198. n,
  199. this._obj.calls.length
  200. );
  201. } else {
  202. _super.apply(this, arguments);
  203. }
  204. };
  205. }
  206. __name(above, "above");
  207. Assertion.overwriteMethod("above", above);
  208. Assertion.overwriteMethod("gt", above);
  209. function below(_super) {
  210. return function(n) {
  211. if (this._obj.__isSpy) {
  212. new Assertion(this._obj).to.be.spy;
  213. this.assert(
  214. this._obj.calls.length < n,
  215. "expected " + this._obj + " to have been called fewer than #{exp} times but got #{act}",
  216. "expected " + this._obj + " to have been called at least #{exp} times but got #{act}",
  217. n,
  218. this._obj.calls.length
  219. );
  220. } else {
  221. _super.apply(this, arguments);
  222. }
  223. };
  224. }
  225. __name(below, "below");
  226. Assertion.overwriteMethod("below", below);
  227. Assertion.overwriteMethod("lt", below);
  228. function min(_super) {
  229. return function(n) {
  230. if (this._obj.__isSpy) {
  231. new Assertion(this._obj).to.be.spy;
  232. this.assert(
  233. this._obj.calls.length >= n,
  234. "expected " + this._obj + " to have been called at least #{exp} times but got #{act}",
  235. "expected " + this._obj + " to have been called fewer than #{exp} times but got #{act}",
  236. n,
  237. this._obj.calls.length
  238. );
  239. } else {
  240. _super.apply(this, arguments);
  241. }
  242. };
  243. }
  244. __name(min, "min");
  245. Assertion.overwriteMethod("min", min);
  246. Assertion.overwriteMethod("least", min);
  247. function max(_super) {
  248. return function(n) {
  249. if (this._obj.__isSpy) {
  250. new Assertion(this._obj).to.be.spy;
  251. this.assert(
  252. this._obj.calls.length <= n,
  253. "expected " + this._obj + " to have been called at most #{exp} times but got #{act}",
  254. "expected " + this._obj + " to have been called more than #{exp} times but got #{act}",
  255. n,
  256. this._obj.calls.length
  257. );
  258. } else {
  259. _super.apply(this, arguments);
  260. }
  261. };
  262. }
  263. __name(max, "max");
  264. Assertion.overwriteMethod("max", max);
  265. Assertion.overwriteMethod("most", max);
  266. }
  267. __name(chaiSpies, "chaiSpies");
  268. // src/create-spy.js
  269. function _parseSpyArgs(target, methodNameOrImpl, customImplForMethod) {
  270. let originalFn;
  271. let customImplementation;
  272. let isMethodSpy = false;
  273. let objToSpyOn;
  274. let methodName;
  275. let hasOwnMethod = false;
  276. const isLikelyFunctionSpy = typeof target === "function" && customImplForMethod === void 0;
  277. const isLikelyMethodSpy = typeof target === "object" && target !== null && typeof methodNameOrImpl === "string";
  278. if (isLikelyFunctionSpy) {
  279. originalFn = target;
  280. if (methodNameOrImpl !== void 0) {
  281. if (typeof methodNameOrImpl !== "function") {
  282. throw new TypeError(
  283. "When spying on a function, the second argument (custom implementation) must be a function if provided."
  284. );
  285. }
  286. customImplementation = methodNameOrImpl;
  287. }
  288. } else if (isLikelyMethodSpy) {
  289. methodName = methodNameOrImpl;
  290. objToSpyOn = target;
  291. isMethodSpy = true;
  292. hasOwnMethod = Object.prototype.hasOwnProperty.call(objToSpyOn, methodName);
  293. if (!(methodName in target)) {
  294. throw new TypeError(
  295. `Attempted to spy on a non-existent property: "${methodName}"`
  296. );
  297. }
  298. const propertyToSpyOn = target[methodName];
  299. if (typeof propertyToSpyOn !== "function") {
  300. throw new TypeError(
  301. `Attempted to spy on "${methodName}" which is not a function. It is a "${typeof propertyToSpyOn}".`
  302. );
  303. }
  304. originalFn = propertyToSpyOn;
  305. if (customImplForMethod !== void 0) {
  306. if (typeof customImplForMethod !== "function") {
  307. throw new TypeError(
  308. "When spying on a method, the third argument (custom implementation) must be a function if provided."
  309. );
  310. }
  311. customImplementation = customImplForMethod;
  312. }
  313. } else {
  314. if (target === null && methodNameOrImpl === void 0 && customImplForMethod === void 0) {
  315. throw new TypeError("Attempted to spy on null.");
  316. }
  317. if (methodNameOrImpl === void 0 && typeof target !== "function") {
  318. throw new TypeError(
  319. "Attempted to spy on a non-function value. To spy on an object method, you must provide the method name as the second argument."
  320. );
  321. }
  322. throw new Error(
  323. "Invalid arguments. Valid signatures:\n createSpy(function, [customImplementationFunction])\n createSpy(object, methodNameString, [customImplementationFunction])"
  324. );
  325. }
  326. return {
  327. originalFn,
  328. // определение функции для выполнения шпионом: либо
  329. // пользовательская реализация, либо оригинальная функция
  330. fnToExecute: customImplementation || originalFn,
  331. isMethodSpy,
  332. objToSpyOn,
  333. methodName,
  334. hasOwnMethod
  335. };
  336. }
  337. __name(_parseSpyArgs, "_parseSpyArgs");
  338. function createSpy(target = void 0, methodNameOrImpl = void 0, customImplForMethod = void 0) {
  339. if (typeof target === "undefined" && typeof methodNameOrImpl === "undefined" && typeof customImplForMethod === "undefined") {
  340. target = /* @__PURE__ */ __name(function() {
  341. }, "target");
  342. }
  343. const {
  344. originalFn,
  345. fnToExecute,
  346. isMethodSpy,
  347. objToSpyOn,
  348. methodName,
  349. hasOwnMethod
  350. } = _parseSpyArgs(target, methodNameOrImpl, customImplForMethod);
  351. const callLog = {
  352. count: 0,
  353. calls: []
  354. };
  355. const spy = /* @__PURE__ */ __name(function(...args) {
  356. callLog.count++;
  357. const callInfo = {
  358. // сохранение аргументов, с которыми
  359. // был вызван шпион
  360. args: [...args],
  361. // сохранение контекста (this)
  362. // вызова шпиона
  363. thisArg: this,
  364. returnValue: void 0,
  365. error: void 0
  366. };
  367. try {
  368. callInfo.returnValue = fnToExecute.apply(this, args);
  369. callLog.calls.push(callInfo);
  370. return callInfo.returnValue;
  371. } catch (e) {
  372. callInfo.error = e;
  373. callLog.calls.push(callInfo);
  374. throw e;
  375. }
  376. }, "spy");
  377. Object.defineProperty(spy, "calls", {
  378. get: /* @__PURE__ */ __name(() => callLog.calls, "get"),
  379. enumerable: true,
  380. configurable: false
  381. });
  382. Object.defineProperty(spy, "callCount", {
  383. get: /* @__PURE__ */ __name(() => callLog.count, "get"),
  384. enumerable: true,
  385. configurable: false
  386. });
  387. Object.defineProperty(spy, "called", {
  388. get: /* @__PURE__ */ __name(() => callLog.count > 0, "get"),
  389. enumerable: true,
  390. configurable: false
  391. });
  392. spy.restore = () => {
  393. if (isMethodSpy && objToSpyOn) {
  394. if (originalFn !== void 0) {
  395. if (hasOwnMethod) {
  396. objToSpyOn[methodName] = originalFn;
  397. } else {
  398. delete objToSpyOn[methodName];
  399. }
  400. }
  401. }
  402. callLog.count = 0;
  403. callLog.calls = [];
  404. };
  405. if (isMethodSpy && objToSpyOn) {
  406. objToSpyOn[methodName] = spy;
  407. }
  408. spy.__isSpy = true;
  409. return spy;
  410. }
  411. __name(createSpy, "createSpy");
  412. // src/create-sandbox.js
  413. var _Sandbox = class _Sandbox {
  414. /**
  415. * Constructor.
  416. */
  417. constructor() {
  418. this.spies = [];
  419. }
  420. /**
  421. * Создает шпиона для отдельной функции
  422. * или метода объекта и добавляет его в песочницу.
  423. *
  424. * @param {Function|object} [target]
  425. * @param {Function|string} [methodNameOrImpl]
  426. * @param {Function} [customImplForMethod]
  427. * @returns {Function}
  428. */
  429. on(target, methodNameOrImpl, customImplForMethod) {
  430. const spy = createSpy(target, methodNameOrImpl, customImplForMethod);
  431. this.spies.push(spy);
  432. return spy;
  433. }
  434. /**
  435. * Восстановление всех оригинальных методов объектов,
  436. * для которых были созданы шпионы в данной песочнице,
  437. * и сброс истории вызовов.
  438. *
  439. * @returns {this}
  440. */
  441. restore() {
  442. this.spies.forEach((spy) => spy.restore());
  443. this.spies = [];
  444. return this;
  445. }
  446. };
  447. __name(_Sandbox, "Sandbox");
  448. var Sandbox = _Sandbox;
  449. function createSandbox() {
  450. return new Sandbox();
  451. }
  452. __name(createSandbox, "createSandbox");
  453. // Annotate the CommonJS export names for ESM import in node:
  454. 0 && (module.exports = {
  455. Sandbox,
  456. chaiSpies,
  457. createSandbox,
  458. createSpy
  459. });