@@ -514,9 +514,15 @@ async function handleRunRequestedTests(request: vscode.TestRunRequest, token?: C
514514 /**
515515 * Collect and filter tests to run.
516516 */
517- const requestedLeafTests = requestedRootTests . flatMap ( ( i ) => collectLeafItems ( i , token ) ) ;
517+ const requestedLeafTests = (
518+ await Promise . all ( requestedRootTests . map ( async ( i ) => await collectLeafItems ( i , token ) ) )
519+ ) . flat ( ) ;
518520 const excludedLeafTests = request . exclude
519- ? request . exclude . flatMap ( ( i ) => collectLeafItems ( i , token ) )
521+ ? (
522+ await Promise . all (
523+ request . exclude . map ( async ( i ) => await collectLeafItems ( i , token ) ) ,
524+ )
525+ ) . flat ( )
520526 : [ ] ;
521527 const testsToRun = requestedLeafTests . filter ( ( t ) => {
522528 if ( token ?. isCancellationRequested ) {
@@ -614,7 +620,7 @@ async function handleRunAll(request: vscode.TestRunRequest, token?: Cancellation
614620 /**
615621 * Collect all tests, i.e. all leafs of the TestItem tree.
616622 */
617- const allTests : TestItem [ ] = collectLeafsFromCollection ( controller . items , token ) ;
623+ const allTests : TestItem [ ] = await collectLeafsFromCollection ( controller . items , token ) ;
618624
619625 /**
620626 * Mark tests as queued
@@ -630,17 +636,6 @@ async function handleRunAll(request: vscode.TestRunRequest, token?: Cancellation
630636 throw new vscode . CancellationError ( ) ;
631637 }
632638
633- /**
634- * Resolve all the test tree before collecting tests
635- */
636- const promises : Promise < void > [ ] = [ ] ;
637- controller . items . forEach ( ( i ) => promises . push ( resolveHandler ( i , true , token ) ) ) ;
638- await Promise . all ( promises ) ;
639-
640- if ( token ?. isCancellationRequested ) {
641- throw new vscode . CancellationError ( ) ;
642- }
643-
644639 /**
645640 * Mark all tests as started.
646641 */
@@ -810,37 +805,71 @@ export function determineTestOutcome(
810805 *
811806 * @param items - a {@link vscode.TestItemCollection}
812807 * @param token - a cancellation token to stop the traversal
808+ * @param resolve - whether to resolve items that can have children or process
809+ * them as they are
813810 * @returns the array of leaf TestItems reachable from the given collection.
814811 */
815- export function collectLeafsFromCollection (
812+ export async function collectLeafsFromCollection (
816813 items : vscode . TestItemCollection ,
817814 token ?: CancellationToken ,
818- ) : vscode . TestItem [ ] {
815+ resolve : boolean = true ,
816+ ) : Promise < vscode . TestItem [ ] > {
819817 const res : vscode . TestItem [ ] = [ ] ;
820- items . forEach ( ( i ) => {
818+
819+ /**
820+ * items.forEach() wouldn't work here because each iteration produces a
821+ * promise that we can't await. Ideally we could have used
822+ * Promise.all(items.map(...)) but the TestItemCollection type doesn't
823+ * offer a map method. So instead we use a regular for-loop and await in
824+ * each iteration.
825+ */
826+ for ( const [ , item ] of items ) {
821827 if ( token ?. isCancellationRequested ) {
822828 throw new vscode . CancellationError ( ) ;
823829 }
824- res . push ( ...collectLeafItems ( i , token ) ) ;
825- } ) ;
830+ res . push ( ...( await collectLeafItems ( item , token , resolve ) ) ) ;
831+ }
826832 return res ;
827833}
828834
829835/**
830836 *
831- * @param item - a {@link vscode. TestItem}
837+ * @param item - a {@link TestItem}
832838 * @param token - a cancellation token to stop the traversal
839+ * @param resolve - whether to resolve items that can have children or process
840+ * them as they are
833841 * @returns the array of leaf TestItems reachable from the given TestItem
834842 */
835- export function collectLeafItems ( item : TestItem , token ?: CancellationToken ) : vscode . TestItem [ ] {
843+ export async function collectLeafItems (
844+ item : TestItem ,
845+ token ?: CancellationToken ,
846+ resolve : boolean = true ,
847+ ) : Promise < vscode . TestItem [ ] > {
848+ /**
849+ * If the TestItem has never been expanded in the UI, its children may have
850+ * not been populated yet. Force a resolve operation to load the children.
851+ */
852+ if ( resolve && item . canResolveChildren && controller . resolveHandler ) {
853+ await controller . resolveHandler ( item ) ;
854+ }
855+
836856 if ( item . children . size > 0 ) {
837857 const res : vscode . TestItem [ ] = [ ] ;
838- item . children . forEach ( ( i ) => {
858+
859+ /**
860+ * items.forEach() wouldn't work here because each iteration produces a
861+ * promise that we can't await. Ideally we could have used
862+ * Promise.all(items.map(...)) but the TestItemCollection type doesn't
863+ * offer a map method. So instead we use a regular for-loop and await in
864+ * each iteration.
865+ */
866+ for ( const [ , i ] of item . children ) {
839867 if ( token ?. isCancellationRequested ) {
840868 throw new vscode . CancellationError ( ) ;
841869 }
842- res . push ( ...collectLeafItems ( i , token ) ) ;
843- } ) ;
870+ res . push ( ...( await collectLeafItems ( i , token , resolve ) ) ) ;
871+ }
872+
844873 return res ;
845874 } else {
846875 return [ item ] ;
0 commit comments