Skip to content

Commit 46eb6cf

Browse files
deepracticexsclaude
andcommitted
feat(logger): add test environment support v1.1.0
- Auto-detect test environments (vitest/jest) - In-memory log capture with inspection utilities - Silent by default, verbose mode support - New entry point: @deepracticex/logger/test - Export getTestLogs(), clearTestLogs(), getTestLogsByLevel() 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 949fadc commit 46eb6cf

9 files changed

Lines changed: 654 additions & 5 deletions

File tree

packages/logger/README.md

Lines changed: 142 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,34 @@ const logger = createLogger({
7272
logger.info("App initialized");
7373
```
7474

75+
### Test Environment (Vitest/Jest)
76+
77+
```typescript
78+
// Uses in-memory test adapter with log capture
79+
import {
80+
createLogger,
81+
getTestLogs,
82+
clearTestLogs,
83+
} from "@deepracticex/logger/test";
84+
85+
const logger = createLogger({
86+
level: "debug",
87+
name: "my-service",
88+
console: false, // Silent by default in tests
89+
});
90+
91+
// Your test code
92+
logger.info("test message");
93+
94+
// Assert logs were captured
95+
const logs = getTestLogs();
96+
expect(logs).toHaveLength(1);
97+
expect(logs[0].message).toBe("test message");
98+
99+
// Clean up between tests
100+
clearTestLogs();
101+
```
102+
75103
## Quick Start
76104

77105
### Default Logger (Node.js)
@@ -182,6 +210,64 @@ Uses browser-optimized console adapter:
182210
- Source map integration
183211
- DevTools friendly
184212

213+
### Test Environment
214+
215+
Uses in-memory test adapter:
216+
217+
- Auto-detected in vitest/jest (via `VITEST=true` or `NODE_ENV=test`)
218+
- Silent by default (no console output)
219+
- Captures all logs in memory for assertions
220+
- Zero I/O overhead for fast tests
221+
- Utilities: `getTestLogs()`, `clearTestLogs()`, `getTestLogsByLevel()`
222+
223+
**Usage in tests:**
224+
225+
```typescript
226+
import {
227+
createLogger,
228+
getTestLogs,
229+
clearTestLogs,
230+
} from "@deepracticex/logger/test";
231+
232+
describe("my feature", () => {
233+
const logger = createLogger();
234+
235+
afterEach(() => {
236+
clearTestLogs(); // Clean up between tests
237+
});
238+
239+
it("should log messages", () => {
240+
logger.info("test started");
241+
logger.warn("warning message");
242+
243+
const logs = getTestLogs();
244+
expect(logs).toHaveLength(2);
245+
expect(logs[0].level).toBe("info");
246+
});
247+
});
248+
```
249+
250+
**Auto-detection:**
251+
252+
When running in vitest, the default `@deepracticex/logger` import automatically uses the test adapter:
253+
254+
```typescript
255+
// Automatically uses test adapter in vitest
256+
import { createLogger } from "@deepracticex/logger";
257+
258+
const logger = createLogger(); // No console output in tests
259+
```
260+
261+
**Enable console output for debugging:**
262+
263+
```typescript
264+
// Show logs in test output
265+
const logger = createLogger({ console: true });
266+
267+
// Or run with verbose reporter
268+
// pnpm test -- --reporter=verbose
269+
```
270+
185271
## Examples
186272

187273
### Node.js Service
@@ -247,25 +333,35 @@ document.addEventListener("click", (e) => {
247333

248334
## Architecture
249335

250-
The logger uses a two-adapter architecture:
336+
The logger uses a multi-adapter architecture with automatic runtime detection:
251337

252338
1. **Pino Adapter** - For Node.js (high performance, file support)
253339
2. **Console Adapter** - For edge/browser (lightweight, universal)
340+
3. **Test Adapter** - For test environments (in-memory, inspectable)
254341

255342
Platform-specific entry points ensure only the necessary adapter is bundled:
256343

257344
```
258-
@deepracticex/logger → nodejs.tspino-adapter
345+
@deepracticex/logger → Auto-detectappropriate adapter
259346
@deepracticex/logger/nodejs → nodejs.ts → pino-adapter
260347
@deepracticex/logger/cloudflare-workers → cloudflare-workers.ts → console-adapter
261348
@deepracticex/logger/browser → browser.ts → console-adapter
349+
@deepracticex/logger/test → test.ts → test-adapter
262350
```
263351

352+
**Auto-detection priority:**
353+
354+
1. Test environment (VITEST=true or NODE_ENV=test) → test adapter
355+
2. Cloudflare Workers (caches API present) → console adapter
356+
3. Node.js (process.versions.node) → pino adapter
357+
4. Fallback → console adapter (browser)
358+
264359
## Bundle Size
265360

266361
- Node.js: Full pino functionality (~200KB with dependencies)
267362
- Cloudflare Workers: ~1.5KB (console adapter only)
268363
- Browser: ~1.5KB (console adapter only)
364+
- Test: ~2KB (test adapter with inspection utilities)
269365

270366
## FAQ
271367

@@ -310,6 +406,50 @@ import { createLogger } from "@deepracticex/logger/cloudflare-workers";
310406
import { createLogger } from "@deepracticex/logger/browser";
311407
```
312408

409+
### How do I use it in tests?
410+
411+
The logger automatically detects test environments (vitest/jest) and uses the test adapter:
412+
413+
```typescript
414+
// Automatically silent in tests
415+
import { createLogger } from "@deepracticex/logger";
416+
const logger = createLogger();
417+
418+
logger.info("message"); // Captured, but no console output
419+
```
420+
421+
To inspect logs in tests:
422+
423+
```typescript
424+
import {
425+
createLogger,
426+
getTestLogs,
427+
clearTestLogs,
428+
} from "@deepracticex/logger/test";
429+
430+
const logger = createLogger();
431+
logger.info("test message");
432+
433+
const logs = getTestLogs();
434+
expect(logs[0].message).toBe("test message");
435+
436+
clearTestLogs(); // Clean up
437+
```
438+
439+
### How do I see logs when debugging tests?
440+
441+
Use vitest's verbose reporter:
442+
443+
```bash
444+
pnpm test -- --reporter=verbose
445+
```
446+
447+
Or enable console output explicitly:
448+
449+
```typescript
450+
const logger = createLogger({ console: true });
451+
```
452+
313453
## License
314454

315455
MIT
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
Feature: Test Environment Support
2+
As a developer writing tests
3+
I want the logger to work seamlessly in test environments
4+
So that I can write reliable tests without polluting test output
5+
6+
Background:
7+
Given I am running in a test environment
8+
9+
Rule: Test environment should be auto-detected
10+
11+
Scenario: Auto-detect vitest environment
12+
Given VITEST environment variable is "true"
13+
When I create a logger without specifying environment
14+
Then the logger should use test adapter
15+
And logs should be captured in memory
16+
17+
Scenario: Explicitly specify test environment
18+
When I create a logger with environment "test"
19+
Then the logger should use test adapter
20+
And logs should be captured in memory
21+
22+
Rule: Logs should be captured in memory by default
23+
24+
Scenario: Log messages are captured silently
25+
Given I have a test logger
26+
When I log "test message" at level "info"
27+
And I log "warning message" at level "warn"
28+
And I log "error message" at level "error"
29+
Then captured logs should contain 3 entries
30+
And captured log at index 0 should have level "info"
31+
And captured log at index 0 should have message "test message"
32+
And captured log at index 1 should have level "warn"
33+
And captured log at index 2 should have level "error"
34+
35+
Scenario: Logs can be cleared between tests
36+
Given I have a test logger
37+
When I log "first message" at level "info"
38+
And I clear captured logs
39+
And I log "second message" at level "info"
40+
Then captured logs should contain 1 entries
41+
And captured log at index 0 should have message "second message"
42+
43+
Rule: Console output should be controllable
44+
45+
Scenario: Default silent mode in tests
46+
Given I have a test logger with default config
47+
When I log "test message" at level "info"
48+
Then the message should not be output to console
49+
And the message should be captured in memory
50+
51+
Scenario: Enable console output with config
52+
Given I have a test logger with console enabled
53+
When I log "test message" at level "info"
54+
Then the message should be output to console
55+
And the message should be captured in memory
56+
57+
Rule: Log inspection utilities should be available
58+
59+
Scenario: Get captured logs
60+
Given I have a test logger
61+
When I log "message 1" at level "info"
62+
And I log "message 2" at level "warn"
63+
And I get captured logs
64+
Then the result should be an array of 2 log entries
65+
And each entry should have level, message, and timestamp
66+
67+
Scenario: Filter logs by level
68+
Given I have a test logger
69+
When I log "info message" at level "info"
70+
And I log "warn message" at level "warn"
71+
And I log "error message" at level "error"
72+
And I get captured logs filtered by level "warn"
73+
Then the result should contain 1 entry
74+
And the entry should have message "warn message"
75+
76+
Rule: Test logger should support same API as production
77+
78+
Scenario: All log levels work in test environment
79+
Given I have a test logger
80+
When I call logger method "trace" with "trace msg"
81+
And I call logger method "debug" with "debug msg"
82+
And I call logger method "info" with "info msg"
83+
And I call logger method "warn" with "warn msg"
84+
And I call logger method "error" with "error msg"
85+
And I call logger method "fatal" with "fatal msg"
86+
Then captured logs should contain 6 entries
87+
And all standard log levels should be present
88+
89+
Scenario: Log with context objects
90+
Given I have a test logger
91+
When I log "user action" at level "info" with context:
92+
| key | value |
93+
| userId | 123 |
94+
| action | login |
95+
Then captured logs should contain the context data
96+
And the context should have "userId" equal to "123"
97+
And the context should have "action" equal to "login"
98+
99+
Rule: Integration with vitest reporter
100+
101+
Scenario: Verbose mode shows logs
102+
Given I have a test logger with console enabled
103+
And vitest is running with verbose reporter
104+
When I log "debug message" at level "debug"
105+
Then the message should be visible in test output
106+
107+
Scenario: Default mode hides logs
108+
Given I have a test logger with default config
109+
And vitest is running with default reporter
110+
When I log "debug message" at level "debug"
111+
Then the message should not be visible in test output
112+
And the message should still be captured in memory

packages/logger/package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@deepracticex/logger",
3-
"version": "1.0.2",
3+
"version": "1.1.0",
44
"description": "Unified logging system for Deepractice projects using Pino",
55
"type": "module",
66
"main": "./dist/index.js",
@@ -26,6 +26,11 @@
2626
"types": "./dist/browser.d.ts",
2727
"import": "./dist/browser.js",
2828
"require": "./dist/browser.cjs"
29+
},
30+
"./test": {
31+
"types": "./dist/test.d.ts",
32+
"import": "./dist/test.js",
33+
"require": "./dist/test.cjs"
2934
}
3035
},
3136
"scripts": {

packages/logger/src/core/adapter-factory.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@ import type { LoggerConfig, RuntimeEnvironment } from "~/types/config.js";
77
* Detect the current runtime environment
88
*/
99
export function detectEnvironment(): RuntimeEnvironment {
10+
// Priority 0: Check for test environment (vitest, jest, etc.)
11+
// Test environments should use in-memory test adapter
12+
if (
13+
typeof process !== "undefined" &&
14+
(process.env.VITEST === "true" ||
15+
process.env.JEST_WORKER_ID !== undefined ||
16+
process.env.NODE_ENV === "test")
17+
) {
18+
return "test";
19+
}
20+
1021
// Priority 1: Check for Cloudflare Workers specific globals
1122
// Workers have caches API - this is the most reliable indicator
1223
// If these globals exist, it's definitely Cloudflare Workers runtime
@@ -45,7 +56,11 @@ export async function createLoggerAdapter(
4556
): Promise<any> {
4657
const env = config.environment || detectEnvironment();
4758

48-
if (env === "nodejs") {
59+
if (env === "test") {
60+
// Test adapter for vitest/jest - in-memory, silent by default
61+
const { createTestLogger } = await import("./test-adapter.js");
62+
return createTestLogger(config);
63+
} else if (env === "nodejs") {
4964
// Use string concatenation to hide import from bundler static analysis
5065
// This prevents edge runtime bundlers from including Node.js-only dependencies
5166
const adapterPath = "./pino-adapter" + ".js";

0 commit comments

Comments
 (0)