create-debugger.spec.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. import {expect} from 'chai';
  2. import {createSpy} from '@e22m4u/js-spy';
  3. import {stripAnsi} from './utils/strip-ansi.js';
  4. import {createDebugger} from './create-debugger.js';
  5. import {DEFAULT_OFFSET_STEP_SPACES} from './create-debugger.js';
  6. describe('createDebugger', function () {
  7. let consoleLogSpy;
  8. let originalDebugEnv;
  9. let originalDebuggerNamespaceEnv;
  10. let originalLocalStorage;
  11. beforeEach(function () {
  12. // шпионим за console.log перед каждым тестом
  13. consoleLogSpy = createSpy(console, 'log');
  14. // сохраняем исходные переменные окружения
  15. originalDebugEnv = process.env.DEBUG;
  16. originalDebuggerNamespaceEnv = process.env.DEBUGGER_NAMESPACE;
  17. // базовая симуляция localStorage для тестов
  18. originalLocalStorage = global.localStorage;
  19. global.localStorage = {
  20. _store: {},
  21. get length() {
  22. return Object.keys(this._store).length;
  23. },
  24. key(num) {
  25. return Object.keys(this._store)[num] || null;
  26. },
  27. getItem(key) {
  28. return this._store[key] || null;
  29. },
  30. setItem(key, value) {
  31. this._store[key] = String(value);
  32. },
  33. removeItem(key) {
  34. delete this._store[key];
  35. },
  36. clear() {
  37. this._store = {};
  38. },
  39. };
  40. // сбрасываем переменные перед тестом
  41. delete process.env.DEBUG;
  42. delete process.env.DEBUGGER_NAMESPACE;
  43. global.localStorage.clear();
  44. });
  45. afterEach(function () {
  46. // восстанавливаем console.log
  47. consoleLogSpy.restore();
  48. // восстанавливаем переменные окружения
  49. if (originalDebugEnv === undefined) {
  50. delete process.env.DEBUG;
  51. } else {
  52. process.env.DEBUG = originalDebugEnv;
  53. }
  54. if (originalDebuggerNamespaceEnv === undefined) {
  55. delete process.env.DEBUGGER_NAMESPACE;
  56. } else {
  57. process.env.DEBUGGER_NAMESPACE = originalDebuggerNamespaceEnv;
  58. }
  59. // восстанавливаем localStorage
  60. global.localStorage = originalLocalStorage;
  61. });
  62. describe('general', function () {
  63. it('should create a debugger function', function () {
  64. const debug = createDebugger('test');
  65. expect(debug).to.be.a('function');
  66. expect(debug.withNs).to.be.a('function');
  67. expect(debug.withHash).to.be.a('function');
  68. expect(debug.withOffset).to.be.a('function');
  69. });
  70. it('should set the "state" property with DebuggerState object', function () {
  71. const debug = createDebugger('test');
  72. const state = debug.state;
  73. console.log(state);
  74. expect(typeof state).to.be.eq('object');
  75. expect(state.nsSegments).to.be.eql(['test']);
  76. });
  77. it('should output a simple string message when enabled', function () {
  78. process.env.DEBUG = 'test';
  79. const debug = createDebugger('test');
  80. debug('hello world');
  81. expect(consoleLogSpy.callCount).to.equal(1);
  82. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  83. 'test hello world',
  84. );
  85. });
  86. it('should not output if not enabled via DEBUG', function () {
  87. process.env.DEBUG = 'other';
  88. const debug = createDebugger('test');
  89. debug('hello world');
  90. expect(consoleLogSpy.isCalled).to.be.false;
  91. });
  92. it('should output formatted string messages using %s, %v, %l', function () {
  93. process.env.DEBUG = 'format';
  94. const debug = createDebugger('format');
  95. debug('hello %s', 'world');
  96. debug('value is %v', 123);
  97. debug('list: %l', ['a', 1, true]);
  98. expect(consoleLogSpy.callCount).to.equal(3);
  99. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  100. 'format hello world',
  101. );
  102. expect(stripAnsi(consoleLogSpy.calls[1].args[0])).to.equal(
  103. 'format value is 123',
  104. );
  105. expect(stripAnsi(consoleLogSpy.calls[2].args[0])).to.equal(
  106. 'format list: "a", 1, true',
  107. );
  108. });
  109. });
  110. describe('namespaces', function () {
  111. it('should use namespace provided in createDebugger', function () {
  112. process.env.DEBUG = 'app';
  113. const debug = createDebugger('app');
  114. debug('message');
  115. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal('app message');
  116. });
  117. it('should use multiple namespace segments provided in createDebugger', function () {
  118. process.env.DEBUG = 'app:service:module';
  119. const debug = createDebugger('app', 'service', 'module');
  120. debug('multi segment message');
  121. expect(consoleLogSpy.callCount).to.equal(1);
  122. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  123. 'app:service:module multi segment message',
  124. );
  125. });
  126. it('should handle subsequent segments even if the first arg is the only namespace part', function () {
  127. process.env.DEBUG = 'service:module';
  128. const debug = createDebugger('service', 'module');
  129. debug('segments only message');
  130. expect(consoleLogSpy.callCount).to.equal(1);
  131. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  132. 'service:module segments only message',
  133. );
  134. });
  135. // --- 2. Влияние DEBUGGER_NAMESPACE при создании ---
  136. it('should use namespace from DEBUGGER_NAMESPACE env variable', function () {
  137. process.env.DEBUGGER_NAMESPACE = 'base';
  138. process.env.DEBUG = 'base';
  139. const debug = createDebugger();
  140. debug('message');
  141. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  142. 'base message',
  143. );
  144. });
  145. it('should combine DEBUGGER_NAMESPACE and createDebugger namespace', function () {
  146. process.env.DEBUGGER_NAMESPACE = 'base';
  147. process.env.DEBUG = 'base:app';
  148. const debug = createDebugger('app');
  149. debug('message');
  150. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  151. 'base:app message',
  152. );
  153. });
  154. it('should combine DEBUGGER_NAMESPACE and multiple createDebugger segments', function () {
  155. process.env.DEBUGGER_NAMESPACE = 'base';
  156. process.env.DEBUG = 'base:app:svc';
  157. const debug = createDebugger('app', 'svc');
  158. debug('env plus multi segment message');
  159. expect(consoleLogSpy.callCount).to.equal(1);
  160. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  161. 'base:app:svc env plus multi segment message',
  162. );
  163. });
  164. // --- 3. Модификация (withNs) ---
  165. it('should extend namespace with withNs()', function () {
  166. process.env.DEBUG = 'app:service';
  167. const debugApp = createDebugger('app');
  168. const debugService = debugApp.withNs('service');
  169. debugService('message');
  170. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  171. 'app:service message',
  172. );
  173. });
  174. it('should extend namespace with multiple args in withNs()', function () {
  175. process.env.DEBUG = 'app:service:module';
  176. const debugApp = createDebugger('app');
  177. const debugService = debugApp.withNs('service', 'module');
  178. debugService('message');
  179. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  180. 'app:service:module message',
  181. );
  182. });
  183. it('should allow chaining withNs()', function () {
  184. process.env.DEBUG = 'app:service:module';
  185. const debug = createDebugger('app').withNs('service').withNs('module');
  186. debug('message');
  187. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  188. 'app:service:module message',
  189. );
  190. });
  191. it('should extend namespace with withNs() after creating with multiple segments', function () {
  192. process.env.DEBUG = 'app:svc:mod';
  193. const debugBase = createDebugger('app', 'svc');
  194. const debugMod = debugBase.withNs('mod');
  195. debugMod('multi create then withNs');
  196. expect(consoleLogSpy.callCount).to.equal(1);
  197. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  198. 'app:svc:mod multi create then withNs',
  199. );
  200. });
  201. // --- 4. Взаимодействие DEBUGGER_NAMESPACE с модификаторами (Тесты на фикс дублирования) ---
  202. it('should NOT duplicate DEBUGGER_NAMESPACE when using withNs', function () {
  203. process.env.DEBUGGER_NAMESPACE = 'envNs';
  204. process.env.DEBUG = 'envNs:service';
  205. const debugBase = createDebugger();
  206. const debugService = debugBase.withNs('service');
  207. debugService('message');
  208. expect(consoleLogSpy.callCount).to.equal(1);
  209. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  210. 'envNs:service message',
  211. );
  212. });
  213. it('should correctly combine DEBUGGER_NAMESPACE, initial segments, and withNs without duplicates', function () {
  214. process.env.DEBUGGER_NAMESPACE = 'envNs';
  215. process.env.DEBUG = 'envNs:init1:init2:added';
  216. const debugBase = createDebugger('init1', 'init2');
  217. const debugAdded = debugBase.withNs('added');
  218. debugAdded('combined message');
  219. expect(consoleLogSpy.callCount).to.equal(1);
  220. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  221. 'envNs:init1:init2:added combined message',
  222. );
  223. });
  224. it('should work correctly with withNs when DEBUGGER_NAMESPACE is NOT set', function () {
  225. expect(process.env.DEBUGGER_NAMESPACE).to.be.undefined;
  226. process.env.DEBUG = 'app:service';
  227. const debugBase = createDebugger('app');
  228. const debugService = debugBase.withNs('service');
  229. debugService('message');
  230. expect(consoleLogSpy.callCount).to.equal(1);
  231. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  232. 'app:service message',
  233. );
  234. });
  235. it('should add namespace prefix for each line if the given message is multiline', function () {
  236. process.env.DEBUG = 'app:service';
  237. const debug = createDebugger('app', 'service');
  238. debug('firstLine\nsecondLine');
  239. expect(consoleLogSpy.callCount).to.equal(2);
  240. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.be.eq(
  241. 'app:service firstLine',
  242. );
  243. expect(stripAnsi(consoleLogSpy.calls[1].args[0])).to.be.eq(
  244. 'app:service secondLine',
  245. );
  246. });
  247. it('should throw error if createDebugger is called with invalid subsequent segment type', function () {
  248. // @ts-ignore
  249. expect(() => createDebugger('app', 'valid', 123)).to.throw(
  250. /Namespace segment must be a non-empty String/,
  251. );
  252. expect(() => createDebugger('app', 'valid', null)).to.throw(
  253. /Namespace segment must be a non-empty String/,
  254. );
  255. expect(() => createDebugger('app', 'valid', '')).to.throw(
  256. /Namespace segment must be a non-empty String/,
  257. );
  258. expect(() => createDebugger('app', '')).to.throw(
  259. /Namespace segment must be a non-empty String/,
  260. );
  261. });
  262. it('should throw error if withNs is called with non-string', function () {
  263. const debug = createDebugger('app');
  264. // @ts-ignore
  265. expect(() => debug.withNs(123)).to.throw(
  266. /Debugger namespace must be a non-empty String/,
  267. );
  268. expect(() => debug.withNs(null)).to.throw(
  269. /Debugger namespace must be a non-empty String/,
  270. );
  271. expect(() => debug.withNs('')).to.throw(
  272. /Debugger namespace must be a non-empty String/,
  273. );
  274. });
  275. });
  276. describe('environment', function () {
  277. it('should enable debugger based on exact match in DEBUG', function () {
  278. process.env.DEBUG = 'app:service';
  279. const debug = createDebugger('app:service');
  280. debug('message');
  281. expect(consoleLogSpy.isCalled).to.be.true;
  282. });
  283. it('should disable debugger if no match in DEBUG', function () {
  284. process.env.DEBUG = 'app:other';
  285. const debug = createDebugger('app:service');
  286. debug('message');
  287. expect(consoleLogSpy.isCalled).to.be.false;
  288. });
  289. it('should enable debugger based on wildcard match in DEBUG (*)', function () {
  290. process.env.DEBUG = 'app:*';
  291. const debugService = createDebugger('app:service');
  292. const debugDb = createDebugger('app:db');
  293. const debugOther = createDebugger('other:app');
  294. debugService('message svc');
  295. debugDb('message db');
  296. debugOther('message other');
  297. expect(consoleLogSpy.callCount).to.equal(2);
  298. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  299. 'app:service message svc',
  300. );
  301. expect(stripAnsi(consoleLogSpy.calls[1].args[0])).to.equal(
  302. 'app:db message db',
  303. );
  304. });
  305. it('should enable all debuggers if DEBUG=*', function () {
  306. process.env.DEBUG = '*';
  307. const debug1 = createDebugger('app:service');
  308. const debug2 = createDebugger('other');
  309. debug1('msg 1');
  310. debug2('msg 2');
  311. expect(consoleLogSpy.callCount).to.equal(2);
  312. });
  313. it('should handle multiple patterns in DEBUG (comma)', function () {
  314. process.env.DEBUG = 'app:*,svc:auth';
  315. const debugAppSvc = createDebugger('app:service');
  316. const debugAppDb = createDebugger('app:db');
  317. const debugSvcAuth = createDebugger('svc:auth');
  318. const debugSvcOther = createDebugger('svc:other');
  319. const debugOther = createDebugger('other');
  320. debugAppSvc('1');
  321. debugAppDb('2');
  322. debugSvcAuth('3');
  323. debugSvcOther('4');
  324. debugOther('5');
  325. expect(consoleLogSpy.callCount).to.equal(3);
  326. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.contain(
  327. 'app:service 1',
  328. );
  329. expect(stripAnsi(consoleLogSpy.calls[1].args[0])).to.contain('app:db 2');
  330. expect(stripAnsi(consoleLogSpy.calls[2].args[0])).to.contain(
  331. 'svc:auth 3',
  332. );
  333. });
  334. it('should handle multiple patterns in DEBUG (space)', function () {
  335. process.env.DEBUG = 'app:* svc:auth';
  336. const debugAppSvc = createDebugger('app:service');
  337. const debugSvcAuth = createDebugger('svc:auth');
  338. const debugOther = createDebugger('other');
  339. debugAppSvc('1');
  340. debugSvcAuth('3');
  341. debugOther('5');
  342. expect(consoleLogSpy.callCount).to.equal(2);
  343. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.contain(
  344. 'app:service 1',
  345. );
  346. expect(stripAnsi(consoleLogSpy.calls[1].args[0])).to.contain(
  347. 'svc:auth 3',
  348. );
  349. });
  350. it('should use localStorage pattern if DEBUG env is not set', function () {
  351. global.localStorage.setItem('debug', 'local:*');
  352. const debugLocal = createDebugger('local:test');
  353. const debugOther = createDebugger('other:test');
  354. debugLocal('message local');
  355. debugOther('message other');
  356. expect(consoleLogSpy.callCount).to.equal(1);
  357. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  358. 'local:test message local',
  359. );
  360. });
  361. it('should prioritize DEBUG env over localStorage', function () {
  362. process.env.DEBUG = 'env:*';
  363. global.localStorage.setItem('debug', 'local:*');
  364. const debugEnv = createDebugger('env:test');
  365. const debugLocal = createDebugger('local:test');
  366. debugEnv('message env');
  367. debugLocal('message local');
  368. expect(consoleLogSpy.callCount).to.equal(1);
  369. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  370. 'env:test message env',
  371. );
  372. });
  373. it('should exclude namespace of DEBUGGER_NAMESPACE in withoutEnvNs()', function () {
  374. process.env.DEBUG = '*';
  375. process.env.DEBUGGER_NAMESPACE = 'myApp';
  376. const debug1 = createDebugger();
  377. const debug2 = debug1.withoutEnvNs();
  378. debug1('message 1');
  379. expect(consoleLogSpy.callCount).to.equal(1);
  380. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  381. 'myApp message 1',
  382. );
  383. debug2('message 2');
  384. expect(consoleLogSpy.callCount).to.equal(2);
  385. expect(stripAnsi(consoleLogSpy.calls[1].args[0])).to.equal('message 2');
  386. });
  387. });
  388. describe('hashing', function () {
  389. it('should add a hash prefix with withHash()', function () {
  390. process.env.DEBUG = 'hash';
  391. const debug = createDebugger('hash').withHash();
  392. debug('message');
  393. expect(consoleLogSpy.callCount).to.equal(1);
  394. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.match(
  395. /^hash:[a-f0-9]{4} message$/,
  396. );
  397. });
  398. it('should use the same hash for multiple calls on the same instance', function () {
  399. process.env.DEBUG = 'hash';
  400. const debug = createDebugger('hash').withHash();
  401. debug('message1');
  402. debug('message2');
  403. expect(consoleLogSpy.callCount).to.equal(2);
  404. const hash1 = stripAnsi(consoleLogSpy.calls[0].args[0]).match(
  405. /hash:([a-f0-9]{4})/,
  406. )[1];
  407. const hash2 = stripAnsi(consoleLogSpy.calls[1].args[0]).match(
  408. /hash:([a-f0-9]{4})/,
  409. )[1];
  410. expect(hash1).to.equal(hash2);
  411. });
  412. it('should generate different hashes for different instances', function () {
  413. process.env.DEBUG = 'hash';
  414. const debug1 = createDebugger('hash').withHash();
  415. const debug2 = createDebugger('hash').withHash();
  416. debug1('m1');
  417. debug2('m2');
  418. expect(consoleLogSpy.callCount).to.equal(2);
  419. const hash1 = stripAnsi(consoleLogSpy.calls[0].args[0]).match(
  420. /hash:([a-f0-9]{4})/,
  421. )[1];
  422. const hash2 = stripAnsi(consoleLogSpy.calls[1].args[0]).match(
  423. /hash:([a-f0-9]{4})/,
  424. )[1];
  425. expect(hash1).to.not.equal(hash2);
  426. });
  427. it('should allow specifying hash length in withHash()', function () {
  428. process.env.DEBUG = 'hash';
  429. const debug = createDebugger('hash').withHash(8);
  430. debug('message');
  431. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.match(
  432. /^hash:[a-f0-9]{8} message$/,
  433. );
  434. });
  435. it('should throw error if withHash is called with invalid length', function () {
  436. const debug = createDebugger('app');
  437. expect(() => debug.withHash(0)).to.throw(/must be a positive Number/);
  438. expect(() => debug.withHash(-1)).to.throw(/must be a positive Number/);
  439. expect(() => debug.withHash(null)).to.throw(/must be a positive Number/);
  440. // @ts-ignore
  441. expect(() => debug.withHash('abc')).to.throw(/must be a positive Number/);
  442. });
  443. it('should NOT duplicate DEBUGGER_NAMESPACE when using withHash', function () {
  444. process.env.DEBUGGER_NAMESPACE = 'envNs';
  445. process.env.DEBUG = 'envNs';
  446. const debugBase = createDebugger();
  447. const debugHashed = debugBase.withHash();
  448. debugHashed('message');
  449. expect(consoleLogSpy.callCount).to.equal(1);
  450. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.match(
  451. /^envNs:[a-f0-9]{4} message$/,
  452. );
  453. });
  454. });
  455. describe('offset', function () {
  456. it('should add offset spaces with withOffset()', function () {
  457. process.env.DEBUG = 'offset';
  458. // предполагая, что offsetStep = ' '
  459. const debug1 = createDebugger('offset').withOffset(1);
  460. const debug2 = createDebugger('offset').withOffset(2);
  461. debug1('message1');
  462. debug2('message2');
  463. expect(consoleLogSpy.callCount).to.equal(2);
  464. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.match(
  465. new RegExp(
  466. '^offset\\s{' + (DEFAULT_OFFSET_STEP_SPACES + 1) + '}message1$',
  467. ),
  468. );
  469. expect(stripAnsi(consoleLogSpy.calls[1].args[0])).to.match(
  470. new RegExp(
  471. '^offset\\s{' + (DEFAULT_OFFSET_STEP_SPACES * 2 + 1) + '}message2$',
  472. ),
  473. );
  474. });
  475. it('should throw error if withOffset is called with invalid size', function () {
  476. const debug = createDebugger('app');
  477. expect(() => debug.withOffset(0)).to.throw(/must be a positive Number/);
  478. expect(() => debug.withOffset(-1)).to.throw(/must be a positive Number/);
  479. expect(() => debug.withOffset(null)).to.throw(
  480. /must be a positive Number/,
  481. );
  482. // @ts-ignore
  483. expect(() => debug.withOffset('abc')).to.throw(
  484. /must be a positive Number/,
  485. );
  486. });
  487. it('should NOT duplicate DEBUGGER_NAMESPACE when using withOffset', function () {
  488. process.env.DEBUGGER_NAMESPACE = 'envNs';
  489. process.env.DEBUG = 'envNs';
  490. const debugBase = createDebugger();
  491. const debugOffset = debugBase.withOffset(1);
  492. debugOffset('message');
  493. expect(consoleLogSpy.callCount).to.equal(1);
  494. // предполагая, что offsetStep = ' '
  495. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.match(
  496. new RegExp(
  497. '^envNs\\s{' + (DEFAULT_OFFSET_STEP_SPACES + 1) + '}message$',
  498. ),
  499. );
  500. });
  501. });
  502. describe('combine', function () {
  503. it('should combine namespace, hash, and offset', function () {
  504. process.env.DEBUG = 'app:svc';
  505. const debug = createDebugger('app')
  506. .withNs('svc')
  507. .withHash(5)
  508. .withOffset(1);
  509. debug('combined message');
  510. expect(consoleLogSpy.callCount).to.equal(1);
  511. // предполагая, что offsetStep = ' '
  512. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.match(
  513. new RegExp(
  514. '^app:svc:[a-f0-9]{5}\\s{' +
  515. (DEFAULT_OFFSET_STEP_SPACES + 1) +
  516. '}combined message$',
  517. ),
  518. );
  519. });
  520. });
  521. describe('creation error', function () {
  522. it('should throw error if createDebugger is called with invalid type', function () {
  523. // @ts-ignore
  524. expect(() => createDebugger(123)).to.throw(
  525. /must be a String or an Object/,
  526. );
  527. // @ts-ignore
  528. expect(() => createDebugger(true)).to.throw(
  529. /must be a String or an Object/,
  530. );
  531. // @ts-ignore
  532. expect(() => createDebugger([])).to.throw(
  533. /must be a String or an Object/,
  534. );
  535. });
  536. });
  537. describe('inspect method', function () {
  538. it('should not output if debugger is disabled', function () {
  539. process.env.DEBUG = 'other';
  540. const debug = createDebugger('app');
  541. debug.inspect({a: 1});
  542. expect(consoleLogSpy.isCalled).to.be.false;
  543. });
  544. it('should output a colorized dump of a single argument', function () {
  545. process.env.DEBUG = 'app';
  546. const debug = createDebugger('app');
  547. const data = {user: {id: 1, name: 'test'}};
  548. debug.inspect(data);
  549. // createColorizedDump выводит многострочный результат,
  550. // поэтому проверяем несколько вызовов console.log
  551. expect(consoleLogSpy.callCount).to.be.above(1);
  552. const firstLine = stripAnsi(consoleLogSpy.calls[0].args[0]);
  553. const secondLine = stripAnsi(consoleLogSpy.calls[1].args[0]);
  554. expect(firstLine).to.equal('app {');
  555. // проверяем вторую строку с отступом,
  556. // который добавляет createColorizedDump
  557. expect(secondLine).to.match(/^app\s{3}user: \{$/);
  558. });
  559. it('should use the first argument as a description for the next ones', function () {
  560. process.env.DEBUG = 'app';
  561. const debug = createDebugger('app');
  562. const data = {id: 123};
  563. debug.inspect('User data:', data);
  564. expect(consoleLogSpy.callCount).to.be.above(1);
  565. const descriptionLine = stripAnsi(consoleLogSpy.calls[0].args[0]);
  566. const dumpLine = stripAnsi(consoleLogSpy.calls[1].args[0]);
  567. expect(descriptionLine).to.equal('app User data:');
  568. // проверка, что к дампу добавлен дополнительный отступ
  569. const expectedInternalOffset = ' '.repeat(DEFAULT_OFFSET_STEP_SPACES);
  570. expect(dumpLine).to.equal(`app ${expectedInternalOffset}{`);
  571. });
  572. describe('combine', function () {
  573. it('should include namespace from withNs() in inspect output', function () {
  574. process.env.DEBUG = 'app:svc';
  575. const debug = createDebugger('app').withNs('svc');
  576. debug.inspect('Data:', {a: 1});
  577. const descriptionLine = stripAnsi(consoleLogSpy.calls[0].args[0]);
  578. expect(descriptionLine).to.equal('app:svc Data:');
  579. });
  580. it('should include hash from withHash() in inspect output', function () {
  581. process.env.DEBUG = 'app';
  582. const debug = createDebugger('app').withHash(6);
  583. debug.inspect({a: 1});
  584. const firstLine = stripAnsi(consoleLogSpy.calls[0].args[0]);
  585. expect(firstLine).to.match(/^app:[a-f0-9]{6} \{$/);
  586. });
  587. it('should apply offset from withOffset() to inspect output', function () {
  588. process.env.DEBUG = 'app';
  589. const debug = createDebugger('app').withOffset(2);
  590. debug.inspect('My Data:', {a: 1});
  591. const offsetSpaces = ' '.repeat(DEFAULT_OFFSET_STEP_SPACES * 2);
  592. const internalOffset = ' '.repeat(DEFAULT_OFFSET_STEP_SPACES);
  593. const descriptionLine = stripAnsi(consoleLogSpy.calls[0].args[0]);
  594. const dumpLine = stripAnsi(consoleLogSpy.calls[1].args[0]);
  595. expect(descriptionLine).to.equal(`app${offsetSpaces} My Data:`);
  596. // проверка применения отступа к дампу (префикс + внутренний отступ)
  597. expect(dumpLine).to.equal(`app${offsetSpaces} ${internalOffset}{`);
  598. });
  599. it('should respect withoutEnvNs() in inspect output', function () {
  600. process.env.DEBUG = '*';
  601. process.env.DEBUGGER_NAMESPACE = 'myApp';
  602. const debugWithEnv = createDebugger('api');
  603. const debugWithoutEnv = debugWithEnv.withoutEnvNs();
  604. // вызов inspect на исходном отладчике
  605. debugWithEnv.inspect('With env');
  606. expect(stripAnsi(consoleLogSpy.calls[0].args[0])).to.equal(
  607. "myApp:api 'With env'",
  608. );
  609. // вызов на новом отладчике
  610. debugWithoutEnv.inspect('Without env');
  611. expect(stripAnsi(consoleLogSpy.calls[1].args[0])).to.equal(
  612. "api 'Without env'",
  613. );
  614. });
  615. it('should combine all modifiers for inspect output', function () {
  616. process.env.DEBUG = '*';
  617. process.env.DEBUGGER_NAMESPACE = 'myApp';
  618. const debug = createDebugger('api')
  619. .withNs('users')
  620. .withHash(4)
  621. .withOffset(1)
  622. .withoutEnvNs();
  623. debug.inspect('User List:', [{id: 1}, {id: 2}]);
  624. const offsetSpaces = ' '.repeat(DEFAULT_OFFSET_STEP_SPACES * 1);
  625. const internalOffset = ' '.repeat(DEFAULT_OFFSET_STEP_SPACES);
  626. const descriptionLine = stripAnsi(consoleLogSpy.calls[0].args[0]);
  627. const dumpLine = stripAnsi(consoleLogSpy.calls[1].args[0]);
  628. // проверка отсутствия DEBUGGER_NAMESPACE
  629. expect(descriptionLine).to.not.contain('myApp');
  630. // проверка всей строки с помощью регулярного выражения
  631. expect(descriptionLine).to.match(
  632. new RegExp(`^api:users:[a-f0-9]{4}${offsetSpaces} User List:$`),
  633. );
  634. expect(dumpLine).to.match(
  635. new RegExp(
  636. `^api:users:[a-f0-9]{4}${offsetSpaces} ${internalOffset}\\[$`,
  637. ),
  638. );
  639. });
  640. });
  641. });
  642. });