0
# Virtual Console
1
2
The VirtualConsole class extends Node.js EventEmitter to capture console output from within jsdom. It captures both direct page output via `window.console` and jsdom implementation errors. This allows external code to monitor, log, or handle output generated inside the jsdom environment.
3
4
## Capabilities
5
6
### Constructor
7
8
Creates a new virtual console with no default behavior.
9
10
```javascript { .api }
11
/**
12
* Create a virtual console for capturing output
13
* Extends EventEmitter
14
*/
15
class VirtualConsole extends EventEmitter {
16
constructor();
17
}
18
```
19
20
**Usage Examples:**
21
22
```javascript
23
const { JSDOM, VirtualConsole } = require("jsdom");
24
25
// Create virtual console
26
const virtualConsole = new VirtualConsole();
27
28
// Add listeners before creating jsdom
29
virtualConsole.on("log", (message) => {
30
console.log("Page logged:", message);
31
});
32
33
const dom = new JSDOM(`
34
<script>console.log("Hello");</script>
35
`, {
36
runScripts: "dangerously",
37
virtualConsole: virtualConsole
38
});
39
// Logs: "Page logged: Hello"
40
```
41
42
### Methods
43
44
#### forwardTo()
45
46
```javascript { .api }
47
/**
48
* Forward console output to another console object
49
* @param anyConsole - Console object to forward to (e.g., Node.js console)
50
* @param options - Control jsdom error forwarding
51
* @param options.jsdomErrors - undefined (forward all), "none" (forward none), or array of error types to forward
52
* @returns This VirtualConsole instance for chaining
53
*/
54
forwardTo(
55
anyConsole: Console,
56
options?: { jsdomErrors?: undefined | "none" | string[] }
57
): VirtualConsole
58
```
59
60
Forwards all virtual console events to another console object. Iterates through the target console's methods and forwards matching events.
61
62
**Parameters:**
63
64
- **`anyConsole`**: Console object (typically Node.js `console`)
65
- **`options.jsdomErrors`** (optional):
66
- `undefined` (default): Forward all jsdom errors
67
- `"none"`: Don't forward any jsdom errors
68
- `string[]`: Array of error types to forward (e.g., `["unhandled-exception", "not-implemented"]`)
69
70
**Returns:** The VirtualConsole instance for chaining
71
72
**Usage Examples:**
73
74
```javascript
75
const { JSDOM, VirtualConsole } = require("jsdom");
76
77
// Forward everything to Node.js console
78
const virtualConsole = new VirtualConsole();
79
virtualConsole.forwardTo(console);
80
81
const dom = new JSDOM(`
82
<script>
83
console.log("Info message");
84
console.error("Error message");
85
</script>
86
`, {
87
runScripts: "dangerously",
88
virtualConsole: virtualConsole
89
});
90
// Both messages appear in Node.js console
91
92
// Forward console but not jsdom errors
93
const vc2 = new VirtualConsole();
94
vc2.forwardTo(console, { jsdomErrors: "none" });
95
96
// Forward only specific jsdom error types
97
const vc3 = new VirtualConsole();
98
vc3.forwardTo(console, {
99
jsdomErrors: ["unhandled-exception", "not-implemented"]
100
});
101
102
// Custom console object
103
const customConsole = {
104
log: (msg) => console.log("[Custom]", msg),
105
error: (msg) => console.error("[Custom Error]", msg)
106
};
107
108
const vc4 = new VirtualConsole();
109
vc4.forwardTo(customConsole);
110
```
111
112
### Console Events
113
114
VirtualConsole emits events for all standard console methods. Listen to these events to handle specific console output.
115
116
**Standard Console Events:**
117
118
```javascript { .api }
119
/**
120
* Console method events
121
*/
122
"log" | "warn" | "error" | "info" | "debug" | "trace" | "dir" | "dirxml" |
123
"assert" | "count" | "countReset" | "group" | "groupCollapsed" | "groupEnd" |
124
"table" | "time" | "timeLog" | "timeEnd" | "clear"
125
```
126
127
Each event receives the same arguments that were passed to the corresponding console method.
128
129
**Usage Examples:**
130
131
```javascript
132
const { JSDOM, VirtualConsole } = require("jsdom");
133
134
const virtualConsole = new VirtualConsole();
135
136
// Listen to specific events
137
virtualConsole.on("log", (...args) => {
138
console.log("LOG:", ...args);
139
});
140
141
virtualConsole.on("error", (...args) => {
142
console.error("ERROR:", ...args);
143
});
144
145
virtualConsole.on("warn", (...args) => {
146
console.warn("WARNING:", ...args);
147
});
148
149
virtualConsole.on("info", (...args) => {
150
console.log("INFO:", ...args);
151
});
152
153
const dom = new JSDOM(`
154
<script>
155
console.log("Message 1", "Message 2");
156
console.error("Error occurred");
157
console.warn("Warning!");
158
console.info("Information");
159
</script>
160
`, {
161
runScripts: "dangerously",
162
virtualConsole: virtualConsole
163
});
164
```
165
166
### jsdomError Event
167
168
Special event emitted for jsdom implementation errors, similar to how error messages show up in web browser consoles.
169
170
```javascript { .api }
171
/**
172
* jsdom implementation error event
173
* @param error - Error object with type, message, and additional properties
174
*/
175
"jsdomError"
176
```
177
178
**Event Object Properties:**
179
180
- **`type`**: Error type string
181
- **`message`**: Error message
182
- Additional properties depend on error type
183
184
#### Error Types
185
186
##### css-parsing
187
188
```javascript { .api }
189
/**
190
* CSS stylesheet parsing error
191
*/
192
interface CSSParsingError {
193
type: "css-parsing";
194
message: string;
195
/** Exception from rrweb-cssom parser */
196
cause: Error;
197
/** Full text of the stylesheet */
198
sheetText: string;
199
}
200
```
201
202
##### not-implemented
203
204
```javascript { .api }
205
/**
206
* Stub method called from unimplemented web platform parts
207
*/
208
interface NotImplementedError {
209
type: "not-implemented";
210
message: string;
211
}
212
```
213
214
##### resource-loading
215
216
```javascript { .api }
217
/**
218
* Resource fetch error
219
*/
220
interface ResourceLoadingError {
221
type: "resource-loading";
222
message: string;
223
/** Network error exception */
224
cause: Error;
225
/** URL that was attempted */
226
url: string;
227
}
228
```
229
230
##### unhandled-exception
231
232
```javascript { .api }
233
/**
234
* Unhandled script execution error
235
*/
236
interface UnhandledExceptionError {
237
type: "unhandled-exception";
238
message: string;
239
/** Original exception object */
240
cause: Error;
241
}
242
```
243
244
**Usage Examples:**
245
246
```javascript
247
const { JSDOM, VirtualConsole } = require("jsdom");
248
249
const virtualConsole = new VirtualConsole();
250
251
// Handle all jsdom errors
252
virtualConsole.on("jsdomError", (error) => {
253
console.error(`jsdom error [${error.type}]:`, error.message);
254
255
switch (error.type) {
256
case "unhandled-exception":
257
console.error("Stack trace:", error.cause.stack);
258
break;
259
case "css-parsing":
260
console.error("Failed stylesheet:", error.sheetText.substring(0, 100));
261
break;
262
case "resource-loading":
263
console.error("Failed to load:", error.url);
264
break;
265
case "not-implemented":
266
// Ignore or log not-implemented warnings
267
break;
268
}
269
});
270
271
const dom = new JSDOM(`
272
<!DOCTYPE html>
273
<style>invalid css { { {</style>
274
<script>
275
// Unhandled exception
276
throw new Error("Something went wrong");
277
</script>
278
`, {
279
runScripts: "dangerously",
280
virtualConsole: virtualConsole
281
});
282
```
283
284
### Default VirtualConsole Behavior
285
286
When no `virtualConsole` option is provided to JSDOM constructor, a default VirtualConsole is created and forwarded to Node.js console.
287
288
```javascript
289
// Default behavior is equivalent to:
290
const virtualConsole = new VirtualConsole();
291
virtualConsole.forwardTo(console);
292
```
293
294
**Default jsdom Error Handling:**
295
296
- `"unhandled-exception"`: Logs `error.cause.stack` via `console.error()`
297
- Other errors: Logs `error.message` via `console.error()`
298
299
## Common Usage Patterns
300
301
### Capturing All Output
302
303
```javascript
304
const { JSDOM, VirtualConsole } = require("jsdom");
305
306
const virtualConsole = new VirtualConsole();
307
const logs = [];
308
309
virtualConsole.on("log", (...args) => {
310
logs.push({ type: "log", args });
311
});
312
313
virtualConsole.on("error", (...args) => {
314
logs.push({ type: "error", args });
315
});
316
317
const dom = new JSDOM(`
318
<script>
319
console.log("Test 1");
320
console.error("Test 2");
321
</script>
322
`, {
323
runScripts: "dangerously",
324
virtualConsole: virtualConsole
325
});
326
327
console.log(logs);
328
// [
329
// { type: "log", args: ["Test 1"] },
330
// { type: "error", args: ["Test 2"] }
331
// ]
332
```
333
334
### Filtering jsdom Errors
335
336
```javascript
337
const { JSDOM, VirtualConsole } = require("jsdom");
338
339
const virtualConsole = new VirtualConsole();
340
341
// Forward console methods but not jsdom errors
342
virtualConsole.forwardTo(console, { jsdomErrors: "none" });
343
344
// Handle jsdom errors separately
345
virtualConsole.on("jsdomError", (error) => {
346
// Only log serious errors
347
if (error.type === "unhandled-exception") {
348
console.error("Script error:", error.cause.stack);
349
}
350
// Ignore css-parsing, resource-loading, not-implemented
351
});
352
353
const dom = new JSDOM(`<!DOCTYPE html><script>console.log("Hi");</script>`, {
354
runScripts: "dangerously",
355
virtualConsole: virtualConsole
356
});
357
```
358
359
### Testing with Virtual Console
360
361
```javascript
362
const { JSDOM, VirtualConsole } = require("jsdom");
363
364
function testPageConsole() {
365
const virtualConsole = new VirtualConsole();
366
const errors = [];
367
368
virtualConsole.on("error", (...args) => {
369
errors.push(args);
370
});
371
372
const dom = new JSDOM(`
373
<script>
374
console.error("Expected error");
375
console.log("This is fine");
376
</script>
377
`, {
378
runScripts: "dangerously",
379
virtualConsole: virtualConsole
380
});
381
382
// Assert expected errors occurred
383
if (errors.length !== 1 || errors[0][0] !== "Expected error") {
384
throw new Error("Test failed");
385
}
386
387
console.log("Test passed");
388
}
389
390
testPageConsole();
391
```
392
393
### Silent Mode
394
395
```javascript
396
const { JSDOM, VirtualConsole } = require("jsdom");
397
398
// Create virtual console with no forwarding
399
const virtualConsole = new VirtualConsole();
400
// Don't call forwardTo() or add listeners
401
402
const dom = new JSDOM(`
403
<script>
404
console.log("This won't appear anywhere");
405
console.error("Neither will this");
406
</script>
407
`, {
408
runScripts: "dangerously",
409
virtualConsole: virtualConsole
410
});
411
412
// All console output is silenced
413
```
414
415
### Custom Logging Format
416
417
```javascript
418
const { JSDOM, VirtualConsole } = require("jsdom");
419
420
const virtualConsole = new VirtualConsole();
421
422
const customConsole = {
423
log: (...args) => {
424
const timestamp = new Date().toISOString();
425
console.log(`[${timestamp}] [LOG]`, ...args);
426
},
427
error: (...args) => {
428
const timestamp = new Date().toISOString();
429
console.error(`[${timestamp}] [ERROR]`, ...args);
430
},
431
warn: (...args) => {
432
const timestamp = new Date().toISOString();
433
console.warn(`[${timestamp}] [WARN]`, ...args);
434
}
435
};
436
437
virtualConsole.forwardTo(customConsole);
438
439
const dom = new JSDOM(`
440
<script>
441
console.log("Application started");
442
console.warn("Deprecated API used");
443
console.error("Something failed");
444
</script>
445
`, {
446
runScripts: "dangerously",
447
virtualConsole: virtualConsole
448
});
449
```
450
451
## Types
452
453
```javascript { .api }
454
interface CSSParsingError {
455
type: "css-parsing";
456
message: string;
457
cause: Error;
458
sheetText: string;
459
}
460
461
interface NotImplementedError {
462
type: "not-implemented";
463
message: string;
464
}
465
466
interface ResourceLoadingError {
467
type: "resource-loading";
468
message: string;
469
cause: Error;
470
url: string;
471
}
472
473
interface UnhandledExceptionError {
474
type: "unhandled-exception";
475
message: string;
476
cause: Error;
477
}
478
479
type JSDOMError =
480
| CSSParsingError
481
| NotImplementedError
482
| ResourceLoadingError
483
| UnhandledExceptionError;
484
```
485