import * as assert from 'assert' ; import * as vscodeTypes from 'vscode' ; import * as types from '../index' ; export enum TestInput { UseDefaultValue , BackButton } class GoBackError extends Error { constructor ( ) { super ( 'Go back.' ) ; } } export class TestUserInput implements types . TestUserInput { private readonly _onDidFinishPromptEmitter : vscodeTypes . EventEmitter < types . PromptResult > ; private readonly _vscode : typeof vscodeTypes ; private _inputs : ( string | RegExp | TestInput ) [ ] = [ ] ; constructor ( vscode ) { this . _vscode = vscode ; this . _onDidFinishPromptEmitter = new this . _vscode . EventEmitter < types . PromptResult > ( ) ; } public static async create ( ) { return new TestUserInput ( await import ( 'vscode' ) ) ; } public get onDidFinishPrompt ( ) : vscodeTypes . Event < types . PromptResult > { return this . _onDidFinishPromptEmitter . event ; } public async runWithInputs < T > ( inputs , callback ) { this . setInputs ( inputs ) ; const result = await callback ( ) ; this . validateAllInputsUsed ( ) ; return result ; } public setInputs ( inputs ) { this . _inputs = < ( string | RegExp | TestInput ) [ ] > inputs ; } public validateAllInputsUsed ( ) { assert . strictEqual ( this . _inputs . length , 0 , `Not all inputs were used: ${ this . _inputs . toString ( ) }` ) ; } public async showQuickPick < T extends vscodeTypes . QuickPickItem > ( items , options ) { const resolvedItems = await Promise . resolve ( items ) ; let result ; const input = this . _inputs . shift ( ) ; if ( input === undefined ) { throw new Error ( `No more inputs left for call to showQuickPick. Placeholder: '${ options . placeHolder }'` ) ; } else if ( input === TestInput . BackButton ) { throw new GoBackError ( ) ; } else { if ( resolvedItems . length === 0 ) { throw new Error ( `No quick pick items found. Placeholder: '${ options . placeHolder }'` ) ; } else if ( input === TestInput . UseDefaultValue ) { result = resolvedItems [ 0 ] ; } else { function qpiMatchesInput ( qpi ) { const description = qpi . description || '' ; const valuesToTest = [ qpi . label , description , `${ qpi . label } ${ description }` ] ; return valuesToTest . some ( v => input instanceof RegExp ? input . test ( v ) : input === v ) ; } if ( options . canPickMany ) { result = resolvedItems . filter ( qpiMatchesInput ) ; } else { const resolvedItem = resolvedItems . find ( qpiMatchesInput ) ; if ( resolvedItem ) { result = resolvedItem ; } else { const picksString = resolvedItems . map ( i => `"${ i . label }"` ) . join ( ', ' ) ; const lastItem = resolvedItems [ resolvedItems . length - 1 ] ; if ( /load more/i . test ( lastItem . label ) ) { console . log ( `Loading more items for quick pick with placeholder "${ options . placeHolder }"...` ) ; result = lastItem ; this . _inputs . unshift ( input ) ; } else { throw new Error ( `Did not find quick pick item matching "${ input }". Placeholder: "${ options . placeHolder }". Picks: ${ picksString }` ) ; } } } } this . _onDidFinishPromptEmitter . fire ( { value : result } ) ; return result ; } } public async showInputBox ( options ) { let result ; const input = this . _inputs . shift ( ) ; if ( input === undefined ) { throw new Error ( `No more inputs left for call to showInputBox. Placeholder: '${ options . placeHolder }'. Prompt: '${ options . prompt }'` ) ; } else if ( input === TestInput . BackButton ) { throw new GoBackError ( ) ; } else if ( input === TestInput . UseDefaultValue ) { if ( ! options . value ) { throw new Error ( 'Can\'t use default value because none was specified' ) ; } else { result = options . value ; } } else if ( typeof input === 'string' ) { if ( options . validateInput ) { const msg = await Promise . resolve ( options . validateInput ( input ) ) ; if ( msg !== null && msg !== undefined ) { throw new Error ( msg ) ; } } result = input ; } else { throw new Error ( `Unexpected input '${ input }' for showInputBox.` ) ; } this . _onDidFinishPromptEmitter . fire ( { value : result , matchesDefault : result === options . value } ) ; return result ; } public showWarningMessage < T extends vscodeTypes . MessageItem > ( message , ... items ) ; public showWarningMessage < T extends vscodeTypes . MessageItem > ( message , options , ... items ) ; public async showWarningMessage < T extends vscodeTypes . MessageItem > ( message , ... args ) { let result ; const input = this . _inputs . shift ( ) ; if ( input === undefined ) { throw new Error ( `No more inputs left for call to showWarningMessage. Message: ${ message }` ) ; } else if ( typeof input === 'string' ) { const matchingItem = args . find ( ( item ) => item . title === input ) ; if ( matchingItem ) { result = matchingItem ; } else { throw new Error ( `Did not find message item matching '${ input }'. Message: '${ message }'` ) ; } } else { throw new Error ( `Unexpected input '${ input }' for showWarningMessage.` ) ; } this . _onDidFinishPromptEmitter . fire ( { value : result } ) ; return result ; } public async showOpenDialog ( options ) { let result ; const input = this . _inputs . shift ( ) ; if ( input === undefined ) { throw new Error ( `No more inputs left for call to showOpenDialog. Message: ${ options . openLabel }` ) ; } else if ( typeof input === 'string' ) { result = [ this . _vscode . Uri . file ( input ) ] ; } else { throw new Error ( `Unexpected input '${ input }' for showOpenDialog.` ) ; } this . _onDidFinishPromptEmitter . fire ( { value : result } ) ; return result ; } } export async function runWithInputs < T > ( callbackId , inputs , registerOnActionStartHandler , callback ) { const testUserInput = await TestUserInput . create ( ) ; testUserInput . setInputs ( inputs ) ; const disposable = registerOnActionStartHandler ( ( context ) => { if ( context . callbackId === callbackId ) { context . ui = testUserInput ; disposable . dispose ( ) ; } } ) ; let result ; try { result = await callback ( ) ; } finally { disposable . dispose ( ) ; } testUserInput . validateAllInputsUsed ( ) ; return result ; }