create-debugger.spec.js 26 KB

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