ololog
Version:
Logging. Colors. Terminals & browsers. Show call locations. Pretty prints objects, Errors, anything!
248 lines (163 loc) • 10.9 kB
JavaScript
;
/* ------------------------------------------------------------------------ */
const ansicolor = require ('ansicolor').nice
const ololog = require (process.env.OLOLOG_TEST_FILE)
/* ------------------------------------------------------------------------ */
const assert = (call, shouldBe = undefined, method = 'log') => {
let impl = console[method], args
try {
console[method] = function (...args_) { args = args_ }; call () // @hide
} catch (e) {
console[method] = impl
throw e
} finally {
console[method] = impl
}
if (shouldBe) {
if (!args) { throw new Error (`console.${method} hasn't been called!`) }
args.should.deep.equal (shouldBe)
} else {
if (args) { throw new Error (`console.${method} has been called (unexpectedly)!`) }
}
}
/* ------------------------------------------------------------------------ */
require ('chai').should ()
/* ------------------------------------------------------------------------ */
describe ('Ololog', () => {
const log = ololog.configure ({ locate: false, time: { format: 'iso', when: new Date ('2017-02-27T12:45:19.951Z') } })
it ('tokenization / line splitting / left-pad work', () => {
assert (() => log ('hello\n', 'world', 'line1\nline2\nline3\n'), ['hello\nworld line1\n line2\n line3\n'])
})
it ('location work', () => {
assert (function hello() {
ololog.configure ({ locate: true }).bgBrightCyan ('with location\n\n')
}, ['\u001b[106mwith location\u001b[49m \u001b[90m(hello @ test.js:49)\u001b[39m\n\u001b[106m\u001b[49m\n\u001b[106m\u001b[49m'])
assert (function hello() {
require ('./ololog') ('with location')
}, ['with location \u001b[90m(hello @ test.js:53)\u001b[39m'])
})
it ('indent work', () => {
assert (() => log.configure ({ indent: { level: 3 } }) ('line 1\nline 2\n\nline 3\n\n'), ['\t\t\tline 1\n\t\t\tline 2\n\t\t\t\n\t\t\tline 3\n\t\t\t\n\t\t\t'])
assert (() => log.indent (3) ('line 1\nline 2\n\nline 3\n\n'), ['\t\t\tline 1\n\t\t\tline 2\n\t\t\t\n\t\t\tline 3\n\t\t\t\n\t\t\t'])
})
it ('timestamps work', () => {
const date = new Date ('2017-02-27T12:45:19.951Z')
const log = ololog.configure ({ locate: false, time: { yes: true, when: date } })
assert (() => log ('foobar'), [`\u001b[90m${date.toLocaleString ()}\u001b[39m\tfoobar`])
assert (() => log.configure ({ time: { format: 'locale' }}) ('foobar'), [`\u001b[90m${date.toLocaleString ()}\u001b[39m\tfoobar`])
assert (() => log.configure ({ time: { format: 'iso' }}) ('foobar'), ["\u001b[90m2017-02-27T12:45:19.951Z\u001b[39m\tfoobar"])
assert (() => log.configure ({ time: { format: 'utc' }}) ('foobar'), ["\u001b[90mMon, 27 Feb 2017 12:45:19 GMT\u001b[39m\tfoobar"])
assert (() => log.configure ({ time: { format: null }}) ('foobar'), [`\u001b[90m${date.toString ()}\u001b[39m\tfoobar`])
assert (() => log.configure ({
time: {
format: 'locale',
locale: 'en-US',
options: { timeZone: 'America/Denver' }
}
}) ('foobar'), [`\u001b[90m${date.toLocaleString ('en-US', { timeZone: 'America/Denver' })}\u001b[39m\tfoobar`])
})
it ('timestamps are good with indent', () => {
const mylog = log.configure ({ time: { yes: true, print: when => when.toUTCString () + ' | ' } })
assert (() => mylog ('foo\nbar\nbaz'), [
'Mon, 27 Feb 2017 12:45:19 GMT | foo\n' +
' bar\n' +
' baz'])
assert (() => mylog.indent (2) ('foo\nbar\nbaz'), [
'Mon, 27 Feb 2017 12:45:19 GMT | \t\tfoo\n' +
' \t\tbar\n' +
' \t\tbaz'])
})
it ('ANSI styles work', () => {
assert (() => log.magenta ('hello' + 'bright'.bright), ['\u001b[35mhello\u001b[22m\u001b[1mbright\u001b[22m\u001b[39m'])
assert (() => log.bgBrightRed ('hello' + 'cyan'.cyan), ['\u001b[101mhello\u001b[36mcyan\u001b[39m\u001b[49m'])
assert (() => log.inverse ('hello' + 'underline_dim'.underline.dim), ['\u001b[7mhello\u001b[22m\u001b[2m\u001b[4munderline_dim\u001b[24m\u001b[22m\u001b[27m'])
})
it ('ANSI style chaining works', () => {
assert (() => log.red.bright ('foo'), ["\u001b[22m\u001b[1m\u001b[31mfoo\u001b[39m\u001b[22m"])
})
it ('string.ify work', () => {
assert (() => log.indent (2).green ({ foo: 42, bar: true, qux: 333, qux1: 444, qux2: 555, zap: '123457890' }),
["\t\t\u001b[32m{ foo: 42,\u001b[39m\n\t\t\u001b[32m bar: true,\u001b[39m\n\t\t\u001b[32m qux: 333,\u001b[39m\n\t\t\u001b[32m qux1: 444,\u001b[39m\n\t\t\u001b[32m qux2: 555,\u001b[39m\n\t\t\u001b[32m zap: \"123457890\" }\u001b[39m"])
assert (() => log.configure ({ stringify: { pretty: false } }) ({ foo: 42, bar: true, qux: 333, zap: '123457', long: [1,2,3,4,5,3,4,5,6,7] }),
["{ foo: 42, bar: true, qux: 333, zap: \"123457\", long: [1, 2, 3, 4, 5, 3, 4, 5, 6, 7] }"])
})
it ('trim works', () => {
assert (() => log.configure ({ trim: { max: 5 } }) ('1234567890', 'abcdefgh'), ["1234… abcd…"])
})
it ('.error / .warn / .info / .debug works', () => {
assert (() => log.error.red ('this goes to stderr'),
['\u001b[31m' + 'this goes to stderr' + '\u001b[39m'], 'error')
assert (() => log.warn.red ('this goes to console.warn'),
['\u001b[31m' + 'this goes to console.warn' + '\u001b[39m'], 'warn')
assert (() => log.info.red ('this goes to console.info'),
['\u001b[31m' + 'this goes to console.info' + '\u001b[39m'], 'info')
assert (() => log.debug.red ('this goes to console.debug'),
['\u001b[31m' + 'this goes to console.debug' + '\u001b[39m'], 'debug')
})
it ('formatting Error works', () => {
const e = new Error ('dummy error')
log.configure ({ '+render': text => {
text.indexOf ('[Error] dummy error\n').should.equal (0)
text.indexOf ('const e = new Error (\'dummy error\')').should.be.gt (0)
}}) (e)
})
it ('does not format Errors that are inside of objects', () => {
const e = new Error ('dummy error')
log.configure ({ '+render': text => {
text.should.equal ('{ inner: <Error: dummy error> }')
}}) ({ inner: e })
})
it ('formatting Assertions works', () => {
const err = (() => { try { 'foo'.should.equal ('bar') } catch (e) { return e } }) ()
log.configure ({ '+render': text => {
text.startsWith ("[AssertionError] expected 'foo' to equal 'bar'\n\n").should.equal (true)
text.includes ('actual: "foo"').should.equal (true)
text.includes ('expected: "bar"').should.equal (true)
text.includes ("const err = (() => { try { 'foo'.should.equal ('bar') } catch (e) { return e }").should.equal (true)
}}) (err)
})
it ('getting rendered text', () => {
log.before ('render') ({ foo: 42 }).should.be.equal ('{ foo: 42 }')
})
it ('returns its first argument', () => {
assert (() => log (42) .should.equal (42), ['42'])
assert (() => log (42, 'foo', 'bar').should.equal (42), ['42 foo bar'])
})
it ('smart newline handling', () => {
assert (() =>
log.bright.magenta (
'this is something:'.yellow, [ "595da547d9b22f23d8228643", "595da547d9b22f23d822863f", "595da547d9b22f23d8228641" ]), ['\u001b[35m\u001b[22m\u001b[1m\u001b[33mthis is something:\u001b[35m [ \"595da547d9b22f23d8228643\",\u001b[22m\u001b[39m\n\u001b[35m\u001b[22m\u001b[1m \"595da547d9b22f23d822863f\",\u001b[22m\u001b[39m\n\u001b[35m\u001b[22m\u001b[1m \"595da547d9b22f23d8228641\" ]\u001b[22m\u001b[39m'])
assert (() =>
log.bright (
'this is something:'.yellow, '[ "595da547d9b22f23d8228643",\n "595da547d9b22f23d822863f",\n "595da547d9b22f23d8228641" ]'.cyan,
'[ "595da547d9b22f23d8228643",\n "595da547d9b22f23d822863f",\n "595da547d9b22f23d8228641" ]'.green), ['\u001b[22m\u001b[1m\u001b[33mthis is something:\u001b[39m \u001b[36m[ \"595da547d9b22f23d8228643\",\u001b[39m\u001b[22m\n\u001b[22m\u001b[1m \u001b[36m \"595da547d9b22f23d822863f\",\u001b[39m\u001b[22m\n\u001b[22m\u001b[1m \u001b[36m \"595da547d9b22f23d8228641\" ]\u001b[39m \u001b[32m[ \"595da547d9b22f23d8228643\",\u001b[39m\u001b[22m\n\u001b[22m\u001b[1m \u001b[32m \"595da547d9b22f23d822863f\",\u001b[39m\u001b[22m\n\u001b[22m\u001b[1m \u001b[32m \"595da547d9b22f23d8228641\" ]\u001b[39m\u001b[22m'])
})
it ('noop', () => {
const noopLog = log.noop
assert (() => noopLog.bright.red ('foo', 'bar').should.equal ('foo'), undefined)
})
it ('location work on different platforms (debug)', () => {
const pipez = require ('pipez')
const StackTracey = require ('stacktracey')
const getStack = pipez ({ locate: () => (new StackTracey ().clean) })
const stack = getStack ()
console.log (stack.pretty)
})
if (String.prototype.padStart) { // no .padStart on Node 8 :(
it ('tag works', () => {
const log = ololog.configure ({ locate: false, tag: true })
assert (() => log ('a regular message'), [' \ta regular message'])
assert (() => log.info ('an info message'), ['\u001b[36m INFO\t\u001b[39man info message'], 'info')
assert (() => log.warn ('a warning'), ['\u001b[33m WARN\t\u001b[39ma warning'], 'warn')
assert (() => log.error ('an error'), ['\u001b[22m\u001b[1m\u001b[31m ERROR\t\u001b[39m\u001b[22man error'], 'error')
})
}
it ('custom stringifier works', () => {
// specifying custom printer
let log = ololog.configure ({ locate: false, stringify: { print (x) { return 'foo!' } } })
assert (() => log (42), ['foo!'])
// overriding the stringify stage completely
log = ololog.configure ({ locate: false, stringify (args, cfg) { return args.map (x => cfg.foo) } })
assert (() => log.configure ({ stringify: { foo: 'foo!' }}) (42, 24), ['foo! foo!'])
})
})