create-debugger.spec.js 26 KB

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