src/index.spec.js
import * as hiplog from '.';
/** @test {hiplog} */
describe('hiplog', () => {
it('exists', () => expect(hiplog).toBeDefined());
it("doesn't export a default", () => expect(hiplog.default).not.toBeDefined());
});
/** @test {defaultOptions} */
describe('defaultOptions', () => {
const { defaultOptions } = hiplog;
it('has 10 members', () => expect(Object.keys(defaultOptions).length).toBe(10));
it('has correct values', () => expect(defaultOptions).toMatchSnapshot());
describe('stream', () => {
const { stream } = defaultOptions;
it('is a function', () => expect(stream).toBeFunction());
/**
* Get the stream for a given level name.
*
* @param {string} level - Level name.
* @returns {Object} Stream returned by `stream(levels[level])`.
*/
const streamByLevel = level => stream(defaultOptions.levels.indexOf(level));
it('returns stdout when level is debug', () => expect(streamByLevel('debug')).toBe(process.stdout));
it('returns stdout when level is info', () => expect(streamByLevel('info')).toBe(process.stdout));
it('returns stdout when level is notice', () => expect(streamByLevel('notice')).toBe(process.stdout));
it('returns stderr when level is warning', () => expect(streamByLevel('warning')).toBe(process.stderr));
it('returns stderr when level is error', () => expect(streamByLevel('error')).toBe(process.stderr));
it('returns stderr when level is critical', () => expect(streamByLevel('critical')).toBe(process.stderr));
it('returns stderr when level is alert', () => expect(streamByLevel('alert')).toBe(process.stderr));
it('returns stderr when level is emergency', () => expect(streamByLevel('emergency')).toBe(process.stderr));
});
});
/** @test {Log} */
describe('Log', () => {
const { Log } = hiplog;
it('exists', () => expect(Log).toBeDefined());
describe('constructor', () => {
const { defaultOptions } = hiplog;
it('assigns defaults options', () => {
const log = new Log();
Object.keys(defaultOptions).forEach(
key => expect(log.options).toHaveProperty(key, defaultOptions[key]),
);
});
it('assigns given options', () => {
Object.keys(defaultOptions).forEach((key) => {
const value = key === 'levels' ? ['foo', 'bar'] : 'foo';
const log = new Log({ [key]: value });
expect(log.options).toHaveProperty(key, value);
});
});
it('create one function member for each level', () => {
const levels = ['foo', 'bar', 'baz'];
const log = new Log({ levels });
levels.forEach(level => expect(log[level]).toBeFunction());
});
});
describe('write', () => {
it('is called with given level by created function members', () => {
const levels = ['foo', 'bar'];
const log = new Log({ levels });
log.write = jest.fn();
levels.forEach((level, i) => {
log[level]('baz', 'foobar');
expect(log.write).toHaveBeenCalledTimes(1);
expect(log.write).toHaveBeenCalledWith(i, ['baz', 'foobar']);
log.write.mockClear();
});
});
it('writes whatever is returned by buildMessage() to stream', () => {
const levels = ['foo'];
const log = new Log({ levels, level: levels[0] });
log.buildMessage = () => 'bar';
log.options.stream = { write: jest.fn() };
log.foo();
expect(log.options.stream.write).toHaveBeenCalledTimes(1);
expect(log.options.stream.write).toHaveBeenCalledWith('bar');
});
it("doesn't write anything to stream when level is below message level", () => {
const log = new Log({ level: 'info' });
log.buildMessage = level => log.options.levels[level];
log.options.stream = { write: jest.fn() };
log.debug();
log.info();
expect(log.options.stream.write).toHaveBeenCalledTimes(1);
expect(log.options.stream.write).toHaveBeenCalledWith('info');
});
});
describe('buildMessage', () => {
it('builds simple messages', () => {
const log = new Log({ level: 'debug', displayTime: false });
const messsages = [
['debug', 'messages to debug an application'],
['info', 'a purely informational message'],
['notice', 'a normal but significant condition'],
['warning', 'warning condition'],
['error', 'error condition'],
['critical', 'the system is in critical condition'],
['alert', 'action must be taken immediately'],
['emergency', 'system is unusable'],
].map(([level, ...parts]) => log.buildMessage(log.options.levels.indexOf(level), parts));
expect(messsages).toMatchSnapshot();
});
it('builds messages with multiple parts', () => {
const log = new Log({ level: 'debug', displayTime: false });
const messsages = [
['debug', 'messages', 'to', 'debug', 'an', 'application'],
['info', 'a', 'purely', 'informational', 'message'],
['notice', 'a', 'normal', 'but', 'significant', 'condition'],
['warning', 'warning', 'condition'],
['error', 'error', 'condition'],
['critical', 'the', 'system', 'is', 'in', 'critical', 'condition'],
['alert', 'action', 'must', 'be', 'taken', 'immediately'],
['emergency', 'system', 'is', 'unusable'],
].map(([level, ...parts]) => log.buildMessage(log.options.levels.indexOf(level), parts));
expect(messsages).toMatchSnapshot();
});
it('builds messages with non-string parts', () => {
const log = new Log({ displayTime: false });
const level = log.options.levels.indexOf('info');
const messsages = [
['boolean:', true, false],
['null:', null],
['undefined:', undefined],
['number:', 1, 2, 3],
['regexp:', /foo/],
['array:', ['foo', 'bar']],
['object:', { foo: 'bar' }],
].map(parts => log.buildMessage(level, parts));
expect(messsages).toMatchSnapshot();
});
it('builds messages with big objects', () => {
const log = new Log({ displayTime: false });
const circular = {};
circular.inner = circular;
const bigObject = {
null: null,
undefined,
integer: 123,
boolean: true,
string: 'Hello',
funtion: function myFunction() {}, // eslint-disable-line require-jsdoc
circular,
array: ['one', 'two', 'three', 'four'],
};
const messsage = log.buildMessage(log.options.levels.indexOf('info'), [bigObject]);
expect(messsage).toMatchSnapshot();
});
it('builds messages with errors', () => {
const log = new Log({ displayTime: false });
let error;
try {
throw new Error('LEROOOOOOOOOOOY JENKINS!!!!!!!!!!!!!!!!!!!!!!!!!');
} catch (e) {
error = e;
}
const messsage = log.buildMessage(log.options.levels.indexOf('error'), [error]);
expect(messsage).toMatchSnapshot();
});
});
});
/** @test {fromEnv} */
describe('fromEnv', () => {
const { fromEnv, defaultOptions } = hiplog;
it('uses default options when `NODE_ENV` set to "production"', () => {
const { options } = fromEnv({}, { NODE_ENV: 'production' });
expect(options).toEqual(defaultOptions);
});
it('uses development options when `NODE_ENV` set to "development"', () => {
const { options } = fromEnv({}, { NODE_ENV: 'development' });
expect(options).toHaveProperty('displayTime', false);
expect(options).toMatchSnapshot();
});
it('uses development options when `NODE_ENV` is not set', () => {
const { options } = fromEnv({}, {});
expect(options).toEqual(fromEnv({}, { NODE_ENV: 'development' }).options);
});
it('uses test options when `NODE_ENV` set to "test"', () => {
const { options } = fromEnv({}, { NODE_ENV: 'test' });
expect(options).toHaveProperty('level', 'critical');
expect(options).toHaveProperty('displayTime', false);
expect(options).toMatchSnapshot();
});
describe('LOG_TIME', () => {
it('sets displayTime to default value when not set', () => expect(fromEnv({}, {}).options).toHaveProperty('displayTime', defaultOptions.displayTime));
it('sets displayTime to true when set to 1', () => expect(fromEnv({}, { LOG_TIME: '1' }).options).toHaveProperty('displayTime', true));
it('sets displayTime to true when set to true', () => expect(fromEnv({}, { LOG_TIME: 'true' }).options).toHaveProperty('displayTime', true));
it('sets displayTime to false when set to 0', () => expect(fromEnv({}, { LOG_TIME: '0' }).options).toHaveProperty('displayTime', false));
it('sets displayTime to false when set to false', () => expect(fromEnv({}, { LOG_TIME: 'false' }).options).toHaveProperty('displayTime', false));
});
});