UNPKG

@plugjs/expect5

Version:

Unit Testing for the PlugJS Build System ========================================

278 lines (277 loc) 10 kB
// expectation/print.ts import { $grn, $gry, $red, $und, $wht, $ylw } from "@plugjs/plug/logging"; import { textDiff } from "@plugjs/plug/utils"; import { isMatcher, stringifyValue } from "./types.mjs"; var _opnPar = $gry("("); var _clsPar = $gry(")"); var _opnCrl = $gry("{"); var _clsCrl = $gry("}"); var _curls = $gry("{}"); var _opnSqr = $gry("["); var _clsSqr = $gry("]"); var _squares = $gry("[]"); var _slash = $gry("/"); var _tilde = $gry("~"); var _error = `${_opnPar}${$gry($und("error"))}${_clsPar}`; var _string = `${_opnPar}${$gry($und("string"))}${_clsPar}`; var _matcher = $gry("\u2026 matcher \u2026"); var _extraProps = $gry("\u2026 extra props \u2026"); var _diffHeader = `${$wht("Differences")} ${_opnPar}${$red("actual")}${_slash}${$grn("expected")}${_slash}${$ylw("errors")}${_clsPar}:`; function printBaseDiff(log, diff, prop, mapping, comma) { if ("props" in diff) return printObjectDiff(log, diff, prop, mapping, comma); if ("values" in diff) return printObjectDiff(log, diff, prop, mapping, comma); if ("mappings" in diff) return printObjectDiff(log, diff, prop, mapping, comma); if ("expected" in diff) return printExpectedDiff(log, diff, prop, mapping, comma); if ("missing" in diff) return printMissingDiff(log, diff, prop, mapping, comma); if ("extra" in diff) return printExtraDiff(log, diff, prop, mapping, comma); const { prefix, suffix } = diff.error ? ( // default style if error is the only property fixups(prop, mapping, comma, diff.error) ) : diff.diff ? ( // label as "differs" if no error was found fixups(prop, mapping, comma, diff.error, $red, "differs") ) : fixups(prop, mapping, comma, diff.error); dump(log, diff.value, prefix, suffix, diff.diff ? $red : $wht); } function formatString(value, color) { const characters = []; for (let i = 0; i < value.length; i++) { const c = value.charCodeAt(i); if (c === 32) { characters.push([$gry, "\xB7"]); } else if (c === 9) { characters.push([$gry, " \u2192 "]); } else if (c < 16) { characters.push([$gry, `\\0${c.toString(16).toUpperCase()}`]); } else if (c < 32) { characters.push([$gry, `\\${c.toString(16).toUpperCase()}`]); } else if (c >= 127 && c <= 160) { characters.push([$gry, `\\${c.toString(16).toUpperCase()}`]); } else { characters.push([color, value[i]]); } } const chunks = characters.reduce((acc, [color2, c]) => { const prev = acc[acc.length - 1]; if (prev?.[0] === color2) prev[1] += c; else acc.push([color2, c]); return acc; }, []); return chunks.map(([color2, c]) => color2(c)).join(""); } function printExpectedDiff(log, diff, prop, mapping, comma) { if (typeof diff.value === "string" && typeof diff.expected === "string") { const { prefix, suffix } = fixups(prop, mapping, false, diff.error); log.warn(`${prefix}${_string}${suffix}`); log.warn(textDiff( diff.value, diff.expected, (add) => ` ${$gry("+")} ${formatString(add, $grn)}`, (del) => ` ${$gry("-")} ${formatString(del, $red)}`, (txt) => ` ${formatString(txt, (s) => s)}` )); } else if (diff.value === null || typeof diff.value !== "object") { const { prefix, suffix } = fixups(prop, mapping, comma, diff.error); const joined = `${prefix}${$red(stringify(diff.value))} ${_tilde} `; dump(log, diff.expected, joined, suffix, $grn); } else if (diff.expected === null || typeof diff.expected !== "object") { const { prefix, suffix } = fixups(prop, mapping, comma, diff.error); const joined = ` ${_tilde} ${$grn(stringify(diff.expected))}${suffix}`; dump(log, diff.value, prefix, joined, $red); } else { const { prefix, suffix: suffix1 } = fixups(prop, mapping, false, ""); const { suffix: suffix2 } = fixups(prop, mapping, comma, diff.error); const lastLine = dumpAndContinue(log, diff.expected, prefix, suffix1, $red); dump(log, diff.value, `${lastLine} ${_tilde} `, suffix2, $grn); } } function printMissingDiff(log, diff, prop, mapping, comma) { const { prefix, suffix } = fixups(prop, mapping, comma, diff.error, $red, "missing"); dump(log, diff.missing, prefix, suffix, $red); } function printExtraDiff(log, diff, prop, mapping, comma) { const { prefix, suffix } = fixups(prop, mapping, comma, diff.error, $red, "extra"); dump(log, diff.extra, prefix, suffix, $red); } function printObjectDiff(log, diff, prop, mapping, comma) { const { prefix, suffix } = fixups(prop, mapping, comma, diff.error); const value = diff.value; const ctor = Object.getPrototypeOf(value)?.constructor; const string = ctor === Object || ctor === Array ? "" : stringifyValue(value); let line = string ? `${prefix}${$wht(string)} ` : prefix; let marked = false; if (diff.values) { if (diff.values.length === 0) { line = `${line}${_squares}`; } else { log.warn(`${line}${_opnSqr}`); log.enter(); try { for (const subdiff of diff.values) { printBaseDiff(log, subdiff, "", false, true); } } finally { log.leave(); } line = _clsSqr; } marked = true; } else if (diff.mappings) { if (Object.keys(diff.mappings).length === 0) { line = `${line}${_curls}`; } else { log.warn(`${line}${_opnCrl}`); log.enter(); try { for (const [key, subdiff] of diff.mappings) { printBaseDiff(log, subdiff, stringifyValue(key), true, true); } } finally { log.leave(); } line = _clsCrl; } marked = true; } if (diff.props) { if (marked) line = `${line} ${_extraProps} `; if (Object.keys(diff.props).length === 0) { line = `${line}${_curls}`; } else { log.warn(`${line}${_opnCrl}`); log.enter(); try { for (const [prop2, subdiff] of Object.entries(diff.props)) { printBaseDiff(log, subdiff, stringifyValue(prop2), false, true); } } finally { log.leave(); } line = _clsCrl; } marked = true; } log.warn(`${line}${suffix}`); } function stringify(value) { if (typeof value === "string") return JSON.stringify(value); return stringifyValue(value); } function fixups(prop, mapping, comma, error, color, label) { if (error) color = color || $ylw; const lbl = label ? `${_opnPar}${$gry($und(label))}${_clsPar} ` : ""; const sep = mapping ? " => " : ": "; const prefix = prop ? color ? `${$gry(lbl)}${color(prop)}${$gry(sep)}` : `${$gry(lbl)}${prop}${$gry(sep)}` : label ? `${$gry(lbl)}` : ""; error = error ? ` ${_error} ${$ylw(error)}` : ""; const suffix = `${comma ? $gry(",") : ""}${error}`; return { prefix, suffix }; } function dump(log, value, prefix, suffix, color, stack = []) { log.warn(dumpAndContinue(log, value, prefix, suffix, color, stack)); } function dumpAndContinue(log, value, prefix, suffix, color, stack = []) { if (value === null || typeof value !== "object") { return `${prefix}${color(stringify(value))}${suffix}`; } if (isMatcher(value)) { return `${prefix}${_matcher}${suffix}`; } const circular = stack.indexOf(value); if (circular >= 0) { return `${prefix}${$gry($und(`<circular ${circular}>`))}${suffix}`; } const ctor = Object.getPrototypeOf(value)?.constructor; const string = ctor === Object || ctor === Array ? "" : stringifyValue(value); const keys = new Set(Object.keys(value)); let line = string ? `${prefix}${color(string)} ` : prefix; let marked = false; if (Array.isArray(value)) { if (value.length === 0) { line = `${line}${_squares}`; } else { log.warn(`${line}${_opnSqr}`); log.enter(); try { for (let i = 0; i < value.length; i++) { const { prefix: prefix2, suffix: suffix2 } = fixups("", false, true, void 0, color); dump(log, value[i], prefix2, suffix2, color, [...stack, value]); keys.delete(String(i)); } } finally { log.leave(); } line = _clsSqr; } marked = true; } else if (value instanceof Set) { if (value.size === 0) { line = `${line}${_squares}`; } else { log.warn(`${line}${_opnSqr}`); log.enter(); try { const { prefix: prefix2, suffix: suffix2 } = fixups("", false, true, void 0, color); value.forEach((v) => dump(log, v, prefix2, suffix2, color, [...stack, value])); } finally { log.leave(); } line = _clsSqr; } marked = true; } else if (value instanceof Map) { if (value.size === 0) { line = `${line}${_curls}`; } else { log.warn(`${line}${_opnCrl}`); log.enter(); try { for (const [key, subvalue] of value) { const { prefix: prefix2, suffix: suffix2 } = fixups(stringifyValue(key), true, true, void 0, color); dump(log, subvalue, prefix2, suffix2, color, [...stack, value]); } } finally { log.leave(); } line = _clsCrl; } marked = true; } if (value instanceof String) { const length = value.valueOf().length; for (let i = 0; i < length; i++) keys.delete(String(i)); } if (keys.size) { if (marked) line = `${line} ${_extraProps} `; log.warn(`${line}${_opnCrl}`); log.enter(); try { for (const key of keys) { const { prefix: prefix2, suffix: suffix2 } = fixups(stringifyValue(key), false, true, void 0, color); dump(log, value[key], prefix2, suffix2, color, [...stack, value]); } } finally { log.leave(); } line = _clsCrl; marked = true; } if (marked) { return `${line}${suffix}`; } else { return `${line}${_curls}${suffix}`; } } function printDiff(log, diff, header = true) { if (!header) return printBaseDiff(log, diff, "", false, false); log.warn(_diffHeader); log.enter(); try { printBaseDiff(log, diff, "", false, false); } finally { log.leave(); } } export { printDiff }; //# sourceMappingURL=print.mjs.map