1- import { type IncomingMessage } from "node:http" ;
21import { vi } from "vitest" ;
32import * as vscode from "vscode" ;
43
5- import { type Logger } from "@/logging/logger" ;
4+ import type { User } from "coder/site/src/api/typesGenerated" ;
5+ import type { IncomingMessage } from "node:http" ;
6+
7+ import type { CoderApi } from "@/api/coderApi" ;
8+ import type { Logger } from "@/logging/logger" ;
69
710/**
811 * Mock configuration provider that integrates with the vscode workspace configuration mock.
@@ -137,24 +140,42 @@ export class MockProgressReporter {
137140}
138141
139142/**
140- * Mock user interaction that integrates with vscode.window message dialogs.
143+ * Mock user interaction that integrates with vscode.window message dialogs and input boxes .
141144 * Use this to control user responses in tests.
142145 */
143146export class MockUserInteraction {
144147 private readonly responses = new Map < string , string | undefined > ( ) ;
148+ private inputBoxValue : string | undefined ;
149+ private inputBoxValidateInput : ( ( value : string ) => Promise < void > ) | undefined ;
145150 private externalUrls : string [ ] = [ ] ;
146151
147152 constructor ( ) {
148153 this . setupVSCodeMock ( ) ;
149154 }
150155
151156 /**
152- * Set a response for a specific message
157+ * Set a response for a specific message dialog
153158 */
154159 setResponse ( message : string , response : string | undefined ) : void {
155160 this . responses . set ( message , response ) ;
156161 }
157162
163+ /**
164+ * Set the value to return from showInputBox.
165+ * Pass undefined to simulate user cancelling.
166+ */
167+ setInputBoxValue ( value : string | undefined ) : void {
168+ this . inputBoxValue = value ;
169+ }
170+
171+ /**
172+ * Set a custom validateInput handler for showInputBox.
173+ * This allows tests to simulate the validation callback behavior.
174+ */
175+ setInputBoxValidateInput ( fn : ( value : string ) => Promise < void > ) : void {
176+ this . inputBoxValidateInput = fn ;
177+ }
178+
158179 /**
159180 * Get all URLs that were opened externally
160181 */
@@ -170,10 +191,13 @@ export class MockUserInteraction {
170191 }
171192
172193 /**
173- * Clear all responses
194+ * Clear all responses and input box values
174195 */
175- clearResponses ( ) : void {
196+ clear ( ) : void {
176197 this . responses . clear ( ) ;
198+ this . inputBoxValue = undefined ;
199+ this . inputBoxValidateInput = undefined ;
200+ this . externalUrls = [ ] ;
177201 }
178202
179203 /**
@@ -206,6 +230,32 @@ export class MockUserInteraction {
206230 return Promise . resolve ( true ) ;
207231 } ,
208232 ) ;
233+
234+ vi . mocked ( vscode . window . showInputBox ) . mockImplementation (
235+ async ( options ?: vscode . InputBoxOptions ) => {
236+ const value = this . inputBoxValue ;
237+ if ( value === undefined ) {
238+ return undefined ; // User cancelled
239+ }
240+
241+ if ( options ?. validateInput ) {
242+ const validationResult = await options . validateInput ( value ) ;
243+ if ( validationResult ) {
244+ // Validation failed - in real VS Code this would show error
245+ // For tests, we can use the custom handler or return undefined
246+ if ( this . inputBoxValidateInput ) {
247+ await this . inputBoxValidateInput ( value ) ;
248+ }
249+ return undefined ;
250+ }
251+ } else if ( this . inputBoxValidateInput ) {
252+ // Run custom validation handler even without options.validateInput
253+ await this . inputBoxValidateInput ( value ) ;
254+ }
255+
256+ return value ;
257+ } ,
258+ ) ;
209259 }
210260}
211261
@@ -399,3 +449,93 @@ export class MockStatusBar {
399449 ) ;
400450 }
401451}
452+
453+ /**
454+ * Mock CoderApi for testing. Tracks method calls and allows controlling responses.
455+ */
456+ export class MockCoderApi
457+ implements
458+ Pick <
459+ CoderApi ,
460+ | "setHost"
461+ | "setSessionToken"
462+ | "setCredentials"
463+ | "getAuthenticatedUser"
464+ | "dispose"
465+ >
466+ {
467+ private _host : string | undefined ;
468+ private _token : string | undefined ;
469+ private authenticatedUser : User | Error | undefined ;
470+
471+ readonly setHost = vi . fn ( ( host : string | undefined ) => {
472+ this . _host = host ;
473+ } ) ;
474+
475+ readonly setSessionToken = vi . fn ( ( token : string ) => {
476+ this . _token = token ;
477+ } ) ;
478+
479+ readonly setCredentials = vi . fn (
480+ ( host : string | undefined , token : string | undefined ) => {
481+ this . _host = host ;
482+ this . _token = token ;
483+ } ,
484+ ) ;
485+
486+ readonly getAuthenticatedUser = vi . fn ( ( ) : Promise < User > => {
487+ if ( this . authenticatedUser instanceof Error ) {
488+ return Promise . reject ( this . authenticatedUser ) ;
489+ }
490+ if ( ! this . authenticatedUser ) {
491+ return Promise . reject ( new Error ( "Not authenticated" ) ) ;
492+ }
493+ return Promise . resolve ( this . authenticatedUser ) ;
494+ } ) ;
495+
496+ readonly dispose = vi . fn ( ) ;
497+
498+ /**
499+ * Get current host (for assertions)
500+ */
501+ get host ( ) : string | undefined {
502+ return this . _host ;
503+ }
504+
505+ /**
506+ * Get current token (for assertions)
507+ */
508+ get token ( ) : string | undefined {
509+ return this . _token ;
510+ }
511+
512+ /**
513+ * Set the authenticated user that will be returned by getAuthenticatedUser.
514+ * Pass an Error to make getAuthenticatedUser reject.
515+ */
516+ setAuthenticatedUserResponse ( user : User | Error | undefined ) : void {
517+ this . authenticatedUser = user ;
518+ }
519+ }
520+
521+ /**
522+ * Create a mock User for testing.
523+ */
524+ export function createMockUser ( overrides : Partial < User > = { } ) : User {
525+ return {
526+ id : "user-123" ,
527+ username : "testuser" ,
528+ 529+ name : "Test User" ,
530+ created_at : new Date ( ) . toISOString ( ) ,
531+ updated_at : new Date ( ) . toISOString ( ) ,
532+ last_seen_at : new Date ( ) . toISOString ( ) ,
533+ status : "active" ,
534+ organization_ids : [ ] ,
535+ roles : [ ] ,
536+ avatar_url : "" ,
537+ login_type : "password" ,
538+ theme_preference : "" ,
539+ ...overrides ,
540+ } ;
541+ }
0 commit comments