UNPKG

toofast

Version:

The Node.js performance testing tool with unit-test-like API.

168 lines (167 loc) 6.03 kB
import { bold, dim, green, red, yellow } from 'kleur/colors'; import rl from 'readline'; const MESSAGE_PADDING = ' '; const MESSAGE_PENDING_0 = dim('○ '); const MESSAGE_PENDING_25 = dim('◔ '); const MESSAGE_PENDING_50 = dim('◑ '); const MESSAGE_PENDING_75 = dim('◕ '); const MESSAGE_PENDING_ERROR = red('○ '); const MESSAGE_WARMUP = yellow('● '); const MESSAGE_ERROR = red('● '); const MESSAGE_SUCCESS = green('● '); const NEW_LINE = '\n'; export function createNodeLogger() { let depth = 0; let testName; let errorMessage; let measureIndex = 0; let hasMargin = false; const blocks = []; return message => { switch (message.type) { case 'blockStart': blocks.push(message.children); break; case 'blockEnd': blocks.pop(); break; case 'fatalError': print(NEW_LINE + red(message.errorMessage) + NEW_LINE); break; case 'describeStart': print(NEW_LINE + MESSAGE_PADDING.repeat(depth) + bold(message.name) + NEW_LINE); hasMargin = false; ++depth; break; case 'describeEnd': if (errorMessage !== undefined) { print(red(errorMessage)); } hasMargin = true; --depth; break; case 'testStart': const maxTestNameLength = blocks[blocks.length - 1].reduce((length, child) => (child.type === 'test' ? Math.max(length, child.name.length) : length), 0); testName = message.name.padEnd(maxTestNameLength); measureIndex = 0; clearLine(); print((hasMargin ? NEW_LINE : '') + MESSAGE_PADDING.repeat(depth) + MESSAGE_PENDING_0 + testName + MESSAGE_PADDING); hasMargin = false; break; case 'testEnd': clearLine(); print(MESSAGE_PADDING.repeat(depth) + (errorMessage !== undefined ? MESSAGE_ERROR : MESSAGE_SUCCESS) + testName + MESSAGE_PADDING + formatMeasurement(message.durationStats.hz, 'Hz') + formatRme(message.durationStats.rme) + MESSAGE_PADDING + formatMeasurement(message.memoryStats.mean, 'B') + formatRme(message.memoryStats.rme) + NEW_LINE + (errorMessage !== undefined ? red(errorMessage) + NEW_LINE : '')); hasMargin = errorMessage !== undefined; errorMessage = undefined; break; case 'error': errorMessage = message.errorMessage; break; case 'measureWarmupStart': clearLine(); print(MESSAGE_PADDING.repeat(depth) + MESSAGE_WARMUP + testName + MESSAGE_PADDING + formatMeasureIndex(measureIndex, blocks[blocks.length - 1].length) + MESSAGE_PADDING); break; case 'measureWarmupEnd': break; case 'measureStart': break; case 'measureEnd': ++measureIndex; break; case 'measureError': if (errorMessage === undefined) { errorMessage = message.errorMessage; } break; case 'measureProgress': const { percentage } = message; clearLine(); print(MESSAGE_PADDING.repeat(depth) + (errorMessage !== undefined ? MESSAGE_PENDING_ERROR : percentage < 0.25 ? MESSAGE_PENDING_0 : percentage < 0.5 ? MESSAGE_PENDING_25 : percentage < 0.75 ? MESSAGE_PENDING_50 : MESSAGE_PENDING_75) + testName + MESSAGE_PADDING + formatMeasureIndex(measureIndex, blocks[blocks.length - 1].length) + formatPercent(percentage)); break; } }; } const decimalFormat = new Intl.NumberFormat('en', { minimumFractionDigits: 1, maximumFractionDigits: 1, useGrouping: true, }); const integerFormat = new Intl.NumberFormat('en', { minimumFractionDigits: 0, maximumFractionDigits: 0, useGrouping: true, }); const percentFormat = new Intl.NumberFormat('en', { minimumFractionDigits: 0, maximumFractionDigits: 0, style: 'percent', }); const rmeFormat = new Intl.NumberFormat('en', { minimumFractionDigits: 0, maximumFractionDigits: 2, style: 'percent', }); function formatMeasureIndex(index, count) { return count === 1 ? '' : dim(integerFormat.format(index + 1) + '/' + integerFormat.format(count)); } function formatMeasurement(value, unit) { let unitLabel = ' ' + unit + ' '; if (value > 1000) { value /= 1000; unitLabel = ' k' + unit; } if (value > 1000) { value /= 1000; unitLabel = ' M' + unit; } if (value > 1000) { value /= 1000; unitLabel = ' G' + unit; } const valueLabel = decimalFormat .format(value) .replace(/\D?\d+$/, dim) .padStart(5 + dim('').length); return valueLabel + unitLabel; } function formatPercent(value) { return percentFormat.format(value).padStart(5); } function formatRme(rme) { return dim(' ± ' + rmeFormat.format(rme).padEnd(6)); } function clearLine() { rl.clearLine(process.stdout, 0); rl.cursorTo(process.stdout, 0); } function print(message) { process.stdout.write(message); }