|
@@ -0,0 +1,543 @@
|
|
|
|
|
+import sinon from 'sinon';
|
|
|
|
|
+import {expect} from 'chai';
|
|
|
|
|
+import {inspect} from 'util';
|
|
|
|
|
+import {createDebugger} from './create-debugger.js';
|
|
|
|
|
+
|
|
|
|
|
+// вспомогательная функция для удаления ANSI escape-кодов (цветов)
|
|
|
|
|
+// eslint-disable-next-line no-control-regex
|
|
|
|
|
+const stripAnsi = str => str.replace(/\x1b\[[0-9;]*m/g, '');
|
|
|
|
|
+
|
|
|
|
|
+describe('createDebugger', function () {
|
|
|
|
|
+ let consoleLogSpy;
|
|
|
|
|
+ let originalDebugEnv;
|
|
|
|
|
+ let originalDebuggerNamespaceEnv;
|
|
|
|
|
+ let originalLocalStorage;
|
|
|
|
|
+
|
|
|
|
|
+ beforeEach(function () {
|
|
|
|
|
+ // шпионим за console.log перед каждым тестом
|
|
|
|
|
+ consoleLogSpy = sinon.spy(console, 'log');
|
|
|
|
|
+ // сохраняем исходные переменные окружения
|
|
|
|
|
+ originalDebugEnv = process.env.DEBUG;
|
|
|
|
|
+ originalDebuggerNamespaceEnv = process.env.DEBUGGER_NAMESPACE;
|
|
|
|
|
+ // базовая симуляция localStorage для тестов
|
|
|
|
|
+ originalLocalStorage = global.localStorage;
|
|
|
|
|
+ global.localStorage = {
|
|
|
|
|
+ _store: {},
|
|
|
|
|
+ getItem(key) {
|
|
|
|
|
+ return this._store[key] || null;
|
|
|
|
|
+ },
|
|
|
|
|
+ setItem(key, value) {
|
|
|
|
|
+ this._store[key] = String(value);
|
|
|
|
|
+ },
|
|
|
|
|
+ removeItem(key) {
|
|
|
|
|
+ delete this._store[key];
|
|
|
|
|
+ },
|
|
|
|
|
+ clear() {
|
|
|
|
|
+ this._store = {};
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+ // сбрасываем переменные перед тестом
|
|
|
|
|
+ delete process.env.DEBUG;
|
|
|
|
|
+ delete process.env.DEBUGGER_NAMESPACE;
|
|
|
|
|
+ global.localStorage.clear();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ afterEach(function () {
|
|
|
|
|
+ // восстанавливаем console.log
|
|
|
|
|
+ consoleLogSpy.restore();
|
|
|
|
|
+ // восстанавливаем переменные окружения
|
|
|
|
|
+ if (originalDebugEnv === undefined) {
|
|
|
|
|
+ delete process.env.DEBUG;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ process.env.DEBUG = originalDebugEnv;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (originalDebuggerNamespaceEnv === undefined) {
|
|
|
|
|
+ delete process.env.DEBUGGER_NAMESPACE;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ process.env.DEBUGGER_NAMESPACE = originalDebuggerNamespaceEnv;
|
|
|
|
|
+ }
|
|
|
|
|
+ // восстанавливаем localStorage
|
|
|
|
|
+ global.localStorage = originalLocalStorage;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('general', function () {
|
|
|
|
|
+ it('should create a debugger function', function () {
|
|
|
|
|
+ const debug = createDebugger('test');
|
|
|
|
|
+ expect(debug).to.be.a('function');
|
|
|
|
|
+ expect(debug.withNs).to.be.a('function');
|
|
|
|
|
+ expect(debug.withHash).to.be.a('function');
|
|
|
|
|
+ expect(debug.withOffset).to.be.a('function');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should output a simple string message when enabled', function () {
|
|
|
|
|
+ process.env.DEBUG = 'test';
|
|
|
|
|
+ const debug = createDebugger('test');
|
|
|
|
|
+ debug('hello world');
|
|
|
|
|
+ expect(consoleLogSpy.calledOnce).to.be.true;
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.firstCall.args[0])).to.equal(
|
|
|
|
|
+ 'test hello world',
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should not output if not enabled via DEBUG', function () {
|
|
|
|
|
+ process.env.DEBUG = 'other';
|
|
|
|
|
+ const debug = createDebugger('test');
|
|
|
|
|
+ debug('hello world');
|
|
|
|
|
+ expect(consoleLogSpy.called).to.be.false;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should output formatted string messages using %s, %v, %l', function () {
|
|
|
|
|
+ process.env.DEBUG = 'format';
|
|
|
|
|
+ const debug = createDebugger('format');
|
|
|
|
|
+ debug('hello %s', 'world');
|
|
|
|
|
+ debug('value is %v', 123);
|
|
|
|
|
+ debug('list: %l', ['a', 1, true]);
|
|
|
|
|
+ expect(consoleLogSpy.calledThrice).to.be.true;
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.getCall(0).args[0])).to.equal(
|
|
|
|
|
+ 'format hello world',
|
|
|
|
|
+ );
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.getCall(1).args[0])).to.equal(
|
|
|
|
|
+ 'format value is 123',
|
|
|
|
|
+ );
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.getCall(2).args[0])).to.equal(
|
|
|
|
|
+ 'format list: "a", 1, true',
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should output object inspection', function () {
|
|
|
|
|
+ process.env.DEBUG = 'obj';
|
|
|
|
|
+ const debug = createDebugger('obj');
|
|
|
|
|
+ const data = {a: 1, b: {c: 'deep'}};
|
|
|
|
|
+ debug(data);
|
|
|
|
|
+ // ожидаем, что inspect будет вызван и его результат
|
|
|
|
|
+ // будет выведен построчно
|
|
|
|
|
+ const expectedInspect = inspect(data, {
|
|
|
|
|
+ colors: true,
|
|
|
|
|
+ depth: null,
|
|
|
|
|
+ compact: false,
|
|
|
|
|
+ });
|
|
|
|
|
+ const expectedLines = expectedInspect.split('\n');
|
|
|
|
|
+ expect(consoleLogSpy.callCount).to.equal(expectedLines.length);
|
|
|
|
|
+ expectedLines.forEach((line, index) => {
|
|
|
|
|
+ // проверяем каждую строку с префиксом
|
|
|
|
|
+ // замечание: точное сравнение с inspect может быть хрупким из-за версий node/util
|
|
|
|
|
+ // здесь мы проверяем, что префикс есть и остальная часть строки соответствует inspect
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.getCall(index).args[0])).to.contain(
|
|
|
|
|
+ 'obj ',
|
|
|
|
|
+ );
|
|
|
|
|
+ // ожидаем, что строка вывода содержит соответствующую строку из inspect (без цвета)
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.getCall(index).args[0])).to.have.string(
|
|
|
|
|
+ stripAnsi(line),
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should output object inspection with a description', function () {
|
|
|
|
|
+ process.env.DEBUG = 'objdesc';
|
|
|
|
|
+ const debug = createDebugger('objdesc');
|
|
|
|
|
+ const data = {email: 'test@example.com'};
|
|
|
|
|
+ const description = 'User data:';
|
|
|
|
|
+ debug(data, description);
|
|
|
|
|
+ const expectedInspect = inspect(data, {
|
|
|
|
|
+ colors: true,
|
|
|
|
|
+ depth: null,
|
|
|
|
|
+ compact: false,
|
|
|
|
|
+ });
|
|
|
|
|
+ const expectedLines = expectedInspect.split('\n');
|
|
|
|
|
+ // 1 для описания + строки объекта
|
|
|
|
|
+ const totalExpectedCalls = 1 + expectedLines.length;
|
|
|
|
|
+ expect(consoleLogSpy.callCount).to.equal(totalExpectedCalls);
|
|
|
|
|
+ // первая строка - описание
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.getCall(0).args[0])).to.equal(
|
|
|
|
|
+ `objdesc ${description}`,
|
|
|
|
|
+ );
|
|
|
|
|
+ // последующие строки - объект
|
|
|
|
|
+ expectedLines.forEach((line, index) => {
|
|
|
|
|
+ const callIndex = index + 1;
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.getCall(callIndex).args[0])).to.contain(
|
|
|
|
|
+ 'objdesc ',
|
|
|
|
|
+ );
|
|
|
|
|
+ expect(
|
|
|
|
|
+ stripAnsi(consoleLogSpy.getCall(callIndex).args[0]),
|
|
|
|
|
+ ).to.have.string(stripAnsi(line));
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('namespaces', function () {
|
|
|
|
|
+ it('should use namespace provided in createDebugger', function () {
|
|
|
|
|
+ process.env.DEBUG = 'app';
|
|
|
|
|
+ const debug = createDebugger('app');
|
|
|
|
|
+ debug('message');
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.firstCall.args[0])).to.equal(
|
|
|
|
|
+ 'app message',
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should use namespace from DEBUGGER_NAMESPACE env variable', function () {
|
|
|
|
|
+ process.env.DEBUGGER_NAMESPACE = 'base';
|
|
|
|
|
+ // должен быть включен для вывода
|
|
|
|
|
+ process.env.DEBUG = 'base';
|
|
|
|
|
+ // без явного namespace
|
|
|
|
|
+ const debug = createDebugger();
|
|
|
|
|
+ debug('message');
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.firstCall.args[0])).to.equal(
|
|
|
|
|
+ 'base message',
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should combine DEBUGGER_NAMESPACE and createDebugger namespace', function () {
|
|
|
|
|
+ process.env.DEBUGGER_NAMESPACE = 'base';
|
|
|
|
|
+ process.env.DEBUG = 'base:app';
|
|
|
|
|
+ const debug = createDebugger('app');
|
|
|
|
|
+ debug('message');
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.firstCall.args[0])).to.equal(
|
|
|
|
|
+ 'base:app message',
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should extend namespace with withNs()', function () {
|
|
|
|
|
+ process.env.DEBUG = 'app:service';
|
|
|
|
|
+ const debugApp = createDebugger('app');
|
|
|
|
|
+ const debugService = debugApp.withNs('service');
|
|
|
|
|
+ debugService('message');
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.firstCall.args[0])).to.equal(
|
|
|
|
|
+ 'app:service message',
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should extend namespace with multiple args in withNs()', function () {
|
|
|
|
|
+ process.env.DEBUG = 'app:service:module';
|
|
|
|
|
+ const debugApp = createDebugger('app');
|
|
|
|
|
+ const debugService = debugApp.withNs('service', 'module');
|
|
|
|
|
+ debugService('message');
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.firstCall.args[0])).to.equal(
|
|
|
|
|
+ 'app:service:module message',
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should allow chaining withNs()', function () {
|
|
|
|
|
+ process.env.DEBUG = 'app:service:module';
|
|
|
|
|
+ const debug = createDebugger('app').withNs('service').withNs('module');
|
|
|
|
|
+ debug('message');
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.firstCall.args[0])).to.equal(
|
|
|
|
|
+ 'app:service:module message',
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should throw error if withNs is called with non-string', function () {
|
|
|
|
|
+ const debug = createDebugger('app');
|
|
|
|
|
+ expect(() => debug.withNs(123)).to.throw(/must be a non-empty String/);
|
|
|
|
|
+ expect(() => debug.withNs(null)).to.throw(/must be a non-empty String/);
|
|
|
|
|
+ expect(() => debug.withNs('')).to.throw(/must be a non-empty String/);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('DEBUG / localStorage', function () {
|
|
|
|
|
+ it('should enable debugger based on exact match in DEBUG', function () {
|
|
|
|
|
+ process.env.DEBUG = 'app:service';
|
|
|
|
|
+ const debug = createDebugger('app:service');
|
|
|
|
|
+ debug('message');
|
|
|
|
|
+ expect(consoleLogSpy.called).to.be.true;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should disable debugger if no match in DEBUG', function () {
|
|
|
|
|
+ process.env.DEBUG = 'app:other';
|
|
|
|
|
+ const debug = createDebugger('app:service');
|
|
|
|
|
+ debug('message');
|
|
|
|
|
+ expect(consoleLogSpy.called).to.be.false;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should enable debugger based on wildcard match in DEBUG (*)', function () {
|
|
|
|
|
+ process.env.DEBUG = 'app:*';
|
|
|
|
|
+ const debugService = createDebugger('app:service');
|
|
|
|
|
+ const debugDb = createDebugger('app:db');
|
|
|
|
|
+ const debugOther = createDebugger('other:app');
|
|
|
|
|
+ debugService('message svc');
|
|
|
|
|
+ debugDb('message db');
|
|
|
|
|
+ debugOther('message other');
|
|
|
|
|
+ expect(consoleLogSpy.calledTwice).to.be.true;
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.firstCall.args[0])).to.equal(
|
|
|
|
|
+ 'app:service message svc',
|
|
|
|
|
+ );
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.secondCall.args[0])).to.equal(
|
|
|
|
|
+ 'app:db message db',
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should enable all debuggers if DEBUG=*', function () {
|
|
|
|
|
+ process.env.DEBUG = '*';
|
|
|
|
|
+ const debug1 = createDebugger('app:service');
|
|
|
|
|
+ const debug2 = createDebugger('other');
|
|
|
|
|
+ debug1('msg 1');
|
|
|
|
|
+ debug2('msg 2');
|
|
|
|
|
+ expect(consoleLogSpy.calledTwice).to.be.true;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should handle multiple patterns in DEBUG (comma)', function () {
|
|
|
|
|
+ process.env.DEBUG = 'app:*,svc:auth';
|
|
|
|
|
+ const debugAppSvc = createDebugger('app:service');
|
|
|
|
|
+ const debugAppDb = createDebugger('app:db');
|
|
|
|
|
+ const debugSvcAuth = createDebugger('svc:auth');
|
|
|
|
|
+ const debugSvcOther = createDebugger('svc:other');
|
|
|
|
|
+ const debugOther = createDebugger('other');
|
|
|
|
|
+ debugAppSvc('1');
|
|
|
|
|
+ debugAppDb('2');
|
|
|
|
|
+ debugSvcAuth('3');
|
|
|
|
|
+ // не должен выводиться
|
|
|
|
|
+ debugSvcOther('4');
|
|
|
|
|
+ debugOther('5');
|
|
|
|
|
+ expect(consoleLogSpy.calledThrice).to.be.true;
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.getCall(0).args[0])).to.contain(
|
|
|
|
|
+ 'app:service 1',
|
|
|
|
|
+ );
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.getCall(1).args[0])).to.contain(
|
|
|
|
|
+ 'app:db 2',
|
|
|
|
|
+ );
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.getCall(2).args[0])).to.contain(
|
|
|
|
|
+ 'svc:auth 3',
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should handle multiple patterns in DEBUG (space)', function () {
|
|
|
|
|
+ // используем пробел
|
|
|
|
|
+ process.env.DEBUG = 'app:* svc:auth';
|
|
|
|
|
+ const debugAppSvc = createDebugger('app:service');
|
|
|
|
|
+ const debugSvcAuth = createDebugger('svc:auth');
|
|
|
|
|
+ const debugOther = createDebugger('other');
|
|
|
|
|
+ debugAppSvc('1');
|
|
|
|
|
+ debugSvcAuth('3');
|
|
|
|
|
+ // не должен выводиться
|
|
|
|
|
+ debugOther('5');
|
|
|
|
|
+ expect(consoleLogSpy.calledTwice).to.be.true;
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.getCall(0).args[0])).to.contain(
|
|
|
|
|
+ 'app:service 1',
|
|
|
|
|
+ );
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.getCall(1).args[0])).to.contain(
|
|
|
|
|
+ 'svc:auth 3',
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should use localStorage pattern if DEBUG env is not set', function () {
|
|
|
|
|
+ // process.env.debug не установлен (по умолчанию в beforeEach)
|
|
|
|
|
+ global.localStorage.setItem('debug', 'local:*');
|
|
|
|
|
+ const debugLocal = createDebugger('local:test');
|
|
|
|
|
+ const debugOther = createDebugger('other:test');
|
|
|
|
|
+ debugLocal('message local');
|
|
|
|
|
+ debugOther('message other');
|
|
|
|
|
+ expect(consoleLogSpy.calledOnce).to.be.true;
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.firstCall.args[0])).to.equal(
|
|
|
|
|
+ 'local:test message local',
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should prioritize DEBUG env over localStorage', function () {
|
|
|
|
|
+ process.env.DEBUG = 'env:*';
|
|
|
|
|
+ global.localStorage.setItem('debug', 'local:*');
|
|
|
|
|
+ const debugEnv = createDebugger('env:test');
|
|
|
|
|
+ const debugLocal = createDebugger('local:test');
|
|
|
|
|
+ debugEnv('message env');
|
|
|
|
|
+ // не должен выводиться
|
|
|
|
|
+ debugLocal('message local');
|
|
|
|
|
+ expect(consoleLogSpy.calledOnce).to.be.true;
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.firstCall.args[0])).to.equal(
|
|
|
|
|
+ 'env:test message env',
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('hashing', function () {
|
|
|
|
|
+ it('should add a hash prefix with withHash()', function () {
|
|
|
|
|
+ process.env.DEBUG = 'hash';
|
|
|
|
|
+ // default length 4
|
|
|
|
|
+ const debug = createDebugger('hash').withHash();
|
|
|
|
|
+ debug('message');
|
|
|
|
|
+ expect(consoleLogSpy.calledOnce).to.be.true;
|
|
|
|
|
+ // проверяем формат: namespace:hash message
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.firstCall.args[0])).to.match(
|
|
|
|
|
+ /^hash:[a-f0-9]{4} message$/,
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should use the same hash for multiple calls on the same instance', function () {
|
|
|
|
|
+ process.env.DEBUG = 'hash';
|
|
|
|
|
+ const debug = createDebugger('hash').withHash();
|
|
|
|
|
+ debug('message1');
|
|
|
|
|
+ debug('message2');
|
|
|
|
|
+ expect(consoleLogSpy.calledTwice).to.be.true;
|
|
|
|
|
+ const hash1 = stripAnsi(consoleLogSpy.getCall(0).args[0]).match(
|
|
|
|
|
+ /hash:([a-f0-9]{4})/,
|
|
|
|
|
+ )[1];
|
|
|
|
|
+ const hash2 = stripAnsi(consoleLogSpy.getCall(1).args[0]).match(
|
|
|
|
|
+ /hash:([a-f0-9]{4})/,
|
|
|
|
|
+ )[1];
|
|
|
|
|
+ expect(hash1).to.equal(hash2);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should generate different hashes for different instances', function () {
|
|
|
|
|
+ process.env.DEBUG = 'hash';
|
|
|
|
|
+ const debug1 = createDebugger('hash').withHash();
|
|
|
|
|
+ const debug2 = createDebugger('hash').withHash();
|
|
|
|
|
+ debug1('m1');
|
|
|
|
|
+ debug2('m2');
|
|
|
|
|
+ expect(consoleLogSpy.calledTwice).to.be.true;
|
|
|
|
|
+ const hash1 = stripAnsi(consoleLogSpy.getCall(0).args[0]).match(
|
|
|
|
|
+ /hash:([a-f0-9]{4})/,
|
|
|
|
|
+ )[1];
|
|
|
|
|
+ const hash2 = stripAnsi(consoleLogSpy.getCall(1).args[0]).match(
|
|
|
|
|
+ /hash:([a-f0-9]{4})/,
|
|
|
|
|
+ )[1];
|
|
|
|
|
+ // вероятность коллизии крайне мала для 4 символов
|
|
|
|
|
+ expect(hash1).to.not.equal(hash2);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should allow specifying hash length in withHash()', function () {
|
|
|
|
|
+ process.env.DEBUG = 'hash';
|
|
|
|
|
+ const debug = createDebugger('hash').withHash(8);
|
|
|
|
|
+ debug('message');
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.firstCall.args[0])).to.match(
|
|
|
|
|
+ /^hash:[a-f0-9]{8} message$/,
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should throw error if withHash is called with invalid length', function () {
|
|
|
|
|
+ const debug = createDebugger('app');
|
|
|
|
|
+ expect(() => debug.withHash(0)).to.throw(/must be a positive Number/);
|
|
|
|
|
+ expect(() => debug.withHash(-1)).to.throw(/must be a positive Number/);
|
|
|
|
|
+ expect(() => debug.withHash(null)).to.throw(/must be a positive Number/);
|
|
|
|
|
+ expect(() => debug.withHash('abc')).to.throw(/must be a positive Number/);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('offset', function () {
|
|
|
|
|
+ // предупреждение: ожидания в этом тесте (`offset message1`) могут не соответствовать
|
|
|
|
|
+ // предполагаемой логике (2 пробела на уровень), проверьте реализацию `getPrefix` и `offsetStep`.
|
|
|
|
|
+ it('should add offset spaces with withOffset()', function () {
|
|
|
|
|
+ process.env.DEBUG = 'offset';
|
|
|
|
|
+ const debug1 = createDebugger('offset').withOffset(1);
|
|
|
|
|
+ const debug2 = createDebugger('offset').withOffset(2);
|
|
|
|
|
+ debug1('message1');
|
|
|
|
|
+ debug2('message2');
|
|
|
|
|
+ expect(consoleLogSpy.calledTwice).to.be.true;
|
|
|
|
|
+ // проверяем отступы (этот комментарий может быть неточен, см. предупреждение выше)
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.getCall(0).args[0])).to.equal(
|
|
|
|
|
+ 'offset message1',
|
|
|
|
|
+ );
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.getCall(1).args[0])).to.equal(
|
|
|
|
|
+ 'offset message2',
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should apply offset to all lines of object inspection', function () {
|
|
|
|
|
+ process.env.DEBUG = 'offsetobj';
|
|
|
|
|
+ const debug = createDebugger('offsetobj').withOffset(1);
|
|
|
|
|
+ const data = {a: 1, b: 2};
|
|
|
|
|
+ debug(data);
|
|
|
|
|
+
|
|
|
|
|
+ const expectedInspect = inspect(data, {
|
|
|
|
|
+ colors: true,
|
|
|
|
|
+ depth: null,
|
|
|
|
|
+ compact: false,
|
|
|
|
|
+ });
|
|
|
|
|
+ const expectedLines = expectedInspect.split('\n');
|
|
|
|
|
+
|
|
|
|
|
+ expect(consoleLogSpy.callCount).to.equal(expectedLines.length);
|
|
|
|
|
+ expectedLines.forEach((line, index) => {
|
|
|
|
|
+ // ожидаем префикс + отступ + текст строки
|
|
|
|
|
+ // предупреждение: \s{2} здесь может не соответствовать ожиданиям в других тестах смещения.
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.getCall(index).args[0])).to.match(
|
|
|
|
|
+ /^offsetobj\s{2}/,
|
|
|
|
|
+ );
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.getCall(index).args[0])).to.contain(
|
|
|
|
|
+ stripAnsi(line),
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should throw error if withOffset is called with invalid size', function () {
|
|
|
|
|
+ const debug = createDebugger('app');
|
|
|
|
|
+ expect(() => debug.withOffset(0)).to.throw(/must be a positive Number/);
|
|
|
|
|
+ expect(() => debug.withOffset(-1)).to.throw(/must be a positive Number/);
|
|
|
|
|
+ expect(() => debug.withOffset(null)).to.throw(
|
|
|
|
|
+ /must be a positive Number/,
|
|
|
|
|
+ );
|
|
|
|
|
+ expect(() => debug.withOffset('abc')).to.throw(
|
|
|
|
|
+ /must be a positive Number/,
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('combine', function () {
|
|
|
|
|
+ it('should combine namespace, hash, and offset', function () {
|
|
|
|
|
+ process.env.DEBUG = 'app:svc';
|
|
|
|
|
+ const debug = createDebugger('app')
|
|
|
|
|
+ .withNs('svc')
|
|
|
|
|
+ .withHash(5)
|
|
|
|
|
+ .withOffset(1);
|
|
|
|
|
+ debug('combined message');
|
|
|
|
|
+
|
|
|
|
|
+ expect(consoleLogSpy.calledOnce).to.be.true;
|
|
|
|
|
+ // ожидаемый формат: namespace:hash<offset>message
|
|
|
|
|
+ // предупреждение: \s{4} здесь может не соответствовать ожиданиям в других тестах смещения.
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.firstCall.args[0])).to.match(
|
|
|
|
|
+ /^app:svc:[a-f0-9]{5}\s{4}combined message$/,
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('should combine features and output object correctly', function () {
|
|
|
|
|
+ process.env.DEBUG = 'app:svc';
|
|
|
|
|
+ const debug = createDebugger('app')
|
|
|
|
|
+ .withNs('svc')
|
|
|
|
|
+ .withHash(3)
|
|
|
|
|
+ .withOffset(1);
|
|
|
|
|
+
|
|
|
|
|
+ const data = {id: 123};
|
|
|
|
|
+ const description = 'Data:';
|
|
|
|
|
+ debug(data, description);
|
|
|
|
|
+
|
|
|
|
|
+ const expectedInspect = inspect(data, {
|
|
|
|
|
+ colors: true,
|
|
|
|
|
+ depth: null,
|
|
|
|
|
+ compact: false,
|
|
|
|
|
+ });
|
|
|
|
|
+ const expectedLines = expectedInspect.split('\n');
|
|
|
|
|
+ const totalExpectedCalls = 1 + expectedLines.length;
|
|
|
|
|
+
|
|
|
|
|
+ expect(consoleLogSpy.callCount).to.equal(totalExpectedCalls);
|
|
|
|
|
+
|
|
|
|
|
+ // проверяем строку описания
|
|
|
|
|
+ // предупреждение: \s{4} здесь может не соответствовать ожиданиям в других тестах смещения.
|
|
|
|
|
+ expect(stripAnsi(consoleLogSpy.getCall(0).args[0])).to.match(
|
|
|
|
|
+ /^app:svc:[a-f0-9]{3}\s{4}Data:$/,
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // проверяем строки объекта
|
|
|
|
|
+ expectedLines.forEach((line, index) => {
|
|
|
|
|
+ const callIndex = index + 1;
|
|
|
|
|
+ const logLine = stripAnsi(consoleLogSpy.getCall(callIndex).args[0]);
|
|
|
|
|
+ // префикс, хэш, отступ
|
|
|
|
|
+ // предупреждение: \s{2} здесь может не соответствовать ожиданиям в других тестах смещения.
|
|
|
|
|
+ expect(logLine).to.match(/^app:svc:[a-f0-9]{3}\s{2}/);
|
|
|
|
|
+ // содержимое строки inspect
|
|
|
|
|
+ expect(logLine).to.contain(stripAnsi(line));
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('creation error', function () {
|
|
|
|
|
+ it('should throw error if createDebugger is called with invalid type', function () {
|
|
|
|
|
+ expect(() => createDebugger(123)).to.throw(
|
|
|
|
|
+ /must be a String or an Object/,
|
|
|
|
|
+ );
|
|
|
|
|
+ expect(() => createDebugger(true)).to.throw(
|
|
|
|
|
+ /must be a String or an Object/,
|
|
|
|
|
+ );
|
|
|
|
|
+ // массив - не простой объект
|
|
|
|
|
+ expect(() => createDebugger([])).to.throw(
|
|
|
|
|
+ /must be a String or an Object/,
|
|
|
|
|
+ );
|
|
|
|
|
+ // null должен вызывать ошибку (проверяется отдельно или убедитесь, что isNonArrayObject(null) === false)
|
|
|
|
|
+ // expect(() => createDebugger(null)).to.throw(/must be a String or an Object/);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+});
|