UNPKG

@japa/errors-printer

Version:

Error printer to pretty print Japa errors

142 lines (141 loc) 4.21 kB
// src/printer.ts import { Youch } from "youch"; import colors from "@poppinss/colors"; import supportsColor from "supports-color"; import { diff as jestDiff } from "jest-diff"; var { columns } = process.stdout; var ansi = supportsColor.stdout ? colors.ansi() : colors.silent(); var pointer = process.platform === "win32" && !process.env.WT_SESSION ? ">" : "\u276F"; var ErrorsPrinter = class { #options; constructor(options) { this.#options = { stackLinesCount: 5, framesMaxLimit: 3, ...options }; } /** * Returns human readable message for error phase */ #getPhaseTitle(phase) { switch (phase) { case "setup": return "Setup hook"; case "setup:cleanup": return "Setup hook cleanup function"; case "teardown": return "Teardown hook"; case "teardown:cleanup": return "Teardown hook cleanup function"; } } /** * Parses the error stack using Youch */ #parseErrorStack(error) { return new Youch().toJSON(error, { frameSourceBuffer: this.#options.stackLinesCount }); } /** * Parsers chai assertion error */ async #parseAssertionError(error) { const parsedError = await this.#parseErrorStack(error); if (!("showDiff" in error) || error.showDiff) { console.error(); const { actual, expected } = error; const diff = jestDiff(expected, actual, { expand: true, includeChangeCounts: true }); parsedError.message = `${parsedError.message} ${diff}`; } return parsedError; } /** * Displays the error stack for a given error */ async #displayErrorStack(error) { const ansiOutput = await new Youch().toANSI(error, { frameSourceBuffer: this.#options.stackLinesCount }); console.error(ansiOutput.trimEnd()); } /** * Display chai assertion error */ async #displayAssertionError(error) { if (!("showDiff" in error) || error.showDiff) { console.error(); const { actual, expected } = error; const diff = jestDiff(expected, actual, { expand: true, includeChangeCounts: true }); console.error(diff); } await this.#displayErrorStack(error); } /** * Prints a section with heading and borders around it */ printSectionBorder(paging) { const border = "\u2500".repeat(columns - (paging.length + 1)); console.error(ansi.red(`${border}${paging}\u2500`)); } /** * Prints section header with a centered title and * borders around it */ printSectionHeader(title) { const whitspacesWidth = (columns - title.length) / 2; const [lhsWidth, rhsWidth] = Number.isInteger(whitspacesWidth) ? [whitspacesWidth, whitspacesWidth] : [whitspacesWidth - 1, whitspacesWidth + 1]; const borderLeft = ansi.red("\u2500".repeat(lhsWidth - 1)); const borderRight = ansi.red("\u2500".repeat(rhsWidth)); console.error(`${borderLeft}${ansi.bgRed().black(` ${title} `)}${borderRight}`); } /** * Parses an error to JSON */ async parseError(error) { if (error === null || Array.isArray(error) || typeof error !== "object") { return { message: String(error) }; } if ("actual" in error && "expected" in error) { return this.#parseAssertionError(error); } return this.#parseErrorStack(error); } /** * Pretty print the error to the console */ async printError(error) { if (error === null || Array.isArray(error) || typeof error !== "object") { console.error(`Error: ${error}`); return; } if ("actual" in error && "expected" in error) { await this.#displayAssertionError(error); return; } await this.#displayErrorStack(error); } /** * Print summary errors */ async printErrors(errors) { const errorsCount = errors.length; let index = 0; for (let { phase, error, title } of errors) { const label = phase === "test" ? title : `${title}: ${this.#getPhaseTitle(phase)}`; console.error(); console.error(`${pointer} ${ansi.underline(label)}`); await this.printError(error); this.printSectionBorder(`[${++index}/${errorsCount}]`); } } }; export { ErrorsPrinter };