@logtape/pretty 1.4.0-dev.409 → 1.4.0-dev.413
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -2
- package/deno.json +0 -39
- package/src/formatter.test.ts +0 -954
- package/src/formatter.ts +0 -1070
- package/src/mod.ts +0 -17
- package/src/terminal.test.ts +0 -67
- package/src/terminal.ts +0 -94
- package/src/truncate.test.ts +0 -104
- package/src/truncate.ts +0 -90
- package/src/util.deno.ts +0 -21
- package/src/util.node.ts +0 -14
- package/src/util.ts +0 -13
- package/src/wcwidth.test.ts +0 -60
- package/src/wcwidth.ts +0 -414
- package/src/wordwrap.test.ts +0 -114
- package/src/wordwrap.ts +0 -148
- package/tsdown.config.ts +0 -24
package/src/formatter.test.ts
DELETED
|
@@ -1,954 +0,0 @@
|
|
|
1
|
-
import { suite } from "@alinea/suite";
|
|
2
|
-
import type { LogRecord } from "@logtape/logtape";
|
|
3
|
-
import { assert } from "@std/assert/assert";
|
|
4
|
-
import { assertEquals } from "@std/assert/equals";
|
|
5
|
-
import { assertMatch } from "@std/assert/match";
|
|
6
|
-
import { assertStringIncludes } from "@std/assert/string-includes";
|
|
7
|
-
import {
|
|
8
|
-
type CategoryColorMap,
|
|
9
|
-
getPrettyFormatter,
|
|
10
|
-
prettyFormatter,
|
|
11
|
-
} from "./formatter.ts";
|
|
12
|
-
|
|
13
|
-
const test = suite(import.meta);
|
|
14
|
-
|
|
15
|
-
function createLogRecord(
|
|
16
|
-
level: LogRecord["level"],
|
|
17
|
-
category: string[],
|
|
18
|
-
message: LogRecord["message"],
|
|
19
|
-
timestamp: number = Date.now(),
|
|
20
|
-
properties: Record<string, unknown> = {},
|
|
21
|
-
): LogRecord {
|
|
22
|
-
// Convert message array to template strings format for rawMessage
|
|
23
|
-
const rawMessage = typeof message === "string"
|
|
24
|
-
? message
|
|
25
|
-
: message.filter((_, i) => i % 2 === 0).join("{}");
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
level,
|
|
29
|
-
category,
|
|
30
|
-
message,
|
|
31
|
-
rawMessage,
|
|
32
|
-
properties,
|
|
33
|
-
timestamp,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
test("prettyFormatter basic output", () => {
|
|
38
|
-
const record = createLogRecord(
|
|
39
|
-
"info",
|
|
40
|
-
["app", "server"],
|
|
41
|
-
["Server started on port ", 3000],
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
const output = prettyFormatter(record);
|
|
45
|
-
|
|
46
|
-
// Should contain emoji, level, category, and message
|
|
47
|
-
assertMatch(output, /✨/);
|
|
48
|
-
assertMatch(output, /info/); // Default level format is "full"
|
|
49
|
-
assertMatch(output, /app·server/);
|
|
50
|
-
assertMatch(output, /Server started on port/);
|
|
51
|
-
assertMatch(output, /3000/);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test("getPrettyFormatter() with no colors", () => {
|
|
55
|
-
const formatter = getPrettyFormatter({ colors: false });
|
|
56
|
-
const record = createLogRecord(
|
|
57
|
-
"error",
|
|
58
|
-
["app", "auth"],
|
|
59
|
-
["Authentication failed"],
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
const output = formatter(record);
|
|
63
|
-
|
|
64
|
-
// Should not contain ANSI escape codes
|
|
65
|
-
assertEquals(output.includes("\x1b["), false);
|
|
66
|
-
assertMatch(output, /❌ error/); // Default level format is "full"
|
|
67
|
-
assertMatch(output, /app·auth/);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test("getPrettyFormatter() with custom icons", () => {
|
|
71
|
-
const formatter = getPrettyFormatter({
|
|
72
|
-
icons: {
|
|
73
|
-
info: "ℹ️ ",
|
|
74
|
-
error: "🔥",
|
|
75
|
-
},
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
const infoRecord = createLogRecord("info", ["test"], ["Info message"]);
|
|
79
|
-
const errorRecord = createLogRecord("error", ["test"], ["Error message"]);
|
|
80
|
-
|
|
81
|
-
assertMatch(formatter(infoRecord), /ℹ️/);
|
|
82
|
-
assertMatch(formatter(errorRecord), /🔥/);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test("getPrettyFormatter() with no icons", () => {
|
|
86
|
-
const formatter = getPrettyFormatter({ icons: false });
|
|
87
|
-
const record = createLogRecord("info", ["test"], ["Message"]);
|
|
88
|
-
|
|
89
|
-
const output = formatter(record);
|
|
90
|
-
|
|
91
|
-
// Should not contain any emoji
|
|
92
|
-
assertEquals(output.includes("✨"), false);
|
|
93
|
-
assertEquals(output.includes("🐛"), false);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test("getPrettyFormatter() with timestamp", () => {
|
|
97
|
-
const timestamp = new Date("2024-01-15T12:34:56Z").getTime();
|
|
98
|
-
|
|
99
|
-
// Time only - note UTC timezone handling
|
|
100
|
-
const timeFormatter = getPrettyFormatter({ timestamp: "time" });
|
|
101
|
-
const record = createLogRecord("info", ["test"], ["Message"], timestamp);
|
|
102
|
-
const timeOutput = timeFormatter(record);
|
|
103
|
-
assertMatch(timeOutput, /\d{2}:\d{2}:\d{2}/);
|
|
104
|
-
|
|
105
|
-
// Date and time
|
|
106
|
-
const datetimeFormatter = getPrettyFormatter({ timestamp: "date-time" });
|
|
107
|
-
const datetimeOutput = datetimeFormatter(record);
|
|
108
|
-
assertMatch(datetimeOutput, /2024-01-15/);
|
|
109
|
-
assertMatch(datetimeOutput, /\d{2}:\d{2}:\d{2}/);
|
|
110
|
-
|
|
111
|
-
// Custom formatter
|
|
112
|
-
const customFormatter = getPrettyFormatter({
|
|
113
|
-
timestamp: (ts) => new Date(ts).toISOString(),
|
|
114
|
-
});
|
|
115
|
-
const customOutput = customFormatter(record);
|
|
116
|
-
assertMatch(customOutput, /2024-01-15T12:34:56/);
|
|
117
|
-
|
|
118
|
-
// Test function returning null
|
|
119
|
-
const nullFormatter = getPrettyFormatter({
|
|
120
|
-
timestamp: () => null,
|
|
121
|
-
});
|
|
122
|
-
const nullOutput = nullFormatter(record);
|
|
123
|
-
assertEquals(nullOutput.includes("2024"), false);
|
|
124
|
-
|
|
125
|
-
// Test none timestamp
|
|
126
|
-
const noneFormatter = getPrettyFormatter({ timestamp: "none" });
|
|
127
|
-
const noneOutput = noneFormatter(record);
|
|
128
|
-
assertEquals(noneOutput.includes("2024"), false);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
test("getPrettyFormatter() category truncation", () => {
|
|
132
|
-
const formatter = getPrettyFormatter({
|
|
133
|
-
categoryWidth: 15,
|
|
134
|
-
categoryTruncate: "middle",
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
const record = createLogRecord(
|
|
138
|
-
"info",
|
|
139
|
-
["app", "server", "http", "middleware"],
|
|
140
|
-
["Request processed"],
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
const output = formatter(record);
|
|
144
|
-
// Category should be truncated and contain app
|
|
145
|
-
assertMatch(output, /app/);
|
|
146
|
-
assertMatch(output, /…/);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
test("getPrettyFormatter() with null colors", () => {
|
|
150
|
-
const formatter = getPrettyFormatter({
|
|
151
|
-
levelColors: {
|
|
152
|
-
info: null, // No color
|
|
153
|
-
},
|
|
154
|
-
categoryColor: null,
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
const record = createLogRecord("info", ["test"], ["Message"]);
|
|
158
|
-
const result = formatter(record);
|
|
159
|
-
// Should work without errors and have basic formatting
|
|
160
|
-
assertStringIncludes(result, "info"); // Default level format is "full"
|
|
161
|
-
assertStringIncludes(result, "test");
|
|
162
|
-
assertStringIncludes(result, "Message");
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
test("getPrettyFormatter() with values", () => {
|
|
166
|
-
const formatter = getPrettyFormatter();
|
|
167
|
-
const record = createLogRecord(
|
|
168
|
-
"debug",
|
|
169
|
-
["app"],
|
|
170
|
-
["User data: ", { id: 123, name: "John" }, ", array: ", [1, 2, 3]],
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
const output = formatter(record);
|
|
174
|
-
assertMatch(output, /User data:/);
|
|
175
|
-
assertMatch(output, /123/);
|
|
176
|
-
assertMatch(output, /John/);
|
|
177
|
-
assertMatch(output, /1.*2.*3/);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
test("getPrettyFormatter() all log levels", () => {
|
|
181
|
-
const formatter = getPrettyFormatter();
|
|
182
|
-
|
|
183
|
-
const levels: LogRecord["level"][] = [
|
|
184
|
-
"trace",
|
|
185
|
-
"debug",
|
|
186
|
-
"info",
|
|
187
|
-
"warning",
|
|
188
|
-
"error",
|
|
189
|
-
"fatal",
|
|
190
|
-
];
|
|
191
|
-
const expectedIcons = ["🔍", "🐛", "✨", "⚡", "❌", "💀"];
|
|
192
|
-
|
|
193
|
-
levels.forEach((level, i) => {
|
|
194
|
-
const record = createLogRecord(level, ["test"], [`${level} message`]);
|
|
195
|
-
const output = formatter(record);
|
|
196
|
-
|
|
197
|
-
assertMatch(output, new RegExp(expectedIcons[i]));
|
|
198
|
-
// Check for full level format (default)
|
|
199
|
-
assertMatch(output, new RegExp(level));
|
|
200
|
-
});
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
test("getPrettyFormatter() alignment", () => {
|
|
204
|
-
const formatter = getPrettyFormatter({ align: true, colors: false });
|
|
205
|
-
|
|
206
|
-
const records = [
|
|
207
|
-
createLogRecord("info", ["app"], ["Short"]),
|
|
208
|
-
createLogRecord("warning", ["app"], ["Longer level"]),
|
|
209
|
-
];
|
|
210
|
-
|
|
211
|
-
const outputs = records.map((r) => formatter(r));
|
|
212
|
-
|
|
213
|
-
// With alignment, warning (longer) should have more padding before the category
|
|
214
|
-
// Just check that both outputs contain the expected content
|
|
215
|
-
assertMatch(outputs[0], /✨ info.*app.*Short/); // Default level format is "full"
|
|
216
|
-
assertMatch(outputs[1], /⚡.*warning.*app.*Longer level/); // Default level format is "full"
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
test("getPrettyFormatter() no alignment", () => {
|
|
220
|
-
const formatter = getPrettyFormatter({ align: false, colors: false });
|
|
221
|
-
|
|
222
|
-
const record = createLogRecord("info", ["app"], ["Message"]);
|
|
223
|
-
const output = formatter(record);
|
|
224
|
-
|
|
225
|
-
// Should still be formatted but without padding
|
|
226
|
-
assertMatch(output, /✨ info app Message/); // Default level format is "full"
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
test("getPrettyFormatter() with hex colors", () => {
|
|
230
|
-
const formatter = getPrettyFormatter({
|
|
231
|
-
levelColors: {
|
|
232
|
-
info: "#00ff00", // Bright green
|
|
233
|
-
error: "#ff0000", // Bright red
|
|
234
|
-
},
|
|
235
|
-
categoryColor: "#888888",
|
|
236
|
-
messageColor: "#cccccc",
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
const record = createLogRecord("info", ["test"], ["Message"]);
|
|
240
|
-
const result = formatter(record);
|
|
241
|
-
// Should contain true color ANSI codes for hex colors
|
|
242
|
-
assertStringIncludes(result, "\x1b[38;2;0;255;0m"); // #00ff00 converted to RGB
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
test("getPrettyFormatter() with rgb colors", () => {
|
|
246
|
-
const formatter = getPrettyFormatter({
|
|
247
|
-
levelColors: {
|
|
248
|
-
info: "rgb(255,128,0)", // Orange
|
|
249
|
-
},
|
|
250
|
-
timestampColor: "rgb(100,100,100)",
|
|
251
|
-
timestamp: "time",
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
const record = createLogRecord("info", ["test"], ["Message"]);
|
|
255
|
-
const result = formatter(record);
|
|
256
|
-
// Should contain true color ANSI codes for RGB colors
|
|
257
|
-
assertStringIncludes(result, "\x1b[38;2;255;128;0m"); // rgb(255,128,0)
|
|
258
|
-
assertStringIncludes(result, "\x1b[38;2;100;100;100m"); // timestamp color
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
test("getPrettyFormatter() with level formats", () => {
|
|
262
|
-
const abbr = getPrettyFormatter({ level: "ABBR" });
|
|
263
|
-
const full = getPrettyFormatter({ level: "FULL" });
|
|
264
|
-
const letter = getPrettyFormatter({ level: "L" });
|
|
265
|
-
const custom = getPrettyFormatter({ level: (level) => `[${level}]` });
|
|
266
|
-
|
|
267
|
-
const record = createLogRecord("info", ["test"], ["Message"]);
|
|
268
|
-
const abbrResult = abbr(record);
|
|
269
|
-
const fullResult = full(record);
|
|
270
|
-
const letterResult = letter(record);
|
|
271
|
-
const customResult = custom(record);
|
|
272
|
-
|
|
273
|
-
assertStringIncludes(abbrResult, "INF");
|
|
274
|
-
assertStringIncludes(fullResult, "INFO");
|
|
275
|
-
assertStringIncludes(letterResult, "I");
|
|
276
|
-
assertStringIncludes(customResult, "[info]");
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
test("getPrettyFormatter() with extended timestamp formats", () => {
|
|
280
|
-
const timestamp = new Date("2023-05-15T10:30:00.000Z").getTime();
|
|
281
|
-
const record = createLogRecord("info", ["test"], ["Message"], timestamp);
|
|
282
|
-
|
|
283
|
-
// Test all TextFormatterOptions timestamp formats
|
|
284
|
-
const dateTimeTimezone = getPrettyFormatter({
|
|
285
|
-
timestamp: "date-time-timezone",
|
|
286
|
-
});
|
|
287
|
-
const dateTimeTz = getPrettyFormatter({ timestamp: "date-time-tz" });
|
|
288
|
-
const dateTime = getPrettyFormatter({ timestamp: "date-time" });
|
|
289
|
-
const timeTimezone = getPrettyFormatter({ timestamp: "time-timezone" });
|
|
290
|
-
const timeTz = getPrettyFormatter({ timestamp: "time-tz" });
|
|
291
|
-
const rfc3339 = getPrettyFormatter({ timestamp: "rfc3339" });
|
|
292
|
-
const dateOnly = getPrettyFormatter({ timestamp: "date" });
|
|
293
|
-
const datetime = getPrettyFormatter({ timestamp: "date-time" });
|
|
294
|
-
const none = getPrettyFormatter({ timestamp: "none" });
|
|
295
|
-
const disabled = getPrettyFormatter({ timestamp: "disabled" });
|
|
296
|
-
|
|
297
|
-
const dateTimeTimezoneResult = dateTimeTimezone(record);
|
|
298
|
-
const dateTimeTzResult = dateTimeTz(record);
|
|
299
|
-
const dateTimeResult = dateTime(record);
|
|
300
|
-
const timeTimezoneResult = timeTimezone(record);
|
|
301
|
-
const timeTzResult = timeTz(record);
|
|
302
|
-
const rfc3339Result = rfc3339(record);
|
|
303
|
-
const dateOnlyResult = dateOnly(record);
|
|
304
|
-
const datetimeResult = datetime(record);
|
|
305
|
-
const noneResult = none(record);
|
|
306
|
-
const disabledResult = disabled(record);
|
|
307
|
-
|
|
308
|
-
// Check that appropriate timestamps are included
|
|
309
|
-
assertStringIncludes(dateTimeTimezoneResult, "2023-05-15");
|
|
310
|
-
assertStringIncludes(dateTimeTimezoneResult, "+00:00");
|
|
311
|
-
assertStringIncludes(dateTimeTzResult, "2023-05-15");
|
|
312
|
-
assertStringIncludes(dateTimeTzResult, "+00");
|
|
313
|
-
assertStringIncludes(dateTimeResult, "2023-05-15");
|
|
314
|
-
assertStringIncludes(timeTimezoneResult, "10:30:00");
|
|
315
|
-
assertStringIncludes(timeTzResult, "10:30:00");
|
|
316
|
-
assertStringIncludes(rfc3339Result, "2023-05-15T10:30:00.000Z");
|
|
317
|
-
assertStringIncludes(dateOnlyResult, "2023-05-15");
|
|
318
|
-
assertStringIncludes(datetimeResult, "2023-05-15 10:30:00");
|
|
319
|
-
|
|
320
|
-
// Check that none/disabled don't include timestamps
|
|
321
|
-
assertEquals(noneResult.includes("2023"), false);
|
|
322
|
-
assertEquals(disabledResult.includes("2023"), false);
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
test("getPrettyFormatter() with styles", () => {
|
|
326
|
-
const formatter = getPrettyFormatter({
|
|
327
|
-
levelStyle: "bold",
|
|
328
|
-
categoryStyle: "italic",
|
|
329
|
-
messageStyle: "underline",
|
|
330
|
-
timestampStyle: "strikethrough",
|
|
331
|
-
timestamp: "time",
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
const record = createLogRecord("info", ["test"], ["Message"]);
|
|
335
|
-
const result = formatter(record);
|
|
336
|
-
// Should contain ANSI style codes
|
|
337
|
-
assertStringIncludes(result, "\x1b[1m"); // bold
|
|
338
|
-
assertStringIncludes(result, "\x1b[3m"); // italic
|
|
339
|
-
assertStringIncludes(result, "\x1b[4m"); // underline
|
|
340
|
-
assertStringIncludes(result, "\x1b[9m"); // strikethrough
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
test("getPrettyFormatter() with custom category separator", () => {
|
|
344
|
-
const formatter = getPrettyFormatter({
|
|
345
|
-
categorySeparator: ">",
|
|
346
|
-
colors: false,
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
const record = createLogRecord("info", ["app", "web", "server"], ["Message"]);
|
|
350
|
-
const result = formatter(record);
|
|
351
|
-
assertStringIncludes(result, "app>web>server");
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
test("getPrettyFormatter() with ANSI colors", () => {
|
|
355
|
-
const formatter = getPrettyFormatter({
|
|
356
|
-
levelColors: {
|
|
357
|
-
info: "green",
|
|
358
|
-
error: "red",
|
|
359
|
-
},
|
|
360
|
-
categoryColor: "blue",
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
const record = createLogRecord("info", ["test"], ["Message"]);
|
|
364
|
-
const result = formatter(record);
|
|
365
|
-
// Should contain ANSI color codes
|
|
366
|
-
assertStringIncludes(result, "\x1b[32m"); // green
|
|
367
|
-
assertStringIncludes(result, "\x1b[34m"); // blue
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
test("Color helper functions with 3-digit hex", () => {
|
|
371
|
-
const formatter = getPrettyFormatter({
|
|
372
|
-
levelColors: {
|
|
373
|
-
info: "#fff", // 3-digit hex
|
|
374
|
-
},
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
const record = createLogRecord("info", ["test"], ["Message"]);
|
|
378
|
-
const result = formatter(record);
|
|
379
|
-
// Should contain converted RGB codes
|
|
380
|
-
assertStringIncludes(result, "\x1b[38;2;255;255;255m"); // #fff -> rgb(255,255,255)
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
test("getPrettyFormatter() with category color mapping", () => {
|
|
384
|
-
const categoryColorMap: CategoryColorMap = new Map([
|
|
385
|
-
[["app", "auth"], "#ff6b6b"], // red for app.auth.*
|
|
386
|
-
[["app", "db"], "#4ecdc4"], // teal for app.db.*
|
|
387
|
-
[["app"], "#45b7d1"], // blue for app.* (fallback)
|
|
388
|
-
[["lib"], "#96ceb4"], // green for lib.*
|
|
389
|
-
]);
|
|
390
|
-
|
|
391
|
-
const formatter = getPrettyFormatter({
|
|
392
|
-
categoryColorMap,
|
|
393
|
-
colors: true,
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
// Test exact match
|
|
397
|
-
const authRecord = createLogRecord("info", ["app", "auth", "login"], [
|
|
398
|
-
"User logged in",
|
|
399
|
-
]);
|
|
400
|
-
const authResult = formatter(authRecord);
|
|
401
|
-
assertStringIncludes(authResult, "\x1b[38;2;255;107;107m"); // #ff6b6b
|
|
402
|
-
|
|
403
|
-
// Test prefix fallback
|
|
404
|
-
const miscRecord = createLogRecord("info", ["app", "utils"], [
|
|
405
|
-
"Utility called",
|
|
406
|
-
]);
|
|
407
|
-
const miscResult = formatter(miscRecord);
|
|
408
|
-
assertStringIncludes(miscResult, "\x1b[38;2;69;183;209m"); // #45b7d1
|
|
409
|
-
|
|
410
|
-
// Test different prefix
|
|
411
|
-
const libRecord = createLogRecord("info", ["lib", "http"], ["HTTP request"]);
|
|
412
|
-
const libResult = formatter(libRecord);
|
|
413
|
-
assertStringIncludes(libResult, "\x1b[38;2;150;206;180m"); // #96ceb4
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
test("Category color mapping precedence", () => {
|
|
417
|
-
const categoryColorMap: CategoryColorMap = new Map([
|
|
418
|
-
[["app", "auth", "jwt"], "#ff0000"], // Most specific
|
|
419
|
-
[["app", "auth"], "#00ff00"], // Less specific
|
|
420
|
-
[["app"], "#0000ff"], // Least specific
|
|
421
|
-
]);
|
|
422
|
-
|
|
423
|
-
const formatter = getPrettyFormatter({
|
|
424
|
-
categoryColorMap,
|
|
425
|
-
colors: true,
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
// Should match most specific pattern
|
|
429
|
-
const jwtRecord = createLogRecord("info", ["app", "auth", "jwt", "verify"], [
|
|
430
|
-
"Token verified",
|
|
431
|
-
]);
|
|
432
|
-
const jwtResult = formatter(jwtRecord);
|
|
433
|
-
assertStringIncludes(jwtResult, "\x1b[38;2;255;0;0m"); // #ff0000
|
|
434
|
-
|
|
435
|
-
// Should match less specific pattern
|
|
436
|
-
const authRecord = createLogRecord("info", ["app", "auth", "session"], [
|
|
437
|
-
"Session created",
|
|
438
|
-
]);
|
|
439
|
-
const authResult = formatter(authRecord);
|
|
440
|
-
assertStringIncludes(authResult, "\x1b[38;2;0;255;0m"); // #00ff00
|
|
441
|
-
|
|
442
|
-
// Should match least specific pattern
|
|
443
|
-
const appRecord = createLogRecord("info", ["app", "server"], [
|
|
444
|
-
"Server started",
|
|
445
|
-
]);
|
|
446
|
-
const appResult = formatter(appRecord);
|
|
447
|
-
assertStringIncludes(appResult, "\x1b[38;2;0;0;255m"); // #0000ff
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
test("Category color mapping with no match", () => {
|
|
451
|
-
const categoryColorMap: CategoryColorMap = new Map([
|
|
452
|
-
[["app"], "#ff0000"],
|
|
453
|
-
]);
|
|
454
|
-
|
|
455
|
-
const formatter = getPrettyFormatter({
|
|
456
|
-
categoryColorMap,
|
|
457
|
-
categoryColor: "#00ff00", // fallback color
|
|
458
|
-
colors: true,
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
// Should use fallback color for non-matching category
|
|
462
|
-
const record = createLogRecord("info", ["system", "kernel"], [
|
|
463
|
-
"Kernel message",
|
|
464
|
-
]);
|
|
465
|
-
const result = formatter(record);
|
|
466
|
-
assertStringIncludes(result, "\x1b[38;2;0;255;0m"); // fallback #00ff00
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
test("Interpolated values with proper color reset/reapply", () => {
|
|
470
|
-
const formatter = getPrettyFormatter({
|
|
471
|
-
messageColor: "#ffffff",
|
|
472
|
-
messageStyle: "dim",
|
|
473
|
-
colors: true,
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
const record = createLogRecord("info", ["test"], [
|
|
477
|
-
"User data: ",
|
|
478
|
-
{ id: 123, name: "John" },
|
|
479
|
-
", status: ",
|
|
480
|
-
"active",
|
|
481
|
-
]);
|
|
482
|
-
|
|
483
|
-
const result = formatter(record);
|
|
484
|
-
|
|
485
|
-
// Should contain proper color reset/reapply around interpolated values
|
|
486
|
-
// The exact ANSI codes depend on inspect() output, but we should see resets
|
|
487
|
-
assertStringIncludes(result, "\x1b[0m"); // Reset code should be present
|
|
488
|
-
assertStringIncludes(result, "\x1b[2m"); // Dim style should be reapplied
|
|
489
|
-
assertStringIncludes(result, "\x1b[38;2;255;255;255m"); // White color should be reapplied
|
|
490
|
-
});
|
|
491
|
-
|
|
492
|
-
test("Multiple styles combination", () => {
|
|
493
|
-
const formatter = getPrettyFormatter({
|
|
494
|
-
levelStyle: ["bold", "underline"],
|
|
495
|
-
categoryStyle: ["dim", "italic"],
|
|
496
|
-
messageStyle: ["bold", "strikethrough"],
|
|
497
|
-
timestampStyle: ["dim", "underline"],
|
|
498
|
-
timestamp: "time",
|
|
499
|
-
colors: true,
|
|
500
|
-
});
|
|
501
|
-
|
|
502
|
-
const record = createLogRecord("info", ["test"], ["Message"]);
|
|
503
|
-
const result = formatter(record);
|
|
504
|
-
|
|
505
|
-
// Should contain multiple ANSI style codes combined
|
|
506
|
-
assertStringIncludes(result, "\x1b[1m"); // bold
|
|
507
|
-
assertStringIncludes(result, "\x1b[4m"); // underline
|
|
508
|
-
assertStringIncludes(result, "\x1b[2m"); // dim
|
|
509
|
-
assertStringIncludes(result, "\x1b[3m"); // italic
|
|
510
|
-
assertStringIncludes(result, "\x1b[9m"); // strikethrough
|
|
511
|
-
});
|
|
512
|
-
|
|
513
|
-
("Bun" in globalThis ? test.skip : test)(
|
|
514
|
-
"Word wrapping enabled by default",
|
|
515
|
-
() => {
|
|
516
|
-
const formatter = getPrettyFormatter({
|
|
517
|
-
colors: false,
|
|
518
|
-
});
|
|
519
|
-
|
|
520
|
-
const longMessage =
|
|
521
|
-
"This is a very long message that would normally exceed the typical console width and should be wrapped when word wrapping is enabled by default.";
|
|
522
|
-
const record = createLogRecord("info", ["test"], [longMessage]);
|
|
523
|
-
const result = formatter(record);
|
|
524
|
-
|
|
525
|
-
// Should contain multiple line breaks due to wrapping
|
|
526
|
-
const lines = result.split("\n");
|
|
527
|
-
assert(lines.length > 2); // More than just content + trailing newline due to wrapping
|
|
528
|
-
|
|
529
|
-
// First line should contain the beginning of the message
|
|
530
|
-
assert(lines[0].includes("This is a very long message"));
|
|
531
|
-
},
|
|
532
|
-
);
|
|
533
|
-
|
|
534
|
-
test("Word wrapping can be disabled", () => {
|
|
535
|
-
const formatter = getPrettyFormatter({
|
|
536
|
-
colors: false,
|
|
537
|
-
wordWrap: false,
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
const longMessage =
|
|
541
|
-
"This is a very long message that would normally exceed the typical console width but should not be wrapped when word wrapping is explicitly disabled.";
|
|
542
|
-
const record = createLogRecord("info", ["test"], [longMessage]);
|
|
543
|
-
const result = formatter(record);
|
|
544
|
-
|
|
545
|
-
// Should not contain any line breaks in the message (only the trailing newline)
|
|
546
|
-
const lines = result.split("\n");
|
|
547
|
-
assertEquals(lines.length, 2); // One content line + one empty line from trailing newline
|
|
548
|
-
assertStringIncludes(lines[0], longMessage);
|
|
549
|
-
});
|
|
550
|
-
|
|
551
|
-
test("Word wrapping with 80", () => {
|
|
552
|
-
const formatter = getPrettyFormatter({
|
|
553
|
-
wordWrap: 80,
|
|
554
|
-
colors: false,
|
|
555
|
-
align: false,
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
const longMessage =
|
|
559
|
-
"This is a very long message that should be wrapped at approximately 80 characters when word wrapping is enabled with the default width setting.";
|
|
560
|
-
const record = createLogRecord("info", ["test"], [longMessage]);
|
|
561
|
-
const result = formatter(record);
|
|
562
|
-
|
|
563
|
-
// Should contain multiple lines due to wrapping
|
|
564
|
-
const lines = result.split("\n");
|
|
565
|
-
assert(lines.length > 2); // More than just content + trailing newline
|
|
566
|
-
|
|
567
|
-
// Each content line should be roughly within the wrap width
|
|
568
|
-
const contentLines = lines.filter((line) => line.length > 0);
|
|
569
|
-
for (const line of contentLines) {
|
|
570
|
-
assert(line.length <= 85); // Allow some tolerance for word boundaries
|
|
571
|
-
}
|
|
572
|
-
});
|
|
573
|
-
|
|
574
|
-
test("Word wrapping with custom width", () => {
|
|
575
|
-
const formatter = getPrettyFormatter({
|
|
576
|
-
wordWrap: 40,
|
|
577
|
-
colors: false,
|
|
578
|
-
align: false,
|
|
579
|
-
});
|
|
580
|
-
|
|
581
|
-
const longMessage =
|
|
582
|
-
"This is a message that should be wrapped at 40 characters maximum width.";
|
|
583
|
-
const record = createLogRecord("info", ["test"], [longMessage]);
|
|
584
|
-
const result = formatter(record);
|
|
585
|
-
|
|
586
|
-
// Should contain multiple lines due to aggressive wrapping
|
|
587
|
-
const lines = result.split("\n");
|
|
588
|
-
assert(lines.length > 2);
|
|
589
|
-
|
|
590
|
-
// Each content line should be within 40 characters
|
|
591
|
-
const contentLines = lines.filter((line) => line.length > 0);
|
|
592
|
-
for (const line of contentLines) {
|
|
593
|
-
assert(line.length <= 45); // Allow some tolerance
|
|
594
|
-
}
|
|
595
|
-
});
|
|
596
|
-
|
|
597
|
-
test("Word wrapping with proper indentation", () => {
|
|
598
|
-
const formatter = getPrettyFormatter({
|
|
599
|
-
wordWrap: 50,
|
|
600
|
-
colors: false,
|
|
601
|
-
align: false,
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
const longMessage =
|
|
605
|
-
"This is a long message that should wrap with proper indentation to align with the message column.";
|
|
606
|
-
const record = createLogRecord("info", ["app"], [longMessage]);
|
|
607
|
-
const result = formatter(record);
|
|
608
|
-
|
|
609
|
-
const lines = result.split("\n");
|
|
610
|
-
const contentLines = lines.filter((line) => line.length > 0);
|
|
611
|
-
|
|
612
|
-
// Should have multiple lines due to wrapping
|
|
613
|
-
assert(contentLines.length > 1);
|
|
614
|
-
|
|
615
|
-
// First line starts with icon
|
|
616
|
-
assert(contentLines[0].startsWith("✨ info"));
|
|
617
|
-
|
|
618
|
-
// Check that lines are properly wrapped at word boundaries
|
|
619
|
-
// With align: false, the format should be "✨ info app message..."
|
|
620
|
-
// and continuation lines should be properly indented
|
|
621
|
-
assert(
|
|
622
|
-
contentLines.length >= 2,
|
|
623
|
-
"Should have at least 2 lines from wrapping",
|
|
624
|
-
);
|
|
625
|
-
});
|
|
626
|
-
|
|
627
|
-
test("getPrettyFormatter() with consistent icon spacing", () => {
|
|
628
|
-
// Test with custom icons of different display widths
|
|
629
|
-
const formatter = getPrettyFormatter({
|
|
630
|
-
icons: {
|
|
631
|
-
info: "ℹ️", // 2 width emoji
|
|
632
|
-
warning: "!", // 1 width character
|
|
633
|
-
error: "🚨🚨", // 4 width (2 emojis)
|
|
634
|
-
},
|
|
635
|
-
colors: false,
|
|
636
|
-
align: true,
|
|
637
|
-
wordWrap: 50,
|
|
638
|
-
});
|
|
639
|
-
|
|
640
|
-
const longMessage = "This is a long message that should wrap consistently";
|
|
641
|
-
|
|
642
|
-
const infoRecord = createLogRecord("info", ["test"], [longMessage]);
|
|
643
|
-
const warningRecord = createLogRecord("warning", ["test"], [longMessage]);
|
|
644
|
-
const errorRecord = createLogRecord("error", ["test"], [longMessage]);
|
|
645
|
-
|
|
646
|
-
const infoResult = formatter(infoRecord);
|
|
647
|
-
const warningResult = formatter(warningRecord);
|
|
648
|
-
const errorResult = formatter(errorRecord);
|
|
649
|
-
|
|
650
|
-
// Split into lines and get continuation lines
|
|
651
|
-
const infoLines = infoResult.split("\n").filter((line) => line.length > 0);
|
|
652
|
-
const warningLines = warningResult.split("\n").filter((line) =>
|
|
653
|
-
line.length > 0
|
|
654
|
-
);
|
|
655
|
-
const errorLines = errorResult.split("\n").filter((line) => line.length > 0);
|
|
656
|
-
|
|
657
|
-
// All should have multiple lines due to wrapping
|
|
658
|
-
assert(infoLines.length > 1, "Info should wrap to multiple lines");
|
|
659
|
-
assert(warningLines.length > 1, "Warning should wrap to multiple lines");
|
|
660
|
-
assert(errorLines.length > 1, "Error should wrap to multiple lines");
|
|
661
|
-
|
|
662
|
-
// Check that continuation lines are indented to the same position
|
|
663
|
-
// despite different icon widths
|
|
664
|
-
if (
|
|
665
|
-
infoLines.length > 1 && warningLines.length > 1 && errorLines.length > 1
|
|
666
|
-
) {
|
|
667
|
-
const infoIndent = infoLines[1].search(/\S/);
|
|
668
|
-
const warningIndent = warningLines[1].search(/\S/);
|
|
669
|
-
const errorIndent = errorLines[1].search(/\S/);
|
|
670
|
-
|
|
671
|
-
// All continuation lines should start at the same position
|
|
672
|
-
assertEquals(
|
|
673
|
-
infoIndent,
|
|
674
|
-
warningIndent,
|
|
675
|
-
"Info and warning should have same indentation",
|
|
676
|
-
);
|
|
677
|
-
assertEquals(
|
|
678
|
-
warningIndent,
|
|
679
|
-
errorIndent,
|
|
680
|
-
"Warning and error should have same indentation",
|
|
681
|
-
);
|
|
682
|
-
}
|
|
683
|
-
});
|
|
684
|
-
|
|
685
|
-
test("getPrettyFormatter() with automatic width detection", () => {
|
|
686
|
-
const formatter = getPrettyFormatter({
|
|
687
|
-
wordWrap: true, // Auto-detect width
|
|
688
|
-
colors: false,
|
|
689
|
-
});
|
|
690
|
-
|
|
691
|
-
const longMessage =
|
|
692
|
-
"This is a long message that should wrap at the detected terminal width";
|
|
693
|
-
const record = createLogRecord("info", ["test"], [longMessage]);
|
|
694
|
-
const result = formatter(record);
|
|
695
|
-
|
|
696
|
-
// Should have wrapped at some reasonable width
|
|
697
|
-
const lines = result.split("\n").filter((line) => line.length > 0);
|
|
698
|
-
assert(lines.length >= 1, "Should have at least one line");
|
|
699
|
-
|
|
700
|
-
// If wrapping occurred, continuation lines should be properly indented
|
|
701
|
-
if (lines.length > 1) {
|
|
702
|
-
const firstLine = lines[0];
|
|
703
|
-
const continuationLine = lines[1];
|
|
704
|
-
|
|
705
|
-
assert(firstLine.includes("✨"), "First line should contain icon");
|
|
706
|
-
assert(
|
|
707
|
-
continuationLine.startsWith(" "),
|
|
708
|
-
"Continuation line should be indented",
|
|
709
|
-
);
|
|
710
|
-
}
|
|
711
|
-
});
|
|
712
|
-
|
|
713
|
-
test("getPrettyFormatter() with multiline interpolated values", () => {
|
|
714
|
-
const formatter = getPrettyFormatter({
|
|
715
|
-
wordWrap: 60,
|
|
716
|
-
colors: false,
|
|
717
|
-
align: true,
|
|
718
|
-
});
|
|
719
|
-
|
|
720
|
-
// Create an error that will have multiline output
|
|
721
|
-
const error = new Error("Test error message");
|
|
722
|
-
const record = createLogRecord("error", ["test"], [
|
|
723
|
-
"Exception occurred: ",
|
|
724
|
-
error,
|
|
725
|
-
]);
|
|
726
|
-
const result = formatter(record);
|
|
727
|
-
|
|
728
|
-
const lines = result.split("\n").filter((line) => line.length > 0);
|
|
729
|
-
|
|
730
|
-
// Should have multiple lines due to error stack trace
|
|
731
|
-
assert(
|
|
732
|
-
lines.length >= 2,
|
|
733
|
-
"Should have multiple lines for error with stack trace",
|
|
734
|
-
);
|
|
735
|
-
|
|
736
|
-
// First line should contain our message and start of error
|
|
737
|
-
assert(
|
|
738
|
-
lines[0].includes("Exception occurred:"),
|
|
739
|
-
"First line should contain our message",
|
|
740
|
-
);
|
|
741
|
-
assert(lines[0].includes("Error:"), "First line should contain error start");
|
|
742
|
-
|
|
743
|
-
// Error message might be on first or second line depending on wrapping
|
|
744
|
-
const fullOutput = result;
|
|
745
|
-
assert(
|
|
746
|
-
fullOutput.includes("Test error message"),
|
|
747
|
-
"Output should contain error message",
|
|
748
|
-
);
|
|
749
|
-
|
|
750
|
-
// Check that continuation lines are properly indented (should start with significant whitespace)
|
|
751
|
-
for (let i = 1; i < lines.length; i++) {
|
|
752
|
-
const line = lines[i];
|
|
753
|
-
const trimmedLine = line.trimStart();
|
|
754
|
-
const indentLength = line.length - trimmedLine.length;
|
|
755
|
-
assert(
|
|
756
|
-
indentLength >= 10,
|
|
757
|
-
`Line ${i} should be indented (has ${indentLength} spaces)`,
|
|
758
|
-
);
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
// Should contain stack trace somewhere
|
|
762
|
-
const stackTraceLine = lines.find((line) => line.trim().startsWith("at "));
|
|
763
|
-
assert(stackTraceLine, "Should contain a stack trace line");
|
|
764
|
-
const trimmedStackTrace = stackTraceLine.trimStart();
|
|
765
|
-
const stackIndentLength = stackTraceLine.length - trimmedStackTrace.length;
|
|
766
|
-
assert(stackIndentLength >= 10, "Stack trace should be properly indented");
|
|
767
|
-
});
|
|
768
|
-
|
|
769
|
-
test("getPrettyFormatter() with multiline interpolated values (no align)", () => {
|
|
770
|
-
const formatter = getPrettyFormatter({
|
|
771
|
-
wordWrap: 50,
|
|
772
|
-
colors: false,
|
|
773
|
-
align: false,
|
|
774
|
-
});
|
|
775
|
-
|
|
776
|
-
const error = new Error("Test error");
|
|
777
|
-
const record = createLogRecord("error", ["app"], [
|
|
778
|
-
"Error: ",
|
|
779
|
-
error,
|
|
780
|
-
]);
|
|
781
|
-
const result = formatter(record);
|
|
782
|
-
|
|
783
|
-
const lines = result.split("\n").filter((line) => line.length > 0);
|
|
784
|
-
|
|
785
|
-
// Should have multiple lines
|
|
786
|
-
assert(lines.length >= 2, "Should have multiple lines for error");
|
|
787
|
-
|
|
788
|
-
// Check that stack trace lines are properly indented relative to the message start
|
|
789
|
-
const firstLine = lines[0];
|
|
790
|
-
assert(
|
|
791
|
-
firstLine.includes("❌ error app Error:"),
|
|
792
|
-
"First line should contain prefix and message start",
|
|
793
|
-
);
|
|
794
|
-
|
|
795
|
-
if (lines.length > 1) {
|
|
796
|
-
const stackTraceLine = lines.find((line) => line.trim().startsWith("at "));
|
|
797
|
-
if (stackTraceLine) {
|
|
798
|
-
// Stack trace should be indented to align with message content
|
|
799
|
-
assert(
|
|
800
|
-
stackTraceLine.length > stackTraceLine.trimStart().length,
|
|
801
|
-
"Stack trace line should be indented",
|
|
802
|
-
);
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
});
|
|
806
|
-
|
|
807
|
-
test("properties set to true", () => {
|
|
808
|
-
const formatter = getPrettyFormatter({
|
|
809
|
-
properties: true,
|
|
810
|
-
colors: false,
|
|
811
|
-
inspectOptions: { colors: false },
|
|
812
|
-
});
|
|
813
|
-
|
|
814
|
-
const record = createLogRecord("info", ["test"], ["FooBar"], Date.now(), {
|
|
815
|
-
foo: "bar",
|
|
816
|
-
bar: "baz",
|
|
817
|
-
});
|
|
818
|
-
const result = formatter(record);
|
|
819
|
-
|
|
820
|
-
// Should contain multiple lines due to wrapping
|
|
821
|
-
const lines = result.split("\n");
|
|
822
|
-
assertEquals(lines.length, 4); // Normal log line + formatted properties + newline
|
|
823
|
-
assertEquals(
|
|
824
|
-
lines[1].trim(),
|
|
825
|
-
"Deno" in globalThis ? 'foo: "bar"' : "foo: 'bar'",
|
|
826
|
-
);
|
|
827
|
-
assertEquals(
|
|
828
|
-
lines[2].trim(),
|
|
829
|
-
"Deno" in globalThis ? 'bar: "baz"' : "bar: 'baz'",
|
|
830
|
-
);
|
|
831
|
-
});
|
|
832
|
-
|
|
833
|
-
test("properties with long keys (regression test for #87)", () => {
|
|
834
|
-
const formatter = getPrettyFormatter({
|
|
835
|
-
properties: true,
|
|
836
|
-
colors: false,
|
|
837
|
-
inspectOptions: { colors: false },
|
|
838
|
-
align: false, // Disable alignment for predictable output
|
|
839
|
-
});
|
|
840
|
-
|
|
841
|
-
// Use fixed timestamp for reproducible output
|
|
842
|
-
const fixedTimestamp = new Date("2024-01-15T00:00:00Z").getTime();
|
|
843
|
-
|
|
844
|
-
// Create properties with very long keys that will cause negative padding
|
|
845
|
-
const longKeyProps: Record<string, unknown> = {
|
|
846
|
-
VERY_LONG_PROPERTY_NAME_THAT_EXCEEDS_INDENT_WIDTH: "value1",
|
|
847
|
-
ANOTHER_EXTREMELY_LONG_KEY_NAME_FOR_TESTING: "value2",
|
|
848
|
-
SHORT: "value3",
|
|
849
|
-
};
|
|
850
|
-
|
|
851
|
-
const record = createLogRecord(
|
|
852
|
-
"info",
|
|
853
|
-
["test"],
|
|
854
|
-
["Test message"],
|
|
855
|
-
fixedTimestamp,
|
|
856
|
-
longKeyProps,
|
|
857
|
-
);
|
|
858
|
-
|
|
859
|
-
// After the fix, this should not throw an error
|
|
860
|
-
const result = formatter(record);
|
|
861
|
-
|
|
862
|
-
// Check the exact output format
|
|
863
|
-
// Note: Long keys have 0 padding, SHORT key still has some padding
|
|
864
|
-
const expectedOutput = `✨ info test Test message
|
|
865
|
-
VERY_LONG_PROPERTY_NAME_THAT_EXCEEDS_INDENT_WIDTH: ${
|
|
866
|
-
"Deno" in globalThis ? '"value1"' : "'value1'"
|
|
867
|
-
}
|
|
868
|
-
ANOTHER_EXTREMELY_LONG_KEY_NAME_FOR_TESTING: ${
|
|
869
|
-
"Deno" in globalThis ? '"value2"' : "'value2'"
|
|
870
|
-
}
|
|
871
|
-
SHORT: ${"Deno" in globalThis ? '"value3"' : "'value3'"}
|
|
872
|
-
`;
|
|
873
|
-
|
|
874
|
-
assertEquals(result, expectedOutput);
|
|
875
|
-
});
|
|
876
|
-
|
|
877
|
-
test("getPrettyFormatter() with getters option", () => {
|
|
878
|
-
const formatter = getPrettyFormatter({
|
|
879
|
-
colors: false,
|
|
880
|
-
inspectOptions: { getters: true },
|
|
881
|
-
});
|
|
882
|
-
|
|
883
|
-
const recordWithGetters = createLogRecord("info", ["test"], [
|
|
884
|
-
"Object with getter: ",
|
|
885
|
-
{
|
|
886
|
-
get computed() {
|
|
887
|
-
return "getter result";
|
|
888
|
-
},
|
|
889
|
-
},
|
|
890
|
-
]);
|
|
891
|
-
|
|
892
|
-
const result = formatter(recordWithGetters);
|
|
893
|
-
|
|
894
|
-
// Should complete without errors and output should be present
|
|
895
|
-
assertStringIncludes(result, "Object with getter:");
|
|
896
|
-
});
|
|
897
|
-
|
|
898
|
-
test("getPrettyFormatter() with showProxy option", () => {
|
|
899
|
-
const formatter = getPrettyFormatter({
|
|
900
|
-
colors: false,
|
|
901
|
-
inspectOptions: { showProxy: true },
|
|
902
|
-
});
|
|
903
|
-
|
|
904
|
-
const proxyObject = new Proxy(
|
|
905
|
-
{ name: "original" },
|
|
906
|
-
{
|
|
907
|
-
get(target, prop) {
|
|
908
|
-
return target[prop as keyof typeof target];
|
|
909
|
-
},
|
|
910
|
-
},
|
|
911
|
-
);
|
|
912
|
-
|
|
913
|
-
const record = createLogRecord("info", ["test"], [
|
|
914
|
-
"Proxy object: ",
|
|
915
|
-
proxyObject,
|
|
916
|
-
]);
|
|
917
|
-
|
|
918
|
-
const result = formatter(record);
|
|
919
|
-
|
|
920
|
-
// Should complete without errors and output should be present
|
|
921
|
-
assertStringIncludes(result, "Proxy object:");
|
|
922
|
-
assertStringIncludes(result, "original");
|
|
923
|
-
});
|
|
924
|
-
|
|
925
|
-
test("getPrettyFormatter() with both getters and showProxy options", () => {
|
|
926
|
-
const formatter = getPrettyFormatter({
|
|
927
|
-
colors: false,
|
|
928
|
-
inspectOptions: { getters: true, showProxy: true },
|
|
929
|
-
});
|
|
930
|
-
|
|
931
|
-
const proxyWithGetters = new Proxy(
|
|
932
|
-
{
|
|
933
|
-
value: 42,
|
|
934
|
-
get computed() {
|
|
935
|
-
return this.value * 2;
|
|
936
|
-
},
|
|
937
|
-
},
|
|
938
|
-
{
|
|
939
|
-
get(target, prop) {
|
|
940
|
-
return target[prop as keyof typeof target];
|
|
941
|
-
},
|
|
942
|
-
},
|
|
943
|
-
);
|
|
944
|
-
|
|
945
|
-
const record = createLogRecord("info", ["test"], [
|
|
946
|
-
"Complex object: ",
|
|
947
|
-
proxyWithGetters,
|
|
948
|
-
]);
|
|
949
|
-
|
|
950
|
-
const result = formatter(record);
|
|
951
|
-
|
|
952
|
-
// Should complete without errors and output should be present
|
|
953
|
-
assertStringIncludes(result, "Complex object:");
|
|
954
|
-
});
|