@@ -18,7 +18,7 @@ import { IDisposableRegistry, Product } from '../common/types';
1818import { IInterpreterService } from '../interpreter/contracts' ;
1919import { IServiceContainer } from '../ioc/types' ;
2020import { EventName } from '../telemetry/constants' ;
21- import { captureTelemetry , sendTelemetryEvent } from '../telemetry/index' ;
21+ import { sendTelemetryEvent } from '../telemetry/index' ;
2222import { selectTestWorkspace } from './common/testUtils' ;
2323import { TestSettingsPropertyNames } from './configuration/types' ;
2424import { ITestConfigurationService , ITestsHelper } from './common/types' ;
@@ -42,6 +42,91 @@ export class TestingService implements ITestingService {
4242 }
4343}
4444
45+ /**
46+ * Registers command handlers but defers service resolution until the commands are actually invoked,
47+ * allowing registration to happen before all services are fully initialized.
48+ */
49+ export function registerTestCommands ( serviceContainer : IServiceContainer ) : void {
50+ // Resolve only the essential services needed for command registration itself
51+ const disposableRegistry = serviceContainer . get < Disposable [ ] > ( IDisposableRegistry ) ;
52+ const commandManager = serviceContainer . get < ICommandManager > ( ICommandManager ) ;
53+
54+ // Helper function to configure tests - services are resolved when invoked, not at registration time
55+ const configureTestsHandler = async ( resource ?: Uri ) => {
56+ sendTelemetryEvent ( EventName . UNITTEST_CONFIGURE ) ;
57+
58+ // Resolve services lazily when the command is invoked
59+ const workspaceService = serviceContainer . get < IWorkspaceService > ( IWorkspaceService ) ;
60+
61+ let wkspace : Uri | undefined ;
62+ if ( resource ) {
63+ const wkspaceFolder = workspaceService . getWorkspaceFolder ( resource ) ;
64+ wkspace = wkspaceFolder ? wkspaceFolder . uri : undefined ;
65+ } else {
66+ const appShell = serviceContainer . get < IApplicationShell > ( IApplicationShell ) ;
67+ wkspace = await selectTestWorkspace ( appShell ) ;
68+ }
69+ if ( ! wkspace ) {
70+ return ;
71+ }
72+ const interpreterService = serviceContainer . get < IInterpreterService > ( IInterpreterService ) ;
73+ const cmdManager = serviceContainer . get < ICommandManager > ( ICommandManager ) ;
74+ if ( ! ( await interpreterService . getActiveInterpreter ( wkspace ) ) ) {
75+ cmdManager . executeCommand ( constants . Commands . TriggerEnvironmentSelection , wkspace ) ;
76+ return ;
77+ }
78+ const configurationService = serviceContainer . get < ITestConfigurationService > ( ITestConfigurationService ) ;
79+ await configurationService . promptToEnableAndConfigureTestFramework ( wkspace ) ;
80+ } ;
81+
82+ disposableRegistry . push (
83+ // Command: python.configureTests - prompts user to configure test framework
84+ commandManager . registerCommand (
85+ constants . Commands . Tests_Configure ,
86+ ( _ , _cmdSource : constants . CommandSource = constants . CommandSource . commandPalette , resource ?: Uri ) => {
87+ // Invoke configuration handler (errors are ignored as this can be called from multiple places)
88+ configureTestsHandler ( resource ) . ignoreErrors ( ) ;
89+ traceVerbose ( 'Testing: Trigger refresh after config change' ) ;
90+ // Refresh test data if test controller is available (resolved lazily)
91+ if ( tests && ! ! tests . createTestController ) {
92+ const testController = serviceContainer . get < ITestController > ( ITestController ) ;
93+ testController ?. refreshTestData ( resource , { forceRefresh : true } ) ;
94+ }
95+ } ,
96+ ) ,
97+ // Command: python.tests.copilotSetup - Copilot integration for test setup
98+ commandManager . registerCommand ( constants . Commands . Tests_CopilotSetup , ( resource ?: Uri ) :
99+ | { message : string ; command : Command }
100+ | undefined => {
101+ // Resolve services lazily when the command is invoked
102+ const workspaceService = serviceContainer . get < IWorkspaceService > ( IWorkspaceService ) ;
103+ const wkspaceFolder =
104+ workspaceService . getWorkspaceFolder ( resource ) || workspaceService . workspaceFolders ?. at ( 0 ) ;
105+ if ( ! wkspaceFolder ) {
106+ return undefined ;
107+ }
108+
109+ const configurationService = serviceContainer . get < ITestConfigurationService > ( ITestConfigurationService ) ;
110+ if ( configurationService . hasConfiguredTests ( wkspaceFolder . uri ) ) {
111+ return undefined ;
112+ }
113+
114+ return {
115+ message : Testing . copilotSetupMessage ,
116+ command : {
117+ title : Testing . configureTests ,
118+ command : constants . Commands . Tests_Configure ,
119+ arguments : [ undefined , constants . CommandSource . ui , resource ] ,
120+ } ,
121+ } ;
122+ } ) ,
123+ // Command: python.copyTestId - copies test ID to clipboard
124+ commandManager . registerCommand ( constants . Commands . CopyTestId , async ( testItem : TestItem ) => {
125+ writeTestIdToClipboard ( testItem ) ;
126+ } ) ,
127+ ) ;
128+ }
129+
45130@injectable ( )
46131export class UnitTestManagementService implements IExtensionActivationService {
47132 private activatedOnce : boolean = false ;
@@ -80,7 +165,6 @@ export class UnitTestManagementService implements IExtensionActivationService {
80165 this . activatedOnce = true ;
81166
82167 this . registerHandlers ( ) ;
83- this . registerCommands ( ) ;
84168
85169 if ( ! ! tests . testResults ) {
86170 await this . updateTestUIButtons ( ) ;
@@ -130,73 +214,6 @@ export class UnitTestManagementService implements IExtensionActivationService {
130214 await Promise . all ( changedWorkspaces . map ( ( u ) => this . testController ?. refreshTestData ( u ) ) ) ;
131215 }
132216
133- @captureTelemetry ( EventName . UNITTEST_CONFIGURE , undefined , false )
134- private async configureTests ( resource ?: Uri ) {
135- let wkspace : Uri | undefined ;
136- if ( resource ) {
137- const wkspaceFolder = this . workspaceService . getWorkspaceFolder ( resource ) ;
138- wkspace = wkspaceFolder ? wkspaceFolder . uri : undefined ;
139- } else {
140- const appShell = this . serviceContainer . get < IApplicationShell > ( IApplicationShell ) ;
141- wkspace = await selectTestWorkspace ( appShell ) ;
142- }
143- if ( ! wkspace ) {
144- return ;
145- }
146- const interpreterService = this . serviceContainer . get < IInterpreterService > ( IInterpreterService ) ;
147- const commandManager = this . serviceContainer . get < ICommandManager > ( ICommandManager ) ;
148- if ( ! ( await interpreterService . getActiveInterpreter ( wkspace ) ) ) {
149- commandManager . executeCommand ( constants . Commands . TriggerEnvironmentSelection , wkspace ) ;
150- return ;
151- }
152- const configurationService = this . serviceContainer . get < ITestConfigurationService > ( ITestConfigurationService ) ;
153- await configurationService . promptToEnableAndConfigureTestFramework ( wkspace ! ) ;
154- }
155-
156- private registerCommands ( ) : void {
157- const commandManager = this . serviceContainer . get < ICommandManager > ( ICommandManager ) ;
158- this . disposableRegistry . push (
159- commandManager . registerCommand (
160- constants . Commands . Tests_Configure ,
161- ( _ , _cmdSource : constants . CommandSource = constants . CommandSource . commandPalette , resource ?: Uri ) => {
162- // Ignore the exceptions returned.
163- // This command will be invoked from other places of the extension.
164- this . configureTests ( resource ) . ignoreErrors ( ) ;
165- traceVerbose ( 'Testing: Trigger refresh after config change' ) ;
166- this . testController ?. refreshTestData ( resource , { forceRefresh : true } ) ;
167- } ,
168- ) ,
169- commandManager . registerCommand ( constants . Commands . Tests_CopilotSetup , ( resource ?: Uri ) :
170- | { message : string ; command : Command }
171- | undefined => {
172- const wkspaceFolder =
173- this . workspaceService . getWorkspaceFolder ( resource ) || this . workspaceService . workspaceFolders ?. at ( 0 ) ;
174- if ( ! wkspaceFolder ) {
175- return undefined ;
176- }
177-
178- const configurationService = this . serviceContainer . get < ITestConfigurationService > (
179- ITestConfigurationService ,
180- ) ;
181- if ( configurationService . hasConfiguredTests ( wkspaceFolder . uri ) ) {
182- return undefined ;
183- }
184-
185- return {
186- message : Testing . copilotSetupMessage ,
187- command : {
188- title : Testing . configureTests ,
189- command : constants . Commands . Tests_Configure ,
190- arguments : [ undefined , constants . CommandSource . ui , resource ] ,
191- } ,
192- } ;
193- } ) ,
194- commandManager . registerCommand ( constants . Commands . CopyTestId , async ( testItem : TestItem ) => {
195- writeTestIdToClipboard ( testItem ) ;
196- } ) ,
197- ) ;
198- }
199-
200217 private registerHandlers ( ) {
201218 const interpreterService = this . serviceContainer . get < IInterpreterService > ( IInterpreterService ) ;
202219 this . disposableRegistry . push (
0 commit comments