@@ -8,6 +8,14 @@ import { Workspace } from "./workspace";
88
99const asyncExec = promisify ( exec ) ;
1010
11+ interface CodeLensData {
12+ type : string ;
13+ // eslint-disable-next-line @typescript-eslint/naming-convention
14+ group_id : number ;
15+ id ?: number ;
16+ kind : string ;
17+ }
18+
1119export class TestController {
1220 private readonly testController : vscode . TestController ;
1321 private readonly testCommands : WeakMap < vscode . TestItem , string > ;
@@ -72,8 +80,7 @@ export class TestController {
7280 this . testCommands . delete ( test ) ;
7381 } ) ;
7482
75- const groupIdMap : Record < string , vscode . TestItem > = { } ;
76- let classTest : vscode . TestItem ;
83+ const groupIdMap : Map < number , vscode . TestItem > = new Map ( ) ;
7784
7885 const uri = vscode . Uri . from ( {
7986 scheme : "file" ,
@@ -88,12 +95,9 @@ export class TestController {
8895 uri ,
8996 ) ;
9097
91- if ( res . data ?. kind ) {
92- testItem . tags = [ new vscode . TestTag ( res . data . kind ) ] ;
93- } else if ( name . startsWith ( "test_" ) ) {
94- // Older Ruby LSP versions may not include 'kind' so we try infer it from the name.
95- testItem . tags = [ new vscode . TestTag ( "example" ) ] ;
96- }
98+ const data : CodeLensData = res . data ;
99+
100+ testItem . tags = [ new vscode . TestTag ( data . kind ) ] ;
97101
98102 this . testCommands . set ( testItem , command ) ;
99103
@@ -102,40 +106,32 @@ export class TestController {
102106 new vscode . Position ( location . end_line , location . end_column ) ,
103107 ) ;
104108
105- // If the test has a group_id, the server supports code lens hierarchy
106- if ( "group_id" in res . data ) {
107- // If it has an id, it's a group
108- if ( res . data ?. id ) {
109- // Add group to the map
110- groupIdMap [ res . data . id ] = testItem ;
111- testItem . canResolveChildren = true ;
112-
113- if ( res . data . group_id ) {
114- // Add nested group to its parent group
115- groupIdMap [ res . data . group_id ] . children . add ( testItem ) ;
116- } else {
117- // Or add it to the top-level
118- this . testController . items . add ( testItem ) ;
119- }
120- // Otherwise, it's a test
121- } else {
122- // Add test to its parent group
123- groupIdMap [ res . data . group_id ] . children . add ( testItem ) ;
124- testItem . tags = [ ...testItem . tags , this . debugTag ] ;
125- }
126- // If the server doesn't support code lens hierarchy, all groups are top-level
109+ // If it has an id, it's a group. Otherwise, it's a test example
110+ if ( data . id ) {
111+ // Add group to the map
112+ groupIdMap . set ( data . id , testItem ) ;
113+ testItem . canResolveChildren = true ;
127114 } else {
128- // Add test methods as children to the test class so it appears nested in Test explorer
129- // and running the test class will run all of the test methods
130- // eslint-disable-next-line no-lonely-if
131- if ( testItem . tags . find ( ( tag ) => tag . id === "example" ) ) {
132- testItem . tags = [ ...testItem . tags , this . debugTag ] ;
133- classTest . children . add ( testItem ) ;
115+ // Set example tags
116+ testItem . tags = [ ...testItem . tags , this . debugTag ] ;
117+ }
118+
119+ // Examples always have a `group_id`. Groups may or may not have it
120+ if ( data . group_id ) {
121+ // Add nested group to its parent group
122+ const group = groupIdMap . get ( data . group_id ) ;
123+
124+ // If there's a mistake on the server or in an addon, a code lens may be produced for a non-existing group
125+ if ( group ) {
126+ group . children . add ( testItem ) ;
134127 } else {
135- classTest = testItem ;
136- classTest . canResolveChildren = true ;
137- this . testController . items . add ( testItem ) ;
128+ this . currentWorkspace ( ) ?. outputChannel . error (
129+ `Test example " ${ name } " is attached to group_id ${ data . group_id } , but that group does not exist` ,
130+ ) ;
138131 }
132+ } else {
133+ // Or add it to the top-level
134+ this . testController . items . add ( testItem ) ;
139135 }
140136 } ) ;
141137 }
0 commit comments