Skip to content

Commit 9aa5570

Browse files
committed
Unit test case for log capture
1 parent 3264a26 commit 9aa5570

1 file changed

Lines changed: 175 additions & 0 deletions

File tree

packages/service/tests/session.test.ts

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,181 @@ describe('SessionCapturer', () => {
189189
})
190190
})
191191

192+
describe('console log capture', () => {
193+
/**
194+
* Test: All console methods (log, info, warn, error) are properly captured
195+
* Validates: method type, arguments, source attribution, and timestamp
196+
*/
197+
it('should capture all console methods from test code', () => {
198+
const capturer = new SessionCapturer()
199+
const initialLength = capturer.consoleLogs.length
200+
201+
// Execute all console methods with different argument patterns
202+
console.log('Log message')
203+
console.info('Info message', 'with multiple', 'arguments')
204+
console.warn('Warning message')
205+
console.error('Error message')
206+
207+
// Verify all 4 logs were captured
208+
expect(capturer.consoleLogs).toHaveLength(initialLength + 4)
209+
210+
// Validate console.log capture
211+
const logEntry = capturer.consoleLogs[initialLength]
212+
expect(logEntry.type).toBe('log')
213+
expect(logEntry.args).toEqual(['Log message'])
214+
expect(logEntry.source).toBe('test')
215+
expect(logEntry.timestamp).toBeDefined()
216+
217+
// Validate console.info capture with multiple arguments
218+
const infoEntry = capturer.consoleLogs[initialLength + 1]
219+
expect(infoEntry.type).toBe('info')
220+
expect(infoEntry.args).toEqual([
221+
'Info message',
222+
'with multiple',
223+
'arguments'
224+
])
225+
expect(infoEntry.source).toBe('test')
226+
227+
// Validate console.warn capture
228+
const warnEntry = capturer.consoleLogs[initialLength + 2]
229+
expect(warnEntry.type).toBe('warn')
230+
expect(warnEntry.args).toEqual(['Warning message'])
231+
expect(warnEntry.source).toBe('test')
232+
233+
// Validate console.error capture
234+
const errorEntry = capturer.consoleLogs[initialLength + 3]
235+
expect(errorEntry.type).toBe('error')
236+
expect(errorEntry.args).toEqual(['Error message'])
237+
expect(errorEntry.source).toBe('test')
238+
})
239+
240+
/**
241+
* Test: Complex argument types are handled correctly
242+
* Validates: object serialization, circular reference handling, null/undefined conversion
243+
*/
244+
it('should handle various argument types', () => {
245+
const capturer = new SessionCapturer()
246+
const initialLength = capturer.consoleLogs.length
247+
248+
// Create test data: object, circular reference
249+
const testObject = { foo: 'bar', nested: { value: 42 } }
250+
const circular: any = { a: 1 }
251+
circular.self = circular
252+
253+
// Log various argument types in sequence
254+
console.log('Object:', testObject)
255+
console.log('Circular:', circular)
256+
console.log('Values:', null, undefined)
257+
258+
expect(capturer.consoleLogs).toHaveLength(initialLength + 3)
259+
260+
// Verify object is stringified to JSON
261+
const objLog = capturer.consoleLogs[initialLength]
262+
expect(objLog.args[0]).toBe('Object:')
263+
expect(objLog.args[1]).toBe(JSON.stringify(testObject))
264+
265+
// Verify circular references don't crash and fallback to [object Object]
266+
const circularLog = capturer.consoleLogs[initialLength + 1]
267+
expect(circularLog.args[1]).toBe('[object Object]')
268+
269+
// Verify null and undefined are converted to strings
270+
const nullLog = capturer.consoleLogs[initialLength + 2]
271+
expect(nullLog.args).toEqual(['Values:', 'null', 'undefined'])
272+
})
273+
274+
/**
275+
* Test: Integration scenarios - cleanup, WebSocket transmission, browser source attribution
276+
* Validates: console restoration on cleanup, upstream WebSocket communication, source distinction
277+
*/
278+
it('should handle cleanup, upstream transmission, and browser source attribution', async () => {
279+
// Part 1: Test console restoration on cleanup
280+
const originalLog = console.log
281+
const originalInfo = console.info
282+
const originalWarn = console.warn
283+
const originalError = console.error
284+
285+
let capturer = new SessionCapturer()
286+
expect(console.log).not.toBe(originalLog)
287+
288+
capturer.cleanup()
289+
expect(console.log).toBe(originalLog)
290+
expect(console.info).toBe(originalInfo)
291+
expect(console.warn).toBe(originalWarn)
292+
expect(console.error).toBe(originalError)
293+
294+
// Part 2: Test WebSocket upstream transmission
295+
const mockWs = {
296+
readyState: WebSocket.OPEN,
297+
send: vi.fn(),
298+
on: vi.fn(),
299+
close: vi.fn()
300+
}
301+
302+
vi.mocked(WebSocket).mockImplementation(function (this: any) {
303+
return mockWs
304+
} as any)
305+
306+
capturer = new SessionCapturer({
307+
hostname: 'localhost',
308+
port: 3000
309+
})
310+
311+
console.log('Test message')
312+
313+
expect(mockWs.send).toHaveBeenCalled()
314+
const sentData = JSON.parse(
315+
mockWs.send.mock.calls[mockWs.send.mock.calls.length - 1][0]
316+
)
317+
expect(sentData.scope).toBe('consoleLogs')
318+
expect(sentData.data).toHaveLength(1)
319+
expect(sentData.data[0].args).toEqual(['Test message'])
320+
expect(sentData.data[0].source).toBe('test')
321+
322+
capturer.cleanup()
323+
324+
// Part 3: Test browser console logs source attribution
325+
capturer = new SessionCapturer()
326+
const mockBrowserLogs = [
327+
{
328+
timestamp: Date.now(),
329+
type: 'log' as const,
330+
args: ['Browser log 1']
331+
},
332+
{
333+
timestamp: Date.now(),
334+
type: 'warn' as const,
335+
args: ['Browser warning']
336+
}
337+
]
338+
339+
const mockExecuteResult = {
340+
mutations: [],
341+
traceLogs: [],
342+
consoleLogs: mockBrowserLogs,
343+
metadata: { url: 'http://test.com', viewport: {} as VisualViewport }
344+
}
345+
346+
mockBrowser.execute = vi.fn().mockResolvedValue(mockExecuteResult)
347+
mockBrowser.scriptAddPreloadScript = vi.fn().mockResolvedValue(undefined)
348+
mockBrowser.isBidi = true
349+
await capturer.injectScript(mockBrowser)
350+
await capturer.afterCommand(
351+
mockBrowser,
352+
'url' as any,
353+
['http://test.com'],
354+
undefined,
355+
undefined
356+
)
357+
358+
const browserLogs = capturer.consoleLogs.filter(
359+
(log) => log.source === 'browser'
360+
)
361+
expect(browserLogs).toHaveLength(2)
362+
expect(browserLogs[0].source).toBe('browser')
363+
expect(browserLogs[1].source).toBe('browser')
364+
})
365+
})
366+
192367
describe('integration', () => {
193368
it('should handle complete session capture workflow', async () => {
194369
const capturer = new SessionCapturer()

0 commit comments

Comments
 (0)