- 
                Notifications
    You must be signed in to change notification settings 
- Fork 18
TestContext for E2E Debugging #1312
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
          
     Open
      
      
            LittorWired
  wants to merge
  48
  commits into
  main
  
    
      
        
          
  
    
      Choose a base branch
      
     
    
      
        
      
      
        
          
          
        
        
          
            
              
              
              
  
           
        
        
          
            
              
              
           
        
       
     
  
        
          
            
          
            
          
        
       
    
      
from
tl/cp-16075-e2e-test-context
  
      
      
   
  
    
  
  
  
 
  
      
    base: main
Could not load branches
            
              
  
    Branch not found: {{ refName }}
  
            
                
      Loading
              
            Could not load tags
            
            
              Nothing to show
            
              
  
            
                
      Loading
              
            Are you sure you want to change the base?
            Some commits from the old base branch may be removed from the timeline,
            and old review comments may become outdated.
          
          
      
        
          +3,662
        
        
          −1,149
        
        
          
        
      
    
  
  
     Open
                    Changes from all commits
      Commits
    
    
            Show all changes
          
          
            48 commits
          
        
        Select commit
          Hold shift + click to select a range
      
      2428fc3
              
                fix waitForFunction bugs with parameters being incorrect
              
              
                LittorWired 4875112
              
                in expectToPass use the correct intervals param to fix polling issues
              
              
                LittorWired 766444a
              
                add polling unit tests for the utils expectToPass and expectPageEvalT…
              
              
                LittorWired 591ab3c
              
                implement the unit tests for the waitForFunction utility with polling…
              
              
                LittorWired 1835909
              
                refactor address.spec.ts tests to use the expectPageEvalToPass utility
              
              
                LittorWired 09fec22
              
                refactor expectCFFinalEvents to utilize expectPageEvalToPass and allo…
              
              
                LittorWired 12663d0
              
                fix type linting issues
              
              
                LittorWired b556e92
              
                refactor agent_customer.spec.ts tests to utilize expectPageEvalToPass
              
              
                LittorWired 7f33331
              
                refactor the first test in audioFlags.spec.ts to utilize expectPageEv…
              
              
                LittorWired b429a13
              
                refactor audioFlags.spec.ts to improve test structure and utilize exp…
              
              
                LittorWired 4e1856c
              
                remove unused type
              
              
                LittorWired d0a1b86
              
                refactor cleanup.spec.ts to improve test structure, utilize expectPag…
              
              
                LittorWired 53bdd00
              
                refactor conversation.spec.ts to utilize expectPageEvalToPass for ass…
              
              
                LittorWired c51cd3c
              
                refactor deviceEvent.spec.ts to utilize expectPageEvalToPass
              
              
                LittorWired e29bc0e
              
                fix util tests after fixing intervals param
              
              
                LittorWired 42ee800
              
                refactor deviceState.spec.ts to utilize expectPageEvalToPass
              
              
                LittorWired fbad4bb
              
                refactor dialAddress e2e utility to use multiple expectPageEvalToPass…
              
              
                LittorWired f5bfb37
              
                increase SW client loglevel to debug
              
              
                LittorWired 22f15eb
              
                break up tests into multiple projects
              
              
                LittorWired e65ad3b
              
                update the list of projects in github workflow matrix
              
              
                LittorWired 3583d15
              
                update playwright names for better debugging
              
              
                LittorWired ac6602b
              
                update the github project matrix names
              
              
                LittorWired f327b73
              
                dedupe projects
              
              
                LittorWired 13872f9
              
                increase the expectCFFinalEvents timeout
              
              
                LittorWired 52123b3
              
                attach listener before call.start
              
              
                LittorWired a910632
              
                attach listener for call.play finished before playback stop
              
              
                LittorWired 46a380e
              
                add optional timeout parameter to expectCFFinalEvents for optionally …
              
              
                LittorWired 5faaacd
              
                refactor the tts audio swml e2e test to utilize the expectPageEvalToP…
              
              
                LittorWired 619fea3
              
                remove import
              
              
                LittorWired dabec90
              
                Merge remote-tracking branch 'origin/main' into tl/cp-15995-refactor-…
              
              
                LittorWired 96a7772
              
                Merge remote-tracking branch 'origin/main' into tl/cp-15995-refactor-…
              
              
                LittorWired 5e84770
              
                implement TestContext class for tracking SDK and server events during…
              
              
                LittorWired b66b5f8
              
                add WebSocket monitoring and test context utilities for the testConte…
              
              
                LittorWired abf3c19
              
                add to the Playwright TestNameReporter to include SDK Test Context du…
              
              
                LittorWired 37b0a23
              
                extend Playwright test fixture to include test context setup and atta…
              
              
                LittorWired 6f778c3
              
                PR feedback: await client.rettach call
              
              
                LittorWired 2045e01
              
                Add isSerializable utility function to check for serializable values …
              
              
                LittorWired 7d35c31
              
                tests for isSerializable utility function for e2e tests
              
              
                LittorWired f60f4ca
              
                add serialization check to  expectPageEvalToPass
              
              
                LittorWired 1866875
              
                fix bug on argument check for expectPageEvalToPass
              
              
                LittorWired 0a76e14
              
                revert the serializable checks
              
              
                LittorWired 8d1d355
              
                revert the serializable checks
              
              
                LittorWired 2bcb78a
              
                PR feedback: address PR feedback on call.reattach and check call inst…
              
              
                LittorWired 4f7a37c
              
                revert the isCallSession checks as they fail
              
              
                LittorWired 7255603
              
                PR feedback: assertions against the callSession.id
              
              
                LittorWired 7524aa2
              
                Merge branch 'tl/cp-15995-refactor-e2e-client-tests-new-util-pattern'…
              
              
                LittorWired d0e84da
              
                PR feedback: modify categories to be transport/WS, connection, callSe…
              
              
                LittorWired 1be5820
              
                PR Feedback: use SDKEvent interface
              
              
                LittorWired File filter
Filter by extension
Conversations
          Failed to load comments.   
        
        
          
      Loading
        
  Jump to
        
          Jump to file
        
      
      
          Failed to load files.   
        
        
          
      Loading
        
  Diff view
Diff view
There are no files selected for viewing
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,307 @@ | ||
| import { randomUUID } from 'crypto' | ||
| import type { JSONRPCMethod } from '@signalwire/core/' | ||
|  | ||
| type EventCategory = | ||
| | 'transport' | ||
| | 'connection' | ||
| | 'callSession' | ||
| | 'conversations' | ||
| | 'tests' | ||
|  | ||
| type Event = | ||
| | 'websocket_open' | ||
| | 'websocket_close' | ||
| | 'websocket_error' | ||
| | 'websocket_message' | ||
| | 'websocket_closed' | ||
|  | ||
| type Params = { | ||
| call_id?: string | ||
| room_session?: { | ||
| id?: string | ||
| } | ||
| } | ||
|  | ||
| type Result = { | ||
| call_id?: string | ||
| room_session?: { | ||
| id?: string | ||
| } | ||
| } | ||
|  | ||
| type Metadata = Record<string, any> | ||
|  | ||
| type Payload = { | ||
| error?: any | ||
| method?: JSONRPCMethod | ||
| event?: Event | ||
| params?: Params | ||
| result?: Result | ||
| metadata?: Metadata | ||
| } | ||
|  | ||
| export class SDKEvent { | ||
| constructor( | ||
| public id: string, | ||
| public timestamp: number, | ||
| public direction: 'send' | 'recv', | ||
| public category: 'request' | 'response' | 'notification' | 'connection', | ||
| public eventType: string, | ||
| public eventCategory: EventCategory, | ||
| public payload: Payload, | ||
| public context: { | ||
| isCallEvent: boolean | ||
| isRoomEvent: boolean | ||
| isConnectionEvent: boolean | ||
| isError: boolean | ||
| callId?: string | ||
| roomId?: string | ||
| }, | ||
| public method?: JSONRPCMethod, | ||
| public metadata?: Metadata | ||
| ) {} | ||
|  | ||
| // Utility methods for common checks | ||
| isCallRelated(): boolean { | ||
| return this.context.isCallEvent | ||
| } | ||
|  | ||
| isRoomRelated(): boolean { | ||
| return this.context.isRoomEvent | ||
| } | ||
|  | ||
| isConnectionRelated(): boolean { | ||
| return this.context.isConnectionEvent | ||
| } | ||
|  | ||
| isError(): boolean { | ||
| return this.context.isError | ||
| } | ||
|  | ||
| isSent(): boolean { | ||
| return this.direction === 'send' | ||
| } | ||
|  | ||
| isReceived(): boolean { | ||
| return this.direction === 'recv' | ||
| } | ||
|  | ||
| // Category checks | ||
| isTransport(): boolean { | ||
| return this.eventCategory === 'transport' | ||
| } | ||
|  | ||
| isConnection(): boolean { | ||
| return this.eventCategory === 'connection' | ||
| } | ||
|  | ||
| isCallSession(): boolean { | ||
| return this.eventCategory === 'callSession' | ||
| } | ||
|  | ||
| isConversations(): boolean { | ||
| return this.eventCategory === 'conversations' | ||
| } | ||
|  | ||
| isTests(): boolean { | ||
| return this.eventCategory === 'tests' | ||
| } | ||
| } | ||
|  | ||
| export interface EventStats { | ||
| totalEvents: number | ||
| sentEvents: number | ||
| receivedEvents: number | ||
| errorEvents: number | ||
| callEvents: number | ||
| roomEvents: number | ||
| connectionEvents: number | ||
| // Category-based counts | ||
| transportCategoryEvents: number | ||
| connectionCategoryEvents: number | ||
| callSessionCategoryEvents: number | ||
| conversationsCategoryEvents: number | ||
| testsCategoryEvents: number | ||
| } | ||
|  | ||
| /** | ||
| * TestContext class for tracking SDK events during e2e tests. | ||
| * | ||
| * Usage: | ||
| * - Automatically set up via Playwright fixtures | ||
| * - Events are captured via WebSocket monitoring | ||
| * - Context dumped to console and attachments on test failures | ||
| */ | ||
| export class TestContext { | ||
| private sdkEvents: SDKEvent[] = [] | ||
| private startTime: number = Date.now() | ||
|  | ||
| addSDKEvent(payload: Payload, direction: 'send' | 'recv') { | ||
| const event = this.categorizeSDKEvent(payload, direction) | ||
| this.sdkEvents.push(event) | ||
| } | ||
|  | ||
| /** | ||
| * Get comprehensive statistics about captured events. | ||
| * Includes both legacy boolean-based counts and new category-based counts. | ||
| */ | ||
| getStats() { | ||
| return { | ||
| totalEvents: this.sdkEvents.length, | ||
| sentEvents: this.sdkEvents.filter((event) => event.isSent()).length, | ||
| receivedEvents: this.sdkEvents.filter((event) => event.isReceived()) | ||
| .length, | ||
| errorEvents: this.sdkEvents.filter((event) => event.isError()).length, | ||
| callEvents: this.sdkEvents.filter((event) => event.isCallRelated()) | ||
| .length, | ||
| roomEvents: this.sdkEvents.filter((event) => event.isRoomRelated()) | ||
| .length, | ||
| connectionEvents: this.sdkEvents.filter((event) => | ||
| event.isConnectionRelated() | ||
| ).length, | ||
| // Category-based counts | ||
| transportCategoryEvents: this.sdkEvents.filter((event) => | ||
| event.isTransport() | ||
| ).length, | ||
| connectionCategoryEvents: this.sdkEvents.filter((event) => | ||
| event.isConnection() | ||
| ).length, | ||
| callSessionCategoryEvents: this.sdkEvents.filter((event) => | ||
| event.isCallSession() | ||
| ).length, | ||
| conversationsCategoryEvents: this.sdkEvents.filter((event) => | ||
| event.isConversations() | ||
| ).length, | ||
| testsCategoryEvents: this.sdkEvents.filter((event) => event.isTests()) | ||
| .length, | ||
| } | ||
| } | ||
|  | ||
| getAllEvents() { | ||
| return [...this.sdkEvents] | ||
| } | ||
|  | ||
| getTestDuration() { | ||
| return Date.now() - this.startTime | ||
| } | ||
|  | ||
| private categorizeSDKEvent( | ||
| payload: Payload, | ||
| direction: 'send' | 'recv' | ||
| ): SDKEvent { | ||
| const timestamp = Date.now() | ||
| const id = `${timestamp}-${randomUUID()}` | ||
|  | ||
| const method = payload.method || '' | ||
|  | ||
| const context = { | ||
| isCallEvent: method.includes('call.') || method.includes('calling.'), | ||
| isRoomEvent: | ||
| method.includes('room.') || | ||
| method.includes('member.') || | ||
| method.includes('layout.'), | ||
| isConnectionEvent: | ||
| method.startsWith('signalwire.') || method.startsWith('subscriber.'), | ||
| isError: Boolean(payload.error), | ||
| callId: payload.params?.call_id || payload.result?.call_id, | ||
| roomId: | ||
| payload.params?.room_session?.id || payload.result?.room_session?.id, | ||
| } | ||
|  | ||
| return new SDKEvent( | ||
| id, | ||
| timestamp, | ||
| direction, | ||
| this.getCategory(payload), | ||
| this.getEventType(payload), | ||
| this.categorizeMethod(payload), | ||
| payload, | ||
| context, | ||
| payload.method, | ||
| payload.metadata | ||
| ) | ||
| } | ||
|  | ||
| private getCategory(payload: Payload) { | ||
| // JSON-RPC 2.0 specification: | ||
| // - Response: Has 'id' and ('result' OR 'error') | ||
| // - Request: Has 'method' and 'id' | ||
| // - Notification: Has 'method' but no 'id' | ||
| const hasId = 'id' in payload | ||
| const hasMethod = 'method' in payload | ||
| const hasResult = 'result' in payload | ||
| const hasError = 'error' in payload | ||
|  | ||
| // Response: has id and (result or error), typically no method | ||
| if (hasId && (hasResult || hasError)) { | ||
| return 'response' as const | ||
| } | ||
|  | ||
| // Request: has both method and id | ||
| if (hasMethod && hasId) { | ||
| return 'request' as const | ||
| } | ||
|  | ||
| // Notification: has method but no id | ||
| if (hasMethod && !hasId) { | ||
| return 'notification' as const | ||
| } | ||
|  | ||
| // Fallback for connection events (websocket events, etc.) | ||
| return 'connection' as const | ||
| } | ||
|  | ||
| private categorizeMethod(payload: Payload): EventCategory { | ||
| const method = payload.method || '' | ||
| const event = payload.event || '' | ||
|  | ||
| if ( | ||
| event === 'websocket_open' || | ||
| event === 'websocket_close' || | ||
| event === 'websocket_error' || | ||
| event === 'websocket_message' || | ||
| event === 'websocket_closed' | ||
| ) { | ||
| return 'transport' | ||
| } | ||
|  | ||
| // SignalWire connection events | ||
| if (method.startsWith('signalwire.') || method.startsWith('subscriber.')) { | ||
| return 'connection' | ||
| } | ||
|  | ||
| // Conversations events | ||
| if (method.startsWith('conversations.')) { | ||
| return 'conversations' | ||
| } | ||
|  | ||
| // Test events (artificial events) | ||
| if (method.startsWith('test.') || method.includes('artificial')) { | ||
| return 'tests' | ||
| } | ||
|  | ||
| // Everything else is callSession (call, room, member, chat, verto, voice, etc.) | ||
| // Note: verto.* are call signaling events, not transport events | ||
| return 'callSession' | ||
| } | ||
|  | ||
| private getEventType(payload: { | ||
| error?: any | ||
| method?: JSONRPCMethod | ||
| event?: Event | ||
| }) { | ||
| if (payload.error) { | ||
| return payload.error.message | ||
| } | ||
|  | ||
| if (payload.method) { | ||
| return payload.method | ||
| } | ||
|  | ||
| if (payload.event) { | ||
| return payload.event | ||
| } | ||
|  | ||
| return 'unknown' | ||
| } | ||
| } | ||
      
      Oops, something went wrong.
        
    
  
      
      Oops, something went wrong.
        
    
  
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could have a concrete implementation for the SDKEvent interface with these utilities
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on the feedback, I implemented the
SDKEventas its own class.