@@ -77,7 +77,7 @@ export class Controller {
77
77
*/
78
78
private currentConfig ?: ConfigurationList ;
79
79
80
- private discoverer ! : SettingsBasedFallbackTestDiscoverer ;
80
+ private discoverer ? : SettingsBasedFallbackTestDiscoverer ;
81
81
82
82
public readonly settings = this . disposables . add (
83
83
new ConfigValue ( 'extractSettings' , defaultTestSymbols ) ,
@@ -107,13 +107,17 @@ export class Controller {
107
107
public readonly onDidDispose = this . disposeEmitter . event ;
108
108
private tsconfigStore ?: TsConfigStore ;
109
109
110
- public ctrl : vscode . TestController ;
110
+ public ctrl ? : vscode . TestController ;
111
111
112
112
/** Gets run profiles the controller has registerd. */
113
113
public get profiles ( ) {
114
114
return [ ...this . runProfiles . values ( ) ] . flat ( ) ;
115
115
}
116
116
117
+ public tryActivate ( ) {
118
+ return this . configFile . tryActivate ( ) ;
119
+ }
120
+
117
121
constructor (
118
122
private readonly logChannel : vscode . LogOutputChannel ,
119
123
private readonly wf : vscode . WorkspaceFolder ,
@@ -126,34 +130,49 @@ export class Controller {
126
130
wf . uri . fsPath ,
127
131
configFileUri . fsPath ,
128
132
) ;
129
- const ctrl = ( this . ctrl = vscode . tests . createTestController (
130
- configFileUri . toString ( ) ,
131
- configFileUri . fsPath ,
132
- ) ) ;
133
- this . disposables . add ( ctrl ) ;
134
133
this . configFile = this . disposables . add ( new ConfigurationFile ( logChannel , configFileUri , wf ) ) ;
135
134
136
- this . recreateDiscoverer ( ) ;
135
+ this . disposables . add (
136
+ this . configFile . onActivate ( ( ) => {
137
+ try {
138
+ const ctrl = ( this . ctrl = vscode . tests . createTestController (
139
+ configFileUri . toString ( ) ,
140
+ configFileUri . fsPath ,
141
+ ) ) ;
142
+ this . disposables . add ( ctrl ) ;
143
+
144
+ this . recreateDiscoverer ( ) ;
145
+ const rescan = async ( reason : string ) => {
146
+ try {
147
+ logChannel . info ( `Rescan of tests triggered (${ reason } ) - ${ this . configFile . uri } }` ) ;
148
+ this . recreateDiscoverer ( ) ;
149
+ await this . scanFiles ( ) ;
150
+ } catch ( e ) {
151
+ this . logChannel . error ( e as Error , 'Failed to rescan tests' ) ;
152
+ }
153
+ } ;
154
+ this . disposables . add ( this . configFile . onDidChange ( ( ) => rescan ( 'mocharc changed' ) ) ) ;
155
+ this . disposables . add ( this . settings . onDidChange ( ( ) => rescan ( 'settings changed' ) ) ) ;
156
+ ctrl . refreshHandler = ( ) => {
157
+ this . configFile . forget ( ) ;
158
+ rescan ( 'user' ) ;
159
+ } ;
160
+ this . scanFiles ( ) ;
161
+ } catch ( e ) {
162
+ this . logChannel . error ( e as Error ) ;
163
+ }
164
+ } ) ,
165
+ ) ;
137
166
138
- const rescan = async ( reason : string ) => {
139
- try {
140
- logChannel . info ( `Rescan of tests triggered (${ reason } ) - ${ this . configFile . uri } }` ) ;
141
- this . recreateDiscoverer ( ) ;
142
- await this . scanFiles ( ) ;
143
- } catch ( e ) {
144
- this . logChannel . error ( e as Error , 'Failed to rescan tests' ) ;
145
- }
146
- } ;
147
- this . disposables . add ( this . configFile . onDidChange ( ( ) => rescan ( 'mocharc changed' ) ) ) ;
148
- this . disposables . add ( this . settings . onDidChange ( ( ) => rescan ( 'settings changed' ) ) ) ;
149
- ctrl . refreshHandler = ( ) => {
150
- this . configFile . forget ( ) ;
151
- rescan ( 'user' ) ;
152
- } ;
153
- this . scanFiles ( ) ;
167
+ this . configFile . tryActivate ( ) ;
154
168
}
155
169
156
170
recreateDiscoverer ( newTsConfig : boolean = true ) {
171
+ if ( ! this . ctrl ) {
172
+ this . logChannel . trace ( 'Skipping discoverer recreation, mocha is not active in this project.' ) ;
173
+ return ;
174
+ }
175
+
157
176
if ( ! this . tsconfigStore ) {
158
177
newTsConfig = true ;
159
178
}
@@ -209,7 +228,7 @@ export class Controller {
209
228
210
229
let tree : IParsedNode [ ] ;
211
230
try {
212
- tree = await this . discoverer . discover ( uri . fsPath , contents ) ;
231
+ tree = await this . discoverer ! . discover ( uri . fsPath , contents ) ;
213
232
} catch ( e ) {
214
233
this . logChannel . error (
215
234
'Error while test extracting ' ,
@@ -242,7 +261,7 @@ export class Controller {
242
261
) : vscode . TestItem => {
243
262
let item = parent . children . get ( node . name ) ;
244
263
if ( ! item ) {
245
- item = this . ctrl . createTestItem ( node . name , node . name , start . uri ) ;
264
+ item = this . ctrl ! . createTestItem ( node . name , node . name , start . uri ) ;
246
265
counter . add ( node . kind ) ;
247
266
testMetadata . set ( item , {
248
267
type : node . kind === NodeKind . Suite ? ItemType . Suite : ItemType . Test ,
@@ -305,7 +324,7 @@ export class Controller {
305
324
for ( const [ id , test ] of previous . items ) {
306
325
if ( ! newTestsInFile . has ( id ) ) {
307
326
const meta = testMetadata . get ( test ) ;
308
- ( test . parent ?. children ?? this . ctrl . items ) . delete ( id ) ;
327
+ ( test . parent ?. children ?? this . ctrl ! . items ) . delete ( id ) ;
309
328
if ( meta ?. type === ItemType . Test ) {
310
329
counter . remove ( NodeKind . Test ) ;
311
330
} else if ( meta ?. type === ItemType . Suite ) {
@@ -337,7 +356,7 @@ export class Controller {
337
356
let last : vscode . TestItemCollection | undefined ;
338
357
for ( const { children, item } of itemsIt ) {
339
358
if ( item && children . size === 1 ) {
340
- deleteFrom ??= { items : last || this . ctrl . items , id : item . id } ;
359
+ deleteFrom ??= { items : last || this . ctrl ! . items , id : item . id } ;
341
360
} else {
342
361
deleteFrom = undefined ;
343
362
}
@@ -352,7 +371,7 @@ export class Controller {
352
371
if ( deleteFrom ) {
353
372
deleteFrom . items . delete ( deleteFrom . id ) ;
354
373
} else {
355
- last ! . delete ( id ) ;
374
+ last ? .delete ( id ) ;
356
375
}
357
376
}
358
377
@@ -384,18 +403,22 @@ export class Controller {
384
403
for ( const key of this . testsInFiles . keys ( ) ) {
385
404
this . deleteFileTests ( key ) ;
386
405
}
387
- const item = ( this . errorItem = this . ctrl . createTestItem ( 'error' , 'Extension Test Error' ) ) ;
406
+ const item = ( this . errorItem = this . ctrl ! . createTestItem ( 'error' , 'Extension Test Error' ) ) ;
388
407
item . error = new vscode . MarkdownString (
389
408
`[View details](command:${ showConfigErrorCommand } ?${ encodeURIComponent (
390
409
JSON . stringify ( [ this . configFile . uri . toString ( ) ] ) ,
391
410
) } )`,
392
411
) ;
393
412
item . error . isTrusted = true ;
394
- this . ctrl . items . add ( item ) ;
413
+ this . ctrl ! . items . add ( item ) ;
395
414
}
396
415
397
416
/** Creates run profiles for each configuration in the extension tests */
398
417
private applyRunHandlers ( ) {
418
+ if ( ! this . ctrl ) {
419
+ return ;
420
+ }
421
+
399
422
const oldRunHandlers = this . runProfiles ;
400
423
this . runProfiles = new Map ( ) ;
401
424
const originalName = 'Mocha Config' ;
@@ -446,6 +469,11 @@ export class Controller {
446
469
}
447
470
448
471
public async scanFiles ( ) {
472
+ if ( ! this . ctrl ) {
473
+ this . logChannel . trace ( 'Skipping file scan, mocha is not active in this project.' ) ;
474
+ return ;
475
+ }
476
+
449
477
if ( this . errorItem ) {
450
478
this . ctrl . items . delete ( this . errorItem . id ) ;
451
479
this . errorItem = undefined ;
@@ -502,6 +530,9 @@ export class Controller {
502
530
503
531
/** Gets the test collection for a file of the given URI, descending from the root. */
504
532
private getContainingItemsForFile ( uri : vscode . Uri , createOpts ?: ICreateOpts ) {
533
+ if ( ! this . ctrl ) {
534
+ return [ ] ;
535
+ }
505
536
return getContainingItemsForFile ( this . configFile . uri , this . ctrl , uri , createOpts ) ;
506
537
}
507
538
}
0 commit comments