@@ -77,7 +77,7 @@ export class Controller {
7777 */
7878 private currentConfig ?: ConfigurationList ;
7979
80- private discoverer ! : SettingsBasedFallbackTestDiscoverer ;
80+ private discoverer ? : SettingsBasedFallbackTestDiscoverer ;
8181
8282 public readonly settings = this . disposables . add (
8383 new ConfigValue ( 'extractSettings' , defaultTestSymbols ) ,
@@ -107,13 +107,17 @@ export class Controller {
107107 public readonly onDidDispose = this . disposeEmitter . event ;
108108 private tsconfigStore ?: TsConfigStore ;
109109
110- public ctrl : vscode . TestController ;
110+ public ctrl ? : vscode . TestController ;
111111
112112 /** Gets run profiles the controller has registerd. */
113113 public get profiles ( ) {
114114 return [ ...this . runProfiles . values ( ) ] . flat ( ) ;
115115 }
116116
117+ public tryActivate ( ) {
118+ return this . configFile . tryActivate ( ) ;
119+ }
120+
117121 constructor (
118122 private readonly logChannel : vscode . LogOutputChannel ,
119123 private readonly wf : vscode . WorkspaceFolder ,
@@ -126,34 +130,49 @@ export class Controller {
126130 wf . uri . fsPath ,
127131 configFileUri . fsPath ,
128132 ) ;
129- const ctrl = ( this . ctrl = vscode . tests . createTestController (
130- configFileUri . toString ( ) ,
131- configFileUri . fsPath ,
132- ) ) ;
133- this . disposables . add ( ctrl ) ;
134133 this . configFile = this . disposables . add ( new ConfigurationFile ( logChannel , configFileUri , wf ) ) ;
135134
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+ ) ;
137166
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 ( ) ;
154168 }
155169
156170 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+
157176 if ( ! this . tsconfigStore ) {
158177 newTsConfig = true ;
159178 }
@@ -209,7 +228,7 @@ export class Controller {
209228
210229 let tree : IParsedNode [ ] ;
211230 try {
212- tree = await this . discoverer . discover ( uri . fsPath , contents ) ;
231+ tree = await this . discoverer ! . discover ( uri . fsPath , contents ) ;
213232 } catch ( e ) {
214233 this . logChannel . error (
215234 'Error while test extracting ' ,
@@ -242,7 +261,7 @@ export class Controller {
242261 ) : vscode . TestItem => {
243262 let item = parent . children . get ( node . name ) ;
244263 if ( ! item ) {
245- item = this . ctrl . createTestItem ( node . name , node . name , start . uri ) ;
264+ item = this . ctrl ! . createTestItem ( node . name , node . name , start . uri ) ;
246265 counter . add ( node . kind ) ;
247266 testMetadata . set ( item , {
248267 type : node . kind === NodeKind . Suite ? ItemType . Suite : ItemType . Test ,
@@ -305,7 +324,7 @@ export class Controller {
305324 for ( const [ id , test ] of previous . items ) {
306325 if ( ! newTestsInFile . has ( id ) ) {
307326 const meta = testMetadata . get ( test ) ;
308- ( test . parent ?. children ?? this . ctrl . items ) . delete ( id ) ;
327+ ( test . parent ?. children ?? this . ctrl ! . items ) . delete ( id ) ;
309328 if ( meta ?. type === ItemType . Test ) {
310329 counter . remove ( NodeKind . Test ) ;
311330 } else if ( meta ?. type === ItemType . Suite ) {
@@ -337,7 +356,7 @@ export class Controller {
337356 let last : vscode . TestItemCollection | undefined ;
338357 for ( const { children, item } of itemsIt ) {
339358 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 } ;
341360 } else {
342361 deleteFrom = undefined ;
343362 }
@@ -352,7 +371,7 @@ export class Controller {
352371 if ( deleteFrom ) {
353372 deleteFrom . items . delete ( deleteFrom . id ) ;
354373 } else {
355- last ! . delete ( id ) ;
374+ last ? .delete ( id ) ;
356375 }
357376 }
358377
@@ -384,18 +403,22 @@ export class Controller {
384403 for ( const key of this . testsInFiles . keys ( ) ) {
385404 this . deleteFileTests ( key ) ;
386405 }
387- const item = ( this . errorItem = this . ctrl . createTestItem ( 'error' , 'Extension Test Error' ) ) ;
406+ const item = ( this . errorItem = this . ctrl ! . createTestItem ( 'error' , 'Extension Test Error' ) ) ;
388407 item . error = new vscode . MarkdownString (
389408 `[View details](command:${ showConfigErrorCommand } ?${ encodeURIComponent (
390409 JSON . stringify ( [ this . configFile . uri . toString ( ) ] ) ,
391410 ) } )`,
392411 ) ;
393412 item . error . isTrusted = true ;
394- this . ctrl . items . add ( item ) ;
413+ this . ctrl ! . items . add ( item ) ;
395414 }
396415
397416 /** Creates run profiles for each configuration in the extension tests */
398417 private applyRunHandlers ( ) {
418+ if ( ! this . ctrl ) {
419+ return ;
420+ }
421+
399422 const oldRunHandlers = this . runProfiles ;
400423 this . runProfiles = new Map ( ) ;
401424 const originalName = 'Mocha Config' ;
@@ -446,6 +469,11 @@ export class Controller {
446469 }
447470
448471 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+
449477 if ( this . errorItem ) {
450478 this . ctrl . items . delete ( this . errorItem . id ) ;
451479 this . errorItem = undefined ;
@@ -502,6 +530,9 @@ export class Controller {
502530
503531 /** Gets the test collection for a file of the given URI, descending from the root. */
504532 private getContainingItemsForFile ( uri : vscode . Uri , createOpts ?: ICreateOpts ) {
533+ if ( ! this . ctrl ) {
534+ return [ ] ;
535+ }
505536 return getContainingItemsForFile ( this . configFile . uri , this . ctrl , uri , createOpts ) ;
506537 }
507538}
0 commit comments