diff --git a/packages/vitest/src/node/reporters/renderers/dotRenderer.ts b/packages/vitest/src/node/reporters/renderers/dotRenderer.ts index fd0964ce2e1a8..abe570b3a4c01 100644 --- a/packages/vitest/src/node/reporters/renderers/dotRenderer.ts +++ b/packages/vitest/src/node/reporters/renderers/dotRenderer.ts @@ -7,25 +7,58 @@ export interface DotRendererOptions { logger: Logger } -const check = c.green('·') -const cross = c.red('x') -const pending = c.yellow('*') -const skip = c.dim(c.gray('-')) +interface Icon { char: string; color: (char: string) => string } -function render(tasks: Task[]) { +const check: Icon = { char: '·', color: c.green } +const cross: Icon = { char: 'x', color: c.red } +const pending: Icon = { char: '*', color: c.yellow } +const skip: Icon = { char: '-', color: (char: string) => c.dim(c.gray(char)) } + +function getIcon(task: Task) { + if (task.mode === 'skip' || task.mode === 'todo') + return skip + switch (task.result?.state) { + case 'pass': + return check + case 'fail': + return cross + default: + return pending + } +} + +function render(tasks: Task[]): string { const all = getTests(tasks) - return all.map((i) => { - if (i.mode === 'skip' || i.mode === 'todo') - return skip - switch (i.result?.state) { - case 'pass': - return check - case 'fail': - return cross - default: - return pending + const texts: string[] = [] + + // The log-update uses various ANSI helper utilities, e.g. ansi-warp, ansi-slice, + // when printing. Passing it hundreds of single characters containing ANSI codes reduces + // performances. We can optimize it by reducing amount of ANSI codes, e.g. by coloring + // multiple tasks at once instead of each task separately. + let currentIcon = pending + let currentTasks = 0 + + for (const task of all) { + const icon = getIcon(task) + const isLast = all.indexOf(task) === all.length - 1 + + if (!isLast && icon === currentIcon) { + currentTasks++ + continue } - }).join('') + + // Task mode/state has changed, handle earlier ones first + texts.push(currentIcon.color(currentIcon.char.repeat(currentTasks))) + + // Start tracking new group + currentTasks = 1 + currentIcon = icon + + if (isLast) + texts.push(currentIcon.color(currentIcon.char.repeat(currentTasks))) + } + + return texts.join('') } export const createDotRenderer = (_tasks: Task[], options: DotRendererOptions) => { @@ -42,12 +75,11 @@ export const createDotRenderer = (_tasks: Task[], options: DotRendererOptions) = start() { if (timer) return this - timer = setInterval(update, 200) + timer = setInterval(update, 16) return this }, update(_tasks: Task[]) { tasks = _tasks - update() return this }, async stop() {