@@ -124,7 +124,7 @@ export async function commonRunTestsHandler(controller: vscode.TestController, r
124124 }
125125 return ;
126126 }
127-
127+
128128 const username : string = server . username || 'UnknownUser' ;
129129 const testRoot = vscode . Uri . from ( { scheme : 'isfs' , authority, path : `/.vscode/UnitTestRoot/${ username } ` } ) ;
130130 try {
@@ -160,8 +160,10 @@ export async function commonRunTestsHandler(controller: vscode.TestController, r
160160 }
161161
162162 // Finally, run the tests using the debugger API
163- const runQualifiers = controller . id === `${ extensionId } -Local` ? "" : "/noload/nodelete" ;
164- // Run tests through the debugger but only stop at breakpoints etc if user chose "Debug Test" instead of "Run Test"
163+ // but only stop at breakpoints etc if user chose "Debug Test" instead of "Run Test"
164+ const isClientSideMode = controller . id === `${ extensionId } -Local` ;
165+ const isDebug = request . profile ?. kind === vscode . TestRunProfileKind . Debug ;
166+ const runQualifiers = ! isClientSideMode ? "/noload/nodelete" : isDebug ? "/noload" : "" ;
165167 const runIndex = allTestRuns . push ( run ) - 1 ;
166168 runIndices . push ( runIndex ) ;
167169
@@ -187,15 +189,49 @@ export async function commonRunTestsHandler(controller: vscode.TestController, r
187189 "testingIdBase" : firstClassTestItem . id . split ( ":" , 2 ) . join ( ":" )
188190 } ;
189191 const sessionOptions : vscode . DebugSessionOptions = {
190- noDebug : request . profile ?. kind !== vscode . TestRunProfileKind . Debug ,
192+ noDebug : ! isDebug ,
191193 suppressDebugToolbar : request . profile ?. kind !== vscode . TestRunProfileKind . Debug
192194 } ;
193195
194196 // ObjectScript debugger's initializeRequest handler needs to identify target server and namespace
195197 // and does this from current active document, so here we make sure there's a suitable one.
196198 vscode . commands . executeCommand ( "vscode.open" , oneUri , { preserveFocus : true } ) ;
197199
198- // Start the debugger unless cancelled
200+ // When debugging in client-side mode the classes must be loaded and compiled before the debug run happens, otherwise breakpoints don't bind
201+ if ( isClientSideMode && isDebug && ! cancellation . isCancellationRequested ) {
202+
203+ // Without the /debug option the classes are compiled without maps, preventing breakpoints from binding.
204+ const preloadConfig = {
205+ "type" : "objectscript" ,
206+ "request" : "launch" ,
207+ "name" : 'LocalTests.Preload' ,
208+ "program" : `##class(%UnitTest.Manager).RunTest("${ testSpec } ","/nodisplay/load/debug/norun/nodelete")` ,
209+ } ;
210+
211+ // Prepare to detect when the preload completes
212+ let sessionTerminated : ( ) => void ;
213+ const listener = vscode . debug . onDidTerminateDebugSession ( ( session ) => {
214+ if ( session . name === 'LocalTests.Preload' ) {
215+ sessionTerminated ( ) ;
216+ }
217+ } ) ;
218+ const sessionTerminatedPromise = new Promise < void > ( resolve => sessionTerminated = resolve ) ;
219+
220+ // Start the preload
221+ if ( ! await vscode . debug . startDebugging ( folder , preloadConfig , { noDebug : true , suppressDebugStatusbar : true } ) ) {
222+ listener . dispose ( ) ;
223+ await vscode . window . showErrorMessage ( `Failed to preload client-side test classes for debugging` , { modal : true } ) ;
224+ run . end ( ) ;
225+ allTestRuns [ runIndex ] = undefined ;
226+ return ;
227+ } ;
228+
229+ // Wait for it to complete
230+ await sessionTerminatedPromise ;
231+ listener . dispose ( ) ;
232+ }
233+
234+ // Start the run unless already cancelled
199235 if ( cancellation . isCancellationRequested || ! await vscode . debug . startDebugging ( folder , configuration , sessionOptions ) ) {
200236 if ( ! cancellation . isCancellationRequested ) {
201237 await vscode . window . showErrorMessage ( `Failed to launch testing` , { modal : true } ) ;
0 commit comments