@controlium/utils 1.0.2-alpha.1 → 1.0.2-alpha.3

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.
@@ -1,875 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const logger_1 = require("./logger");
4
- const fs_1 = require("fs");
5
- // =============================================================================
6
- // Helpers
7
- // =============================================================================
8
- /** Builds a standard log-capture callback and returns the output array. */
9
- function makeLogCapture() {
10
- const logOutput = [];
11
- const callback = (message, mediaType) => {
12
- logOutput.push(`MediaType: [${mediaType ?? "Not Defined"}], Message: [${message}]`);
13
- };
14
- return { logOutput, callback };
15
- }
16
- // =============================================================================
17
- // Top-level suite
18
- // =============================================================================
19
- describe("Logger", () => {
20
- afterEach(() => {
21
- logger_1.Logger.reset(true);
22
- });
23
- // ---------------------------------------------------------------------------
24
- // Getters and setters (non-output)
25
- // ---------------------------------------------------------------------------
26
- describe("Getters and setters", () => {
27
- it("logToConsole round-trips correctly", () => {
28
- logger_1.Logger.logToConsole = true;
29
- expect(logger_1.Logger.logToConsole).toBe(true);
30
- logger_1.Logger.logToConsole = false;
31
- expect(logger_1.Logger.logToConsole).toBe(false);
32
- });
33
- it("panicMode round-trips correctly", () => {
34
- logger_1.Logger.panicMode = true;
35
- expect(logger_1.Logger.panicMode).toBe(true);
36
- logger_1.Logger.panicMode = false;
37
- expect(logger_1.Logger.panicMode).toBe(false);
38
- });
39
- it("throwErrorIfLogOutputFails round-trips correctly", () => {
40
- logger_1.Logger.throwErrorIfLogOutputFails = true;
41
- expect(logger_1.Logger.throwErrorIfLogOutputFails).toBe(true);
42
- logger_1.Logger.throwErrorIfLogOutputFails = false;
43
- expect(logger_1.Logger.throwErrorIfLogOutputFails).toBe(false);
44
- });
45
- it("writeLineOptions getter returns the active writeLine configuration", () => {
46
- const opts = logger_1.Logger.writeLineOptions;
47
- expect(opts).toBeDefined();
48
- expect(typeof opts.maxLines).toBe("number");
49
- });
50
- it("clearOutputCallback sets callback to undefined", () => {
51
- logger_1.Logger.logOutputCallback = () => { };
52
- expect(logger_1.Logger.logOutputCallback).toBeDefined();
53
- logger_1.Logger.clearOutputCallback();
54
- expect(logger_1.Logger.logOutputCallback).toBeUndefined();
55
- });
56
- describe("reset()", () => {
57
- it("reset(false) preserves the existing start time", () => {
58
- // Give Logger a known start time by resetting with true first
59
- logger_1.Logger.reset(true);
60
- const timeBefore = logger_1.Logger["startTime"];
61
- // Wait a tick to ensure time would differ if reset
62
- // (no actual sleep needed — just check the value is unchanged)
63
- logger_1.Logger.reset(false);
64
- const timeAfter = logger_1.Logger["startTime"];
65
- expect(timeAfter).toBe(timeBefore);
66
- });
67
- it("reset(true) updates the start time to approximately now", () => {
68
- const before = Date.now();
69
- logger_1.Logger.reset(true);
70
- const after = Date.now();
71
- const startTime = logger_1.Logger["startTime"];
72
- expect(startTime).toBeGreaterThanOrEqual(before);
73
- expect(startTime).toBeLessThanOrEqual(after);
74
- });
75
- it("reset() restores default options (logToConsole becomes false)", () => {
76
- logger_1.Logger.logToConsole = true;
77
- logger_1.Logger.reset();
78
- expect(logger_1.Logger.logToConsole).toBe(false);
79
- });
80
- it("reset() clears the output callback", () => {
81
- logger_1.Logger.logOutputCallback = () => { };
82
- logger_1.Logger.reset();
83
- expect(logger_1.Logger.logOutputCallback).toBeUndefined();
84
- });
85
- });
86
- });
87
- // ---------------------------------------------------------------------------
88
- // Logging level — numeric
89
- // ---------------------------------------------------------------------------
90
- describe("Logging level (numeric)", () => {
91
- it.each([
92
- [logger_1.Logger.Levels.FrameworkDebug],
93
- [logger_1.Logger.Levels.FrameworkInformation],
94
- [logger_1.Logger.Levels.TestDebug],
95
- [logger_1.Logger.Levels.TestInformation],
96
- [logger_1.Logger.Levels.Error],
97
- [logger_1.Logger.Levels.NoOutput],
98
- [7],
99
- ])("level %p round-trips correctly", (level) => {
100
- logger_1.Logger.loggingLevel = level;
101
- expect(logger_1.Logger.loggingLevel).toEqual(level);
102
- });
103
- it("negative value defaults to FrameworkDebug and emits a warning", () => {
104
- const { logOutput, callback } = makeLogCapture();
105
- logger_1.Logger.logOutputCallback = callback;
106
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.NoOutput;
107
- logger_1.Logger.loggingLevel = -1;
108
- expect(logger_1.Logger.loggingLevel).toEqual(logger_1.Logger.Levels.FrameworkDebug);
109
- expect(logOutput).toHaveLength(1);
110
- expect(logOutput[0]).toMatch(/WARNG.*Invalid Log Level \[-1\] \(Must be integer greater than zero\)\. Defaulting to Framework Debug!/);
111
- });
112
- it("decimal value defaults to FrameworkDebug and emits a warning", () => {
113
- const { logOutput, callback } = makeLogCapture();
114
- logger_1.Logger.logOutputCallback = callback;
115
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.NoOutput;
116
- logger_1.Logger.loggingLevel = 3.7;
117
- expect(logger_1.Logger.loggingLevel).toEqual(logger_1.Logger.Levels.FrameworkDebug);
118
- expect(logOutput).toHaveLength(1);
119
- expect(logOutput[0]).toMatch(/WARNG.*Invalid Log Level \[3\.7\] \(Must be integer greater than zero\)\. Defaulting to Framework Debug!/);
120
- });
121
- });
122
- // ---------------------------------------------------------------------------
123
- // Logging level — text
124
- // ---------------------------------------------------------------------------
125
- describe("Logging level (text)", () => {
126
- it.each([
127
- ["Framework debug", "Framework debug (FKDBG)"],
128
- ["Framework information", "Framework information (FKINF)"],
129
- ["TEST DEBUG", "Test debug (TSDBG)"],
130
- ["test information", "Test information (TSINF)"],
131
- ["Error", "Errors only (ERROR)"],
132
- ["No Output", "No output from Log (NOOUT)"],
133
- ["verbose", "Special Level"],
134
- ["maximum", "Special Level"],
135
- ["max", "Special Level"],
136
- ["special", "Special Level"],
137
- [32, "Special Level"]
138
- ])("text '%s' resolves to '%s'", (input, expected) => {
139
- logger_1.Logger.loggingLevel = input;
140
- expect(logger_1.Logger.loggingLevelText).toMatch(expected);
141
- });
142
- it.each([
143
- ["fkdbg", "Framework debug (FKDBG)"],
144
- ["fkINF", "Framework information (FKINF)"],
145
- ["ts dbg", "Test debug (TSDBG)"],
146
- ["TSINF", "Test information (TSINF)"],
147
- ["noout", "No output from Log (NOOUT)"],
148
- ])("shortcode '%s' resolves to '%s'", (input, expected) => {
149
- logger_1.Logger.loggingLevel = input;
150
- expect(logger_1.Logger.loggingLevelText).toMatch(expected);
151
- });
152
- it("unknown text defaults to FrameworkDebug and emits a warning", () => {
153
- const { logOutput, callback } = makeLogCapture();
154
- logger_1.Logger.logOutputCallback = callback;
155
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.NoOutput;
156
- logger_1.Logger.loggingLevel = "wibble";
157
- expect(logger_1.Logger.loggingLevel).toEqual(logger_1.Logger.Levels.FrameworkDebug);
158
- expect(logOutput).toHaveLength(1);
159
- expect(logOutput[0]).toMatch(/WARNG.*Unknown Log Level \[wibble\]\. Defaulting to Framework Debug!/);
160
- });
161
- });
162
- // ---------------------------------------------------------------------------
163
- // Logging level — loggingLevelText
164
- // ---------------------------------------------------------------------------
165
- describe("loggingLevelText", () => {
166
- it.each([
167
- [logger_1.Logger.Levels.FrameworkDebug, "Framework debug (FKDBG)"],
168
- [logger_1.Logger.Levels.FrameworkInformation, "Framework information (FKINF)"],
169
- [logger_1.Logger.Levels.TestDebug, "Test debug (TSDBG)"],
170
- [logger_1.Logger.Levels.TestInformation, "Test information (TSINF)"],
171
- [logger_1.Logger.Levels.Error, "Errors only (ERROR)"],
172
- [logger_1.Logger.Levels.NoOutput, "No output from Log (NOOUT)"],
173
- [7, "Special Level - (7)"],
174
- ])("level %p returns '%s'", (level, expectedText) => {
175
- logger_1.Logger.loggingLevel = level;
176
- expect(logger_1.Logger.loggingLevelText).toMatch(expectedText);
177
- });
178
- it("negative internal level returns 'Unknown!'", () => {
179
- logger_1.Logger["options"]["loggingCurrentLevel"] = -10;
180
- expect(logger_1.Logger.loggingLevelText).toMatch("Unknown!");
181
- });
182
- });
183
- // ---------------------------------------------------------------------------
184
- // loggingLevelDescription
185
- // ---------------------------------------------------------------------------
186
- describe("loggingLevelDescription", () => {
187
- it("returns plain level name when no filter is active", () => {
188
- const result = logger_1.Logger.loggingLevelDescription(logger_1.Logger.Levels.FrameworkDebug, logger_1.Logger.Levels.NoOutput, logger_1.Logger.Levels.NoOutput);
189
- expect(result).toMatch("Framework debug (FKDBG)");
190
- });
191
- it("returns single-level description when min equals max", () => {
192
- const result = logger_1.Logger.loggingLevelDescription(logger_1.Logger.Levels.Error, logger_1.Logger.Levels.Warning, logger_1.Logger.Levels.Warning);
193
- expect(result).toMatch(/Levels \[.*\] and \[.*\]/);
194
- });
195
- it("returns range description when min is less than max", () => {
196
- const result = logger_1.Logger.loggingLevelDescription(logger_1.Logger.Levels.Error, logger_1.Logger.Levels.TestInformation, logger_1.Logger.Levels.FrameworkDebug);
197
- expect(result).toMatch(/Between levels \[.*\]/);
198
- });
199
- it("includes panic preamble when panicMode is active", () => {
200
- logger_1.Logger.panicMode = true;
201
- const result = logger_1.Logger.loggingLevelDescription(logger_1.Logger.Levels.Error, logger_1.Logger.Levels.NoOutput, logger_1.Logger.Levels.NoOutput);
202
- expect(result).toMatch(/^P: /);
203
- });
204
- it("returns plain level name when filter range is inverted (min > max)", () => {
205
- const result = logger_1.Logger.loggingLevelDescription(logger_1.Logger.Levels.Error, logger_1.Logger.Levels.FrameworkDebug, logger_1.Logger.Levels.TestInformation);
206
- expect(result).toBeDefined();
207
- expect(typeof result).toBe("string");
208
- });
209
- });
210
- // ---------------------------------------------------------------------------
211
- // loggingFilter
212
- // ---------------------------------------------------------------------------
213
- describe("loggingFilter", () => {
214
- it("getter returns { min, max } matching what was set", () => {
215
- logger_1.Logger.loggingFilter = { min: 20, max: 25 };
216
- expect(logger_1.Logger.loggingFilter).toEqual({ min: 20, max: 25 });
217
- });
218
- it("round-trips correctly via getter after setter", () => {
219
- logger_1.Logger.loggingFilter = { min: logger_1.Logger.Levels.Error, max: logger_1.Logger.Levels.Warning };
220
- const filter = logger_1.Logger.loggingFilter;
221
- expect(filter.min).toBe(logger_1.Logger.Levels.Error);
222
- expect(filter.max).toBe(logger_1.Logger.Levels.Warning);
223
- });
224
- it("accepts string level names", () => {
225
- logger_1.Logger.loggingFilter = { min: "Error", max: "Warning" };
226
- expect(logger_1.Logger.loggingFilter).toEqual({
227
- min: logger_1.Logger.Levels.Error,
228
- max: logger_1.Logger.Levels.Warning,
229
- });
230
- });
231
- it("invalid min defaults to NoOutput and emits a warning", () => {
232
- const { logOutput, callback } = makeLogCapture();
233
- logger_1.Logger.logOutputCallback = callback;
234
- logger_1.Logger.loggingFilter = { min: -5, max: 10 };
235
- expect(logger_1.Logger.loggingFilter.min).toBe(logger_1.Logger.Levels.NoOutput);
236
- expect(logOutput.length).toBeGreaterThanOrEqual(1);
237
- expect(logOutput[0]).toMatch(/WARNG.*Invalid Log Level/);
238
- });
239
- it("invalid max defaults to NoOutput and emits a warning", () => {
240
- const { logOutput, callback } = makeLogCapture();
241
- logger_1.Logger.logOutputCallback = callback;
242
- logger_1.Logger.loggingFilter = { min: 1, max: -5 };
243
- expect(logger_1.Logger.loggingFilter.max).toBe(logger_1.Logger.Levels.NoOutput);
244
- expect(logOutput.length).toBeGreaterThanOrEqual(1);
245
- });
246
- });
247
- // ---------------------------------------------------------------------------
248
- // writeLine — preamble and filtering (normal mode)
249
- // ---------------------------------------------------------------------------
250
- describe("writeLine — normal mode", () => {
251
- let logOutput;
252
- beforeEach(() => {
253
- const capture = makeLogCapture();
254
- logOutput = capture.logOutput;
255
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Verbose;
256
- logger_1.Logger.logOutputCallback = capture.callback;
257
- });
258
- it("emits the correct write-type label for every built-in level", () => {
259
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "error test");
260
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Warning, "warn test");
261
- logger_1.Logger.writeLine(logger_1.Logger.Levels.FrameworkDebug, "framework debug test");
262
- logger_1.Logger.writeLine(logger_1.Logger.Levels.FrameworkInformation, "framework info test");
263
- logger_1.Logger.writeLine(logger_1.Logger.Levels.TestDebug, "test debug test");
264
- logger_1.Logger.writeLine(logger_1.Logger.Levels.TestInformation, "test info test");
265
- logger_1.Logger.writeLine(20, "special stuff");
266
- expect(logOutput).toHaveLength(7);
267
- expect(logOutput[0]).toMatch(/Message: \[ERROR - /);
268
- expect(logOutput[1]).toMatch(/Message: \[WARNG - /);
269
- expect(logOutput[2]).toMatch(/Message: \[FKDBG - /);
270
- expect(logOutput[3]).toMatch(/Message: \[FKINF - /);
271
- expect(logOutput[4]).toMatch(/Message: \[TSDBG - /);
272
- expect(logOutput[5]).toMatch(/Message: \[TSINF - /);
273
- expect(logOutput[6]).toMatch(/Message: \[00020 - /);
274
- });
275
- it("preamble contains a wall-clock timestamp and an elapsed time", () => {
276
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
277
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "timestamp test");
278
- expect(logOutput).toHaveLength(1);
279
- expect(logOutput[0]).toMatch(/- \[\d{2}:\d{2}:\d{2}\]\[00:00\.\d{3}\] \[/);
280
- });
281
- it("preamble contains the caller's file, line, and column", () => {
282
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
283
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "caller test");
284
- expect(logOutput).toHaveLength(1);
285
- expect(logOutput[0]).toMatch(/\[Object\.<anonymous>\(logger\.spec\.ts:\d{1,3}:\d{1,2}\)\]/);
286
- });
287
- it("message text appears at the end of the log line", () => {
288
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
289
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "hello world");
290
- expect(logOutput).toHaveLength(1);
291
- expect(logOutput[0]).toMatch(/: hello world]$/);
292
- });
293
- it("NoOutput level produces no output", () => {
294
- logger_1.Logger.writeLine(logger_1.Logger.Levels.NoOutput, "should be silent");
295
- expect(logOutput).toHaveLength(0);
296
- });
297
- it("custom timestamp and elapsed format strings are applied", () => {
298
- logger_1.Logger.writeLineOptions.timeFormat = ">>'TIME:' yyyy<<";
299
- logger_1.Logger.writeLineOptions.elapsedFormat = ">>'ELAP:' MMM<<";
300
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "format test");
301
- expect(logOutput).toHaveLength(1);
302
- expect(logOutput[0]).toMatch(/\[>>TIME: 20\d{2}<<\]\[>>ELAP: Jan<<\]/);
303
- });
304
- it("suppressTimeStamp omits the timestamp from the preamble", () => {
305
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
306
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "no timestamp", {
307
- suppressTimeStamp: true,
308
- });
309
- expect(logOutput).toHaveLength(1);
310
- // Timestamp bracket should be absent; elapsed bracket should still be present
311
- expect(logOutput[0]).not.toMatch(/\[\d{2}:\d{2}:\d{2}\]/);
312
- expect(logOutput[0]).toMatch(/\[00:00\.\d{3}\]/);
313
- });
314
- it("suppressElapsed omits the elapsed time from the preamble", () => {
315
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
316
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "no elapsed", {
317
- suppressElapsed: true,
318
- });
319
- expect(logOutput).toHaveLength(1);
320
- expect(logOutput[0]).toMatch(/\[\d{2}:\d{2}:\d{2}\]/);
321
- expect(logOutput[0]).not.toMatch(/\[00:00\.\d{3}\]/);
322
- });
323
- it("suppressTimeStamp and suppressElapsed together remove both from preamble", () => {
324
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
325
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "no time parts", {
326
- suppressTimeStamp: true,
327
- suppressElapsed: true,
328
- });
329
- expect(logOutput).toHaveLength(1);
330
- expect(logOutput[0]).not.toMatch(/\[\d{2}:\d{2}:\d{2}\]/);
331
- expect(logOutput[0]).not.toMatch(/\[00:00\.\d{3}\]/);
332
- });
333
- describe("level filtering", () => {
334
- it("only emits messages at or below the current level", () => {
335
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.TestDebug;
336
- logger_1.Logger.writeLine(logger_1.Logger.Levels.TestInformation, "should pass");
337
- logger_1.Logger.writeLine(logger_1.Logger.Levels.TestDebug, "should pass");
338
- logger_1.Logger.writeLine(logger_1.Logger.Levels.FrameworkInformation, "should be filtered");
339
- expect(logOutput).toHaveLength(2);
340
- expect(logOutput[0]).toMatch(/TSINF/);
341
- expect(logOutput[1]).toMatch(/TSDBG/);
342
- });
343
- it("filter range passes messages within [min, max] regardless of current level", () => {
344
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.TestDebug;
345
- logger_1.Logger.loggingFilter = { min: 20, max: 25 };
346
- logger_1.Logger.writeLine(logger_1.Logger.Levels.TestInformation, "pass — within level");
347
- logger_1.Logger.writeLine(logger_1.Logger.Levels.TestDebug, "pass — within level");
348
- logger_1.Logger.writeLine(logger_1.Logger.Levels.FrameworkInformation, "filtered — above level, outside range");
349
- logger_1.Logger.writeLine(19, "filtered — below range");
350
- logger_1.Logger.writeLine(20, "pass — range min");
351
- logger_1.Logger.writeLine(25, "pass — range max");
352
- logger_1.Logger.writeLine(26, "filtered — above range");
353
- expect(logOutput).toHaveLength(4);
354
- expect(logOutput[0]).toMatch(/TSINF/);
355
- expect(logOutput[1]).toMatch(/TSDBG/);
356
- expect(logOutput[2]).toMatch(/00020/);
357
- expect(logOutput[3]).toMatch(/00025/);
358
- });
359
- });
360
- it("logs to console.error when callback throws (throwErrorIfLogOutputFails=false)", () => {
361
- const consoleSpy = jest.spyOn(console, "error").mockImplementation(() => { });
362
- logger_1.Logger.logOutputCallback = () => { throw new Error("callback boom"); };
363
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "test");
364
- expect(consoleSpy).toHaveBeenCalledWith(expect.stringMatching(/Error thrown from Log Output Callback during writeLine/));
365
- expect(consoleSpy).toHaveBeenCalledWith(expect.stringMatching(/callback boom/));
366
- consoleSpy.mockRestore();
367
- });
368
- it("re-throws when callback throws and throwErrorIfLogOutputFails=true", () => {
369
- jest.spyOn(console, "error").mockImplementation(() => { });
370
- logger_1.Logger.throwErrorIfLogOutputFails = true;
371
- logger_1.Logger.logOutputCallback = () => { throw new Error("callback boom"); };
372
- expect(() => logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "test")).toThrow(/Error thrown from Log Output Callback during writeLine/);
373
- jest.restoreAllMocks();
374
- });
375
- });
376
- // ---------------------------------------------------------------------------
377
- // writeLine — panic mode
378
- // ---------------------------------------------------------------------------
379
- describe("writeLine — panic mode", () => {
380
- let logOutput;
381
- beforeEach(() => {
382
- const capture = makeLogCapture();
383
- logOutput = capture.logOutput;
384
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
385
- logger_1.Logger.panicMode = true;
386
- logger_1.Logger.logOutputCallback = capture.callback;
387
- });
388
- it("prepends 'P' to each write-type label", () => {
389
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Verbose;
390
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "error test");
391
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Warning, "warn test");
392
- logger_1.Logger.writeLine(logger_1.Logger.Levels.FrameworkDebug, "framework debug test");
393
- logger_1.Logger.writeLine(logger_1.Logger.Levels.FrameworkInformation, "framework info test");
394
- logger_1.Logger.writeLine(logger_1.Logger.Levels.TestDebug, "test debug test");
395
- logger_1.Logger.writeLine(logger_1.Logger.Levels.TestInformation, "test info test");
396
- logger_1.Logger.writeLine(20, "special stuff");
397
- expect(logOutput).toHaveLength(7);
398
- expect(logOutput[0]).toMatch(/Message: \[PERROR - /);
399
- expect(logOutput[1]).toMatch(/Message: \[PWARNG - /);
400
- expect(logOutput[2]).toMatch(/Message: \[PFKDBG - /);
401
- expect(logOutput[3]).toMatch(/Message: \[PFKINF - /);
402
- expect(logOutput[4]).toMatch(/Message: \[PTSDBG - /);
403
- expect(logOutput[5]).toMatch(/Message: \[PTSINF - /);
404
- expect(logOutput[6]).toMatch(/Message: \[P00020 - /);
405
- });
406
- it("outputs all messages regardless of current logging level", () => {
407
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.TestDebug;
408
- logger_1.Logger.loggingFilter = { min: 20, max: 25 };
409
- logger_1.Logger.writeLine(logger_1.Logger.Levels.TestInformation, "should pass");
410
- logger_1.Logger.writeLine(logger_1.Logger.Levels.TestDebug, "should pass");
411
- logger_1.Logger.writeLine(logger_1.Logger.Levels.FrameworkInformation, "should pass — panic");
412
- logger_1.Logger.writeLine(19, "should pass — panic");
413
- logger_1.Logger.writeLine(20, "should pass");
414
- logger_1.Logger.writeLine(25, "should pass");
415
- logger_1.Logger.writeLine(26, "should pass — panic");
416
- expect(logOutput).toHaveLength(7);
417
- });
418
- it("NoOutput level is still suppressed in panic mode", () => {
419
- logger_1.Logger.writeLine(logger_1.Logger.Levels.NoOutput, "should still be silent");
420
- expect(logOutput).toHaveLength(1);
421
- });
422
- it("preamble still contains timestamp and elapsed time", () => {
423
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "panic timestamp test");
424
- expect(logOutput[0]).toMatch(/- \[\d{2}:\d{2}:\d{2}\]\[00:00\.\d{3}\] \[/);
425
- });
426
- it("preamble still shows the caller's file and location", () => {
427
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "panic caller test");
428
- expect(logOutput[0]).toMatch(/\[Object\.<anonymous>\(logger\.spec\.ts:\d{1,3}:\d{1,2}\)\]/);
429
- });
430
- it("custom timestamp and elapsed formats are applied in panic mode", () => {
431
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Verbose;
432
- logger_1.Logger.writeLineOptions.timeFormat = ">>'TIME:' yyyy<<";
433
- logger_1.Logger.writeLineOptions.elapsedFormat = ">>'ELAP:' MMM<<";
434
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "format test");
435
- expect(logOutput).toHaveLength(1);
436
- expect(logOutput[0]).toMatch(/\[PERROR - \[>>TIME: 20\d{2}<<\]\[>>ELAP: Jan<<\]/);
437
- });
438
- });
439
- // ---------------------------------------------------------------------------
440
- // writeLine — multiline
441
- // ---------------------------------------------------------------------------
442
- describe("writeLine — multiline", () => {
443
- let logOutput;
444
- beforeEach(() => {
445
- const capture = makeLogCapture();
446
- logOutput = capture.logOutput;
447
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
448
- logger_1.Logger.logOutputCallback = (message) => logOutput.push(message);
449
- });
450
- it("splits a two-line string into two separate log entries", () => {
451
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "line 1\nline 2");
452
- expect(logOutput).toHaveLength(2);
453
- expect(logOutput[0]).toMatch(/: line 1$/);
454
- expect(logOutput[1]).toMatch(/: line 2$/);
455
- });
456
- it("respects maxLines and inserts a truncation notice for skipped lines", () => {
457
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "line 1\nline 2\nline 3\nline 4\nline 5\nline 6", { maxLines: 4 });
458
- expect(logOutput).toHaveLength(4);
459
- expect(logOutput[0]).toMatch(/: line 1$/);
460
- expect(logOutput[1]).toMatch(/: line 2$/);
461
- expect(logOutput[2]).toMatch(/\.\.\. \(Skipping some lines as total length \(6\) > 4!!\)$/);
462
- expect(logOutput[3]).toMatch(/: line 6$/);
463
- });
464
- it("maxLines of 1 is treated as 1 (not 0 or negative)", () => {
465
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "line 1\nline 2\nline 3\nline 4", {
466
- maxLines: 3,
467
- });
468
- // maxLines=1: first line becomes the truncation notice, last line is kept
469
- expect(logOutput).toHaveLength(3);
470
- });
471
- it("throws and logs an error when maxLines is less than 3", () => {
472
- expect(() => {
473
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "line 1\nline 2", { maxLines: 2 });
474
- }).toThrow("maxLines must be 3 or greater! Number given was <2>");
475
- expect(logOutput.some((line) => line.includes("maxLines must be 3 or greater! Number given was <2>"))).toBe(true);
476
- });
477
- it("outputs all lines when line count exactly equals maxLines", () => {
478
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "line 1\nline 2\nline 3", { maxLines: 3 });
479
- expect(logOutput).toHaveLength(3);
480
- expect(logOutput[0]).toMatch(/: line 1$/);
481
- expect(logOutput[1]).toMatch(/: line 2$/);
482
- expect(logOutput[2]).toMatch(/: line 3$/);
483
- });
484
- });
485
- // ---------------------------------------------------------------------------
486
- // writeLine — preamble suppression
487
- // ---------------------------------------------------------------------------
488
- describe("writeLine — preamble suppression", () => {
489
- let logOutput;
490
- beforeEach(() => {
491
- logOutput = [];
492
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
493
- logger_1.Logger.logOutputCallback = (message) => logOutput.push(message);
494
- });
495
- it("suppressAllPreamble removes preamble from a single-line message", () => {
496
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "plain text", {
497
- suppressAllPreamble: true,
498
- });
499
- expect(logOutput[0]).toBe("plain text");
500
- });
501
- it("suppressAllPreamble removes preamble from every line of a multi-line message", () => {
502
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "line 1\nline 2\nline 3", { suppressAllPreamble: true });
503
- expect(logOutput[0]).toBe("line 1");
504
- expect(logOutput[1]).toBe("line 2");
505
- expect(logOutput[2]).toBe("line 3");
506
- });
507
- it("suppressMultilinePreamble keeps preamble on the first line only", () => {
508
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "line 1\nline 2\nline 3", { suppressMultilinePreamble: true });
509
- expect(logOutput[0]).toMatch(/^.*: line 1$/);
510
- expect(logOutput[1]).toBe("line 2");
511
- expect(logOutput[2]).toBe("line 3");
512
- });
513
- it("suppressAllPreamble takes precedence over suppressMultilinePreamble", () => {
514
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "line 1\nline 2\nline 3", { suppressMultilinePreamble: true, suppressAllPreamble: true });
515
- expect(logOutput[0]).toBe("line 1");
516
- expect(logOutput[1]).toBe("line 2");
517
- expect(logOutput[2]).toBe("line 3");
518
- });
519
- it("suppressAllPreamble with maxLines: truncation notice has no preamble", () => {
520
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "line 1\nline 2\nline 3\nline 4\nline 5\nline 6", { maxLines: 4, suppressAllPreamble: true });
521
- expect(logOutput[0]).toBe("line 1");
522
- expect(logOutput[1]).toBe("line 2");
523
- expect(logOutput[2]).toMatch(/^\.\.\. \(Skipping/);
524
- expect(logOutput[3]).toBe("line 6");
525
- });
526
- it("suppressMultilinePreamble with maxLines: truncation notice has no preamble", () => {
527
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "line 1\nline 2\nline 3\nline 4\nline 5\nline 6", { maxLines: 4, suppressMultilinePreamble: true });
528
- expect(logOutput[0]).toMatch(/^.*: line 1$/);
529
- expect(logOutput[1]).toBe("line 2");
530
- expect(logOutput[2]).toMatch(/^\.\.\. \(Skipping some lines as total length \(6\) > 4!!\)$/);
531
- expect(logOutput[3]).toBe("line 6");
532
- });
533
- it("both suppression flags with maxLines: all lines bare", () => {
534
- logger_1.Logger.writeLine(logger_1.Logger.Levels.Error, "line 1\nline 2\nline 3\nline 4\nline 5\nline 6", { maxLines: 4, suppressMultilinePreamble: true, suppressAllPreamble: true });
535
- expect(logOutput[0]).toBe("line 1");
536
- expect(logOutput[1]).toBe("line 2");
537
- expect(logOutput[2]).toMatch(/^\.\.\. \(Skipping/);
538
- expect(logOutput[3]).toBe("line 6");
539
- });
540
- });
541
- // ---------------------------------------------------------------------------
542
- // Console output behaviour
543
- // ---------------------------------------------------------------------------
544
- describe("Console output", () => {
545
- let logSpy;
546
- let logOutput;
547
- beforeEach(() => {
548
- logOutput = [];
549
- logSpy = jest.spyOn(console, "log").mockImplementation();
550
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.FrameworkDebug;
551
- });
552
- afterEach(() => {
553
- logSpy.mockRestore();
554
- });
555
- it("logToConsole=true writes to console in addition to the callback", () => {
556
- logger_1.Logger.logOutputCallback = (message) => logOutput.push(message);
557
- logger_1.Logger.logToConsole = true;
558
- logger_1.Logger.writeLine(logger_1.Logger.Levels.TestInformation, "console test");
559
- expect(logSpy).toHaveBeenCalledWith(expect.stringMatching(/TSINF - \[.*console test/));
560
- expect(logOutput).toHaveLength(1);
561
- });
562
- it("logToConsole=false does not write to console when callback is set", () => {
563
- logger_1.Logger.logOutputCallback = (message) => logOutput.push(message);
564
- logger_1.Logger.logToConsole = false;
565
- logger_1.Logger.writeLine(logger_1.Logger.Levels.TestInformation, "silent console");
566
- expect(logSpy).not.toHaveBeenCalled();
567
- expect(logOutput).toHaveLength(1);
568
- });
569
- it("with no callback and logToConsole=false, still writes to console as fallback", () => {
570
- logger_1.Logger.clearOutputCallback();
571
- logger_1.Logger.logToConsole = false;
572
- logger_1.Logger.writeLine(logger_1.Logger.Levels.TestInformation, "fallback console");
573
- expect(logSpy).toHaveBeenCalledWith(expect.stringMatching(/TSINF - \[.*fallback console/));
574
- });
575
- it("with no callback and logToConsole=true, writes to console exactly once", () => {
576
- logger_1.Logger.clearOutputCallback();
577
- logger_1.Logger.logToConsole = true;
578
- logger_1.Logger.writeLine(logger_1.Logger.Levels.TestInformation, "once only");
579
- expect(logSpy).toHaveBeenCalledTimes(1);
580
- });
581
- it("logToConsole getter matches what was set", () => {
582
- logger_1.Logger.logToConsole = true;
583
- expect(logger_1.Logger.logToConsole).toBe(true);
584
- logger_1.Logger.logToConsole = false;
585
- expect(logger_1.Logger.logToConsole).toBe(false);
586
- });
587
- });
588
- // ---------------------------------------------------------------------------
589
- // attach()
590
- // ---------------------------------------------------------------------------
591
- describe("attach()", () => {
592
- let logOutput;
593
- beforeEach(() => {
594
- const capture = makeLogCapture();
595
- logOutput = capture.logOutput;
596
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
597
- logger_1.Logger.logOutputCallback = capture.callback;
598
- });
599
- it("passes data and mediaType through to the callback", () => {
600
- logger_1.Logger.attach(logger_1.Logger.Levels.Error, "<b>hi</b>", "text/html");
601
- expect(logOutput).toHaveLength(1);
602
- expect(logOutput[0]).toMatch(/MediaType: \[text\/html\], Message: \[<b>hi<\/b>\]/);
603
- });
604
- it("is suppressed when logLevel is below the current logging level", () => {
605
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
606
- logger_1.Logger.attach(logger_1.Logger.Levels.Warning, "should be filtered", "text/plain");
607
- expect(logOutput).toHaveLength(0);
608
- });
609
- it("logs an error when callback is not a function", () => {
610
- const consoleSpy = jest.spyOn(console, "log").mockImplementation();
611
- logger_1.Logger.clearOutputCallback();
612
- logger_1.Logger.logToConsole = false;
613
- logger_1.Logger.attach(logger_1.Logger.Levels.Error, "test", "text/plain");
614
- // Output goes to console fallback since no callback is set
615
- expect(consoleSpy).toHaveBeenCalledWith(expect.stringMatching(/Log Output callback is type \[undefined\]/));
616
- consoleSpy.mockRestore();
617
- });
618
- it("when callback is set to a non-function, error is logged via console", () => {
619
- logger_1.Logger.logOutputCallback = "not-a-function";
620
- const consoleSpy = jest.spyOn(console, "log").mockImplementation();
621
- logger_1.Logger.attach(logger_1.Logger.Levels.Error, "test", "text/plain");
622
- expect(consoleSpy).toHaveBeenCalledWith(expect.stringMatching(/Log Output callback is type \[string\]/));
623
- consoleSpy.mockRestore();
624
- });
625
- it("non-string data is described correctly in the error message", () => {
626
- logger_1.Logger.logOutputCallback = (message, mediaType) => {
627
- if (mediaType === "base64:image/png")
628
- throw new Error("deliberate");
629
- logOutput.push(`MediaType: [${mediaType ?? "Not Defined"}], Message: [${message}]`);
630
- };
631
- logger_1.Logger.attach(logger_1.Logger.Levels.Error, 24, "base64:image/png");
632
- // Error details are written back through writeLine -> logOutput
633
- const combined = logOutput.join("\n");
634
- expect(combined).toMatch(/Not a string! Is type number/);
635
- });
636
- it("long data strings are truncated in the error message", () => {
637
- logger_1.Logger.logOutputCallback = (message, mediaType) => {
638
- if (mediaType === "base64:image/png")
639
- throw new Error("deliberate");
640
- logOutput.push(`MediaType: [${mediaType ?? "Not Defined"}], Message: [${message}]`);
641
- };
642
- logger_1.Logger.attach(logger_1.Logger.Levels.Error, "A very long text that will have to be truncated so that it doesnt overrun", "base64:image/png");
643
- const combined = logOutput.join("\n");
644
- expect(combined).toMatch(/A very long text that wil\.\.\.run/);
645
- });
646
- it("throwErrorIfLogOutputFails=true re-throws callback errors", () => {
647
- logger_1.Logger.throwErrorIfLogOutputFails = true;
648
- logger_1.Logger.logOutputCallback = () => {
649
- throw new Error("callback failure");
650
- };
651
- expect(() => logger_1.Logger.attach(logger_1.Logger.Levels.Error, "data", "text/plain")).toThrow("callback failure");
652
- });
653
- });
654
- // ---------------------------------------------------------------------------
655
- // attachHTML()
656
- // ---------------------------------------------------------------------------
657
- describe("attachHTML()", () => {
658
- let logOutput;
659
- beforeEach(() => {
660
- const capture = makeLogCapture();
661
- logOutput = capture.logOutput;
662
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
663
- logger_1.Logger.logOutputCallback = capture.callback;
664
- });
665
- it("sends HTML content with text/html media type", () => {
666
- const html = "<div>hello</div>";
667
- logger_1.Logger.attachHTML(logger_1.Logger.Levels.Error, html);
668
- expect(logOutput).toHaveLength(1);
669
- expect(logOutput[0]).toMatch(new RegExp(`MediaType: \\[text/html\\], Message: \\[${html.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\]`));
670
- });
671
- it("is suppressed when logLevel is below the current logging level", () => {
672
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
673
- logger_1.Logger.attachHTML(logger_1.Logger.Levels.Warning, "<p>filtered</p>");
674
- expect(logOutput).toHaveLength(0);
675
- });
676
- });
677
- // ---------------------------------------------------------------------------
678
- // attachScreenshot()
679
- // ---------------------------------------------------------------------------
680
- describe("attachScreenshot()", () => {
681
- let logOutput;
682
- beforeEach(() => {
683
- const capture = makeLogCapture();
684
- logOutput = capture.logOutput;
685
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
686
- logger_1.Logger.logOutputCallback = capture.callback;
687
- });
688
- it("attaches a Buffer as a base64-encoded PNG", () => {
689
- const buffer = Buffer.from("Test string", "utf8");
690
- const expected = buffer.toString("base64");
691
- logger_1.Logger.attachScreenshot(logger_1.Logger.Levels.Error, buffer);
692
- expect(logOutput).toHaveLength(1);
693
- expect(logOutput[0]).toMatch(new RegExp(`MediaType: \\[base64:image/png\\], Message: \\[${expected}\\]`));
694
- });
695
- it("attaches a plain string by base64-encoding it", () => {
696
- const input = "Test string";
697
- const expected = Buffer.from(input, "utf8").toString("base64");
698
- logger_1.Logger.attachScreenshot(logger_1.Logger.Levels.Error, input);
699
- expect(logOutput).toHaveLength(1);
700
- expect(logOutput[0]).toMatch(new RegExp(`MediaType: \\[base64:image/png\\], Message: \\[${expected}\\]`));
701
- });
702
- it("is suppressed when logLevel is below the current logging level", () => {
703
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
704
- logger_1.Logger.attachScreenshot(logger_1.Logger.Levels.Warning, "filtered");
705
- expect(logOutput).toHaveLength(0);
706
- });
707
- it("processes callback errors via processError when callback throws", () => {
708
- const errorMessage = "reporter rejected";
709
- logger_1.Logger.logOutputCallback = (message, mediaType) => {
710
- if (mediaType === "base64:image/png")
711
- throw new Error(errorMessage);
712
- logOutput.push(message);
713
- };
714
- logger_1.Logger.attachScreenshot(logger_1.Logger.Levels.Error, "test");
715
- const combined = logOutput.join("\n");
716
- expect(combined).toMatch(/Error thrown from Log Output Callback/);
717
- expect(combined).toMatch(new RegExp(errorMessage));
718
- });
719
- it("re-throws callback errors when throwErrorIfLogOutputFails=true", () => {
720
- const screenshot = "test-screenshot";
721
- logger_1.Logger.throwErrorIfLogOutputFails = true;
722
- logger_1.Logger.logOutputCallback = (_, mediaType) => {
723
- if (mediaType === "base64:image/png")
724
- throw new Error("No Like");
725
- logOutput.push(`MediaType: [${mediaType}]`);
726
- };
727
- let thrown = "";
728
- try {
729
- logger_1.Logger.attachScreenshot(logger_1.Logger.Levels.Error, screenshot);
730
- }
731
- catch (err) {
732
- thrown = err.message;
733
- }
734
- const lines = thrown.split("\n");
735
- expect(lines[0]).toMatch(/Error thrown from Log Output Callback/);
736
- expect(lines[1]).toMatch(/No Like/);
737
- expect(lines[3]).toMatch(new RegExp(Buffer.from(screenshot, "utf8").toString("base64")));
738
- expect(lines[5]).toMatch(/base64:image\/png/);
739
- });
740
- });
741
- // ---------------------------------------------------------------------------
742
- // attachVideo() and attachVideoFile()
743
- // ---------------------------------------------------------------------------
744
- describe("attachVideo()", () => {
745
- let logOutput;
746
- beforeEach(() => {
747
- const capture = makeLogCapture();
748
- logOutput = capture.logOutput;
749
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
750
- logger_1.Logger.logOutputCallback = capture.callback;
751
- });
752
- it("wraps the buffer in a <video> tag with default options", () => {
753
- const buffer = Buffer.from("Test string", "utf8");
754
- const base64 = buffer.toString("base64");
755
- logger_1.Logger.attachVideo(logger_1.Logger.Levels.Error, buffer);
756
- const expected = `<video controls width="320" height="180"><source src="data:video/webm;base64,${base64}" type="video/webm">Video (Codec webm) not supported by browser</video>`;
757
- expect(logOutput).toHaveLength(1);
758
- expect(logOutput[0]).toMatch(new RegExp(`MediaType: \\[text/html\\], Message: \\[${expected.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\]`));
759
- });
760
- it("uses caller-supplied VideoOptions over the defaults", () => {
761
- const buffer = Buffer.from("Test string", "utf8");
762
- const base64 = buffer.toString("base64");
763
- logger_1.Logger.attachVideo(logger_1.Logger.Levels.Error, buffer, {
764
- width: 640,
765
- height: 360,
766
- videoCodec: "mp4",
767
- });
768
- const expected = `<video controls width="640" height="360"><source src="data:video/mp4;base64,${base64}" type="video/mp4">Video (Codec mp4) not supported by browser</video>`;
769
- expect(logOutput[0]).toMatch(new RegExp(`MediaType: \\[text/html\\], Message: \\[${expected.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\]`));
770
- });
771
- it("is suppressed when logLevel is below the current logging level", () => {
772
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
773
- logger_1.Logger.attachVideo(logger_1.Logger.Levels.Warning, Buffer.from("x"));
774
- expect(logOutput).toHaveLength(0);
775
- });
776
- it("adds title='PANIC_MODE' to the video element when panicMode is active", () => {
777
- logger_1.Logger.panicMode = true;
778
- const buffer = Buffer.from("test", "utf8");
779
- logger_1.Logger.attachVideo(logger_1.Logger.Levels.Error, buffer);
780
- expect(logOutput[0]).toMatch(/title="PANIC_MODE"/);
781
- });
782
- });
783
- describe("attachVideoFile()", () => {
784
- let logOutput;
785
- beforeEach(() => {
786
- const capture = makeLogCapture();
787
- logOutput = capture.logOutput;
788
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
789
- logger_1.Logger.logOutputCallback = capture.callback;
790
- });
791
- it("reads the file and attaches it as video", () => {
792
- const fileName = "testData/testData.txt";
793
- const buffer = (0, fs_1.readFileSync)(fileName);
794
- const base64 = buffer.toString("base64");
795
- logger_1.Logger.attachVideoFile(logger_1.Logger.Levels.Error, fileName);
796
- const expected = `<video controls width="320" height="180"><source src="data:video/webm;base64,${base64}" type="video/webm">Video (Codec webm) not supported by browser</video>`;
797
- expect(logOutput).toHaveLength(1);
798
- expect(logOutput[0]).toMatch(new RegExp(`MediaType: \\[text/html\\], Message: \\[${expected.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\]`));
799
- });
800
- it("logs an error when the file does not exist", () => {
801
- logger_1.Logger.attachVideoFile(logger_1.Logger.Levels.Error, "testData/does-not-exist.txt");
802
- expect(logOutput).toHaveLength(2);
803
- expect(logOutput[0]).toMatch(/\[ERROR.*Error thrown reading video data/);
804
- expect(logOutput[1]).toMatch(/ENOENT.*no such file or directory/);
805
- });
806
- it("is suppressed when logLevel is below the current logging level", () => {
807
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Error;
808
- logger_1.Logger.attachVideoFile(logger_1.Logger.Levels.Warning, "testData/testData.txt");
809
- expect(logOutput).toHaveLength(0);
810
- });
811
- });
812
- // ---------------------------------------------------------------------------
813
- // videoOptions getter/setter
814
- // ---------------------------------------------------------------------------
815
- describe("videoOptions", () => {
816
- let logOutput;
817
- beforeEach(() => {
818
- const capture = makeLogCapture();
819
- logOutput = capture.logOutput;
820
- logger_1.Logger.loggingLevel = logger_1.Logger.Levels.Warning;
821
- logger_1.Logger.logOutputCallback = capture.callback;
822
- logger_1.Logger.videoOptions = { height: 180, width: 320 };
823
- });
824
- it("accepts valid dimensions within limits", () => {
825
- logger_1.Logger.videoOptions = { height: 181, width: 320 };
826
- expect(logOutput).toHaveLength(0);
827
- expect(logger_1.Logger.videoOptions).toEqual({ videoCodec: "webm", height: 181, width: 320 });
828
- });
829
- it("accepts a codec-only change without altering dimensions", () => {
830
- logger_1.Logger.videoOptions = { videoCodec: "mp4" };
831
- expect(logOutput).toHaveLength(0);
832
- expect(logger_1.Logger.videoOptions).toEqual({ videoCodec: "mp4", height: 180, width: 320 });
833
- });
834
- it("accepts a height-only change without altering width", () => {
835
- logger_1.Logger.videoOptions = { height: 200 };
836
- expect(logOutput).toHaveLength(0);
837
- expect(logger_1.Logger.videoOptions).toEqual({ videoCodec: "webm", height: 200, width: 320 });
838
- });
839
- it("accepts a width-only change without altering height", () => {
840
- logger_1.Logger.videoOptions = { width: 640 };
841
- expect(logOutput).toHaveLength(0);
842
- expect(logger_1.Logger.videoOptions).toEqual({ videoCodec: "webm", height: 180, width: 640 });
843
- });
844
- it("rejects both dimensions when height is too low; existing values are preserved", () => {
845
- logger_1.Logger.videoOptions = { height: 179, width: 319 };
846
- expect(logOutput).toHaveLength(2);
847
- expect(logOutput[0]).toMatch(/WARNG.*Invalid video window height \[179\]/);
848
- expect(logOutput[1]).toMatch(/WARNG.*Invalid video window width \[319\]/);
849
- expect(logger_1.Logger.videoOptions).toEqual({ videoCodec: "webm", height: 180, width: 320 });
850
- });
851
- it("rejects both dimensions when height is too high; existing values are preserved", () => {
852
- logger_1.Logger.videoOptions = { height: 4321, width: 7681 };
853
- expect(logOutput).toHaveLength(2);
854
- expect(logOutput[0]).toMatch(/WARNG.*Invalid video window height \[4321\]/);
855
- expect(logOutput[1]).toMatch(/WARNG.*Invalid video window width \[7681\]/);
856
- });
857
- it("rejects only width when width is invalid; height and width both preserved", () => {
858
- logger_1.Logger.videoOptions = { height: 1000, width: 3 };
859
- expect(logOutput).toHaveLength(1);
860
- expect(logOutput[0]).toMatch(/WARNG.*Invalid video window width \[3\]/);
861
- expect(logger_1.Logger.videoOptions).toEqual({ videoCodec: "webm", height: 180, width: 320 });
862
- });
863
- it("rejects only height when height is invalid; height and width both preserved", () => {
864
- logger_1.Logger.videoOptions = { height: 2, width: 1000 };
865
- expect(logOutput).toHaveLength(1);
866
- expect(logOutput[0]).toMatch(/WARNG.*Invalid video window height \[2\]/);
867
- expect(logger_1.Logger.videoOptions).toEqual({ videoCodec: "webm", height: 180, width: 320 });
868
- });
869
- it("throws when a non-number is passed as a resolution value", () => {
870
- expect(() => {
871
- logger_1.Logger.isValidVideoResolutionNumber("not-a-number", 180, 4320, "bad value");
872
- }).toThrow(/Resolution number given was a <string>/);
873
- });
874
- });
875
- });