@plugjs/expect5
Version:
Unit Testing for the PlugJS Build System ========================================
278 lines (277 loc) • 10 kB
JavaScript
// 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