@@ -42,9 +42,9 @@ export const suiteContext = createContext<Record<string, any>[]>(
4242
4343const hasConnection = createContext < boolean > ( Symbol ( 'hasConnection' ) )
4444
45- interface SocketMessage < T extends keyof TraceLog = keyof TraceLog > {
45+ interface SocketMessage < T extends keyof TraceLog | 'testStopped' = keyof TraceLog | 'testStopped' > {
4646 scope : T
47- data : TraceLog [ T ]
47+ data : T extends keyof TraceLog ? TraceLog [ T ] : unknown
4848}
4949
5050export class DataManagerController implements ReactiveController {
@@ -103,17 +103,113 @@ export class DataManagerController implements ReactiveController {
103103 }
104104
105105 // Public method to clear execution data when rerun is triggered
106- clearExecutionData ( ) {
107- this . mutationsContextProvider . setValue ( [ ] )
108- this . commandsContextProvider . setValue ( [ ] )
109- this . logsContextProvider . setValue ( [ ] )
110- this . consoleLogsContextProvider . setValue ( [ ] )
106+ clearExecutionData ( uid ?: string ) {
107+ this . #resetExecutionData( )
108+ if ( uid ) {
109+ this . #markTestAsRunning( uid )
110+ }
111+ }
112+
113+ // Private method to mark a test/suite as running immediately for UI feedback
114+ #markTestAsRunning( uid : string ) {
115+ const suites = this . suitesContextProvider . value || [ ]
116+
117+ // If uid is '*', mark ALL tests/suites as running
118+ if ( uid === '*' ) {
119+ const updatedSuites = suites . map ( ( chunk ) => {
120+ const updatedChunk : Record < string , SuiteStatsFragment > = { }
121+ Object . entries ( chunk as Record < string , SuiteStatsFragment > ) . forEach (
122+ ( [ suiteUid , suite ] ) => {
123+ if ( ! suite ) {
124+ updatedChunk [ suiteUid ] = suite
125+ return
126+ }
127+
128+ const markAllAsRunning = ( s : SuiteStatsFragment ) : SuiteStatsFragment => {
129+ return {
130+ ...s ,
131+ start : new Date ( ) ,
132+ end : undefined ,
133+ tests : s . tests ?. map ( ( test ) => ( {
134+ ...test ,
135+ start : new Date ( ) ,
136+ end : undefined
137+ } ) ) || [ ] ,
138+ suites : s . suites ?. map ( markAllAsRunning ) || [ ]
139+ }
140+ }
141+
142+ updatedChunk [ suiteUid ] = markAllAsRunning ( suite )
143+ }
144+ )
145+ return updatedChunk
146+ } )
147+ this . suitesContextProvider . setValue ( updatedSuites )
148+ this . #host. requestUpdate ( )
149+ return
150+ }
151+
152+ // Otherwise, mark specific test/suite as running
153+ const updatedSuites = suites . map ( ( chunk ) => {
154+ const updatedChunk : Record < string , SuiteStatsFragment > = { }
155+ Object . entries ( chunk as Record < string , SuiteStatsFragment > ) . forEach (
156+ ( [ suiteUid , suite ] ) => {
157+ if ( ! suite ) {
158+ updatedChunk [ suiteUid ] = suite
159+ return
160+ }
161+
162+ // Recursive helper to mark tests/suites as running
163+ const markAsRunning = ( s : SuiteStatsFragment ) : SuiteStatsFragment => {
164+ // If this is the target suite/test, mark it as running
165+ if ( s . uid === uid ) {
166+ return {
167+ ...s ,
168+ start : new Date ( ) ,
169+ end : undefined , // Clear end to mark as running
170+ tests : s . tests ?. map ( ( test ) => ( {
171+ ...test ,
172+ start : new Date ( ) ,
173+ end : undefined
174+ } ) ) || [ ] ,
175+ suites : s . suites ?. map ( markAsRunning ) || [ ]
176+ }
177+ }
178+
179+ // Check if any child test matches
180+ const updatedTests = s . tests ?. map ( ( test ) => {
181+ if ( test . uid === uid ) {
182+ return {
183+ ...test ,
184+ start : new Date ( ) ,
185+ end : undefined
186+ }
187+ }
188+ return test
189+ } )
190+
191+ // Recursively check nested suites
192+ const updatedNestedSuites = s . suites ?. map ( markAsRunning )
193+
194+ return {
195+ ...s ,
196+ tests : updatedTests || [ ] ,
197+ suites : updatedNestedSuites || [ ]
198+ }
199+ }
200+
201+ updatedChunk [ suiteUid ] = markAsRunning ( suite )
202+ }
203+ )
204+ return updatedChunk
205+ } )
206+
207+ this . suitesContextProvider . setValue ( updatedSuites )
111208 this . #host. requestUpdate ( )
112209 }
113210
114211 hostConnected ( ) {
115212 const wsUrl = `ws://${ window . location . host } /client`
116- console . log ( `Connecting to ${ wsUrl } ` )
117213 const ws = ( this . #ws = new WebSocket ( wsUrl ) )
118214
119215 ws . addEventListener ( 'open' , ( ) => {
@@ -150,6 +246,13 @@ export class DataManagerController implements ReactiveController {
150246 return
151247 }
152248
249+ // Handle test stopped event
250+ if ( scope === 'testStopped' ) {
251+ this . #handleTestStopped( )
252+ this . #host. requestUpdate ( )
253+ return
254+ }
255+
153256 // Check for new run BEFORE processing suites data
154257 if ( scope === 'suites' ) {
155258 const shouldReset = this . #shouldResetForNewRun( data )
@@ -227,6 +330,55 @@ export class DataManagerController implements ReactiveController {
227330 this . #host. requestUpdate ( )
228331 }
229332
333+ #handleTestStopped( ) {
334+ // Mark all running tests as failed when test execution is stopped
335+ const suites = this . suitesContextProvider . value || [ ]
336+ const updatedSuites = suites . map ( ( chunk ) => {
337+ const updatedChunk : Record < string , SuiteStatsFragment > = { }
338+ Object . entries ( chunk as Record < string , SuiteStatsFragment > ) . forEach (
339+ ( [ uid , suite ] ) => {
340+ if ( ! suite ) {
341+ updatedChunk [ uid ] = suite
342+ return
343+ }
344+
345+ // Recursive helper to update tests and nested suites
346+ const updateSuite = ( s : SuiteStatsFragment ) : SuiteStatsFragment => {
347+ const updatedTests = s . tests ?. map ( ( test ) : TestStatsFragment => {
348+ // If test is running (no end time), mark it as failed
349+ if ( test && ! test . end ) {
350+ return {
351+ ...test ,
352+ end : new Date ( ) ,
353+ state : 'failed' as 'failed' ,
354+ error : {
355+ message : 'Test execution stopped' ,
356+ name : 'TestStoppedError'
357+ }
358+ }
359+ }
360+ return test
361+ } )
362+
363+ // Recursively update nested suites (for Cucumber scenarios)
364+ const updatedNestedSuites = s . suites ?. map ( updateSuite )
365+
366+ return {
367+ ...s ,
368+ tests : updatedTests || [ ] ,
369+ suites : updatedNestedSuites || [ ]
370+ }
371+ }
372+
373+ updatedChunk [ uid ] = updateSuite ( suite )
374+ }
375+ )
376+ return updatedChunk
377+ } )
378+
379+ this . suitesContextProvider . setValue ( updatedSuites )
380+ }
381+
230382 #handleMutationsUpdate( data : TraceMutation [ ] ) {
231383 this . mutationsContextProvider . setValue ( [
232384 ...( this . mutationsContextProvider . value || [ ] ) ,
0 commit comments