Skip to content

Commit 269d0bb

Browse files
authored
test: Add custom serializer for WTR (#70)
1 parent f6c1356 commit 269d0bb

File tree

5 files changed

+166
-1
lines changed

5 files changed

+166
-1
lines changed

package-lock.json

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"@web/test-runner": "^0.18.3",
4343
"chai": "^5.1.1",
4444
"htm": "^3.1.1",
45+
"kleur": "^4.1.5",
4546
"preact": "^10.24.3",
4647
"preact-render-to-string": "^6.5.11",
4748
"sinon": "^18.0.0",

test/lazy.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import sinonChai from 'sinon-chai';
66
import { LocationProvider, Router } from '../src/router.js';
77
import lazy, { ErrorBoundary } from '../src/lazy.js';
88

9+
import './setup.js';
10+
911
const expect = chai.expect;
1012
chai.use(sinonChai);
1113

test/router.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import sinonChai from 'sinon-chai';
77
import { LocationProvider, Router, useLocation, Route, useRoute } from '../src/router.js';
88
import lazy, { ErrorBoundary } from '../src/lazy.js';
99

10+
import './setup.js';
11+
1012
const expect = chai.expect;
1113
chai.use(sinonChai);
1214

test/setup.js

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import kl from 'kleur';
2+
3+
/*
4+
* Custom serializer borrowed from the Preact repo as the default in wtr
5+
* is busted when it comes to cyclical references or objects beyond a
6+
* certain depth.
7+
8+
* This also has the benefit of being much prettier and human readable,
9+
* includes indentation, coloring, and support for Map and Set objects.
10+
*/
11+
function patchConsole(method) {
12+
const original = window.console[method];
13+
window.console[method] = (...args) => {
14+
original.apply(window.console, serializeConsoleArgs(args));
15+
};
16+
}
17+
18+
patchConsole('log');
19+
patchConsole('warn');
20+
patchConsole('error');
21+
patchConsole('info');
22+
23+
/**
24+
* @param {any[]} args
25+
* @returns {[string]}
26+
*/
27+
function serializeConsoleArgs(args) {
28+
const flat = args.map(arg => serialize(arg, 'flat', 0, new Set()));
29+
// We don't have access to the users terminal width, so we'll try to
30+
// format everything into one line if possible and assume a terminal
31+
// width of 80 chars
32+
if (kl.reset(flat.join(', ')).length <= 80) {
33+
return [flat.join(', ')];
34+
}
35+
36+
const serialized = args.map(arg => serialize(arg, 'default', 0, new Set()));
37+
return ['\n' + serialized.join(',\n') + '\n'];
38+
}
39+
40+
/**
41+
* @param {number} n
42+
* @returns {string}
43+
*/
44+
function applyIndent(n) {
45+
if (n <= 0) return '';
46+
return ' '.repeat(n);
47+
}
48+
49+
/**
50+
* @param {any} value
51+
* @param {"flat" | "default"} mode
52+
* @param {number} indent
53+
* @param {Set<any>} seen
54+
* @returns {string}
55+
*/
56+
function serialize(value, mode, indent, seen) {
57+
if (seen.has(value)) {
58+
return kl.cyan('[Circular]');
59+
}
60+
61+
if (value === null) {
62+
return kl.bold('null');
63+
} else if (Array.isArray(value)) {
64+
seen.add(value);
65+
const values = value.map(v => serialize(v, mode, indent + 1, seen));
66+
if (mode === 'flat') {
67+
return `[ ${values.join(', ')} ]`;
68+
}
69+
70+
const space = applyIndent(indent);
71+
const pretty = values.map(v => applyIndent(indent + 1) + v).join(',\n');
72+
return `[\n${pretty}\n${space}]`;
73+
} else if (value instanceof Set) {
74+
const values = [];
75+
value.forEach(v => {
76+
values.push(serialize(v, mode, indent, seen));
77+
});
78+
79+
if (mode === 'flat') {
80+
return `Set(${value.size}) { ${values.join(', ')} }`;
81+
}
82+
83+
const pretty = values.map(v => applyIndent(indent + 1) + v).join(',\n');
84+
return `Set(${value.size}) {\n${pretty}\n${applyIndent(indent)}}`;
85+
} else if (value instanceof Map) {
86+
const values = [];
87+
value.forEach((v, k) => {
88+
values.push([
89+
serialize(v, 'flat', indent, seen),
90+
serialize(k, 'flat', indent, seen)
91+
]);
92+
});
93+
94+
if (mode === 'flat') {
95+
const pretty = values.map(v => `${v[0]} => ${v[1]}`).join(', ');
96+
return `Map(${value.size}) { ${pretty} }`;
97+
}
98+
99+
const pretty = values
100+
.map(v => {
101+
return applyIndent(indent + 1) + `${v[0]} => ${v[1]}`;
102+
})
103+
.join(', ');
104+
return `Map(${value.size}) {\n${pretty}\n${applyIndent(indent)}}`;
105+
}
106+
107+
switch (typeof value) {
108+
case 'undefined':
109+
return kl.dim('undefined');
110+
111+
case 'bigint':
112+
case 'number':
113+
case 'boolean':
114+
return kl.yellow(String(value));
115+
case 'string': {
116+
// By default node's built in logging doesn't wrap top level
117+
// strings with quotes
118+
if (indent === 0) {
119+
return String(value);
120+
}
121+
const quote = /[^\\]"/.test(value) ? '"' : "'";
122+
return kl.green(String(quote + value + quote));
123+
}
124+
case 'symbol':
125+
return kl.green(value.toString());
126+
case 'function':
127+
return kl.cyan(`[Function: ${value.name || 'anonymous'}]`);
128+
}
129+
130+
if (value instanceof Element) {
131+
return value.outerHTML;
132+
}
133+
if (value instanceof Error) {
134+
return value.stack;
135+
}
136+
137+
seen.add(value);
138+
139+
const props = Object.keys(value).map(key => {
140+
// Skip calling getters
141+
const desc = Object.getOwnPropertyDescriptor(value, key);
142+
if (typeof desc.get === 'function') {
143+
return `get ${key}()`;
144+
}
145+
146+
const v = serialize(value[key], mode, indent + 1, seen);
147+
return `${key}: ${v}`;
148+
});
149+
150+
if (props.length === 0) {
151+
return '{}';
152+
} else if (mode === 'flat') {
153+
const pretty = props.join(', ');
154+
return `{ ${pretty} }`;
155+
}
156+
157+
const pretty = props.map(p => applyIndent(indent + 1) + p).join(',\n');
158+
return `{\n${pretty}\n${applyIndent(indent)}}`;
159+
}

0 commit comments

Comments
 (0)