import { QuickInput } from './quickinput' ; import { Code } from './code' ; import { QuickAccess } from './quickaccess' ; export enum Selector { TerminalView = `#terminal` , Xterm = `#terminal .terminal-wrapper` , XtermEditor = `.editor-instance .terminal-wrapper` , TabsEntry = '.terminal-tabs-entry' , XtermFocused = '.terminal.xterm.focus' , PlusButton = '.codicon-plus' , EditorGroups = '.editor .split-view-view' , EditorTab = '.terminal-tab' , SingleTab = '.single-terminal-tab' , Tabs = '.tabs-list .monaco-list-row' , SplitButton = '.editor .codicon-split-horizontal' , XtermSplitIndex0 = '#terminal .terminal-groups-container .split-view-view:nth-child(1) .terminal-wrapper' , XtermSplitIndex1 = '#terminal .terminal-groups-container .split-view-view:nth-child(2) .terminal-wrapper' } /**
 * Terminal commands that accept a value in a quick input.
 */ export enum TerminalCommandIdWithValue { Rename = 'workbench.action.terminal.rename' , ChangeColor = 'workbench.action.terminal.changeColor' , ChangeIcon = 'workbench.action.terminal.changeIcon' , NewWithProfile = 'workbench.action.terminal.newWithProfile' , SelectDefaultProfile = 'workbench.action.terminal.selectDefaultShell' , AttachToSession = 'workbench.action.terminal.attachToSession' } /**
 * Terminal commands that do not present a quick input.
 */ export enum TerminalCommandId { Split = 'workbench.action.terminal.split' , KillAll = 'workbench.action.terminal.killAll' , Unsplit = 'workbench.action.terminal.unsplit' , Join = 'workbench.action.terminal.join' , Show = 'workbench.action.terminal.toggleTerminal' , CreateNewEditor = 'workbench.action.createTerminalEditor' , SplitEditor = 'workbench.action.createTerminalEditorSide' , MoveToPanel = 'workbench.action.terminal.moveToTerminalPanel' , MoveToEditor = 'workbench.action.terminal.moveToEditor' , NewWithProfile = 'workbench.action.terminal.newWithProfile' , SelectDefaultProfile = 'workbench.action.terminal.selectDefaultShell' , DetachSession = 'workbench.action.terminal.detachSession' , CreateNew = 'workbench.action.terminal.new' } interface TerminalLabel { name ? : string ; icon ? : string ; color ? : string ; } type TerminalGroup = TerminalLabel [ ] ; export class Terminal { constructor ( private code , private quickaccess , private quickinput ) { } async runCommand ( commandId ) { const keepOpen = commandId === TerminalCommandId . Join ; await this . quickaccess . runCommand ( commandId , keepOpen ) ; if ( keepOpen ) { await this . code . dispatchKeybinding ( 'enter' ) ; await this . quickinput . waitForQuickInputClosed ( ) ; } if ( commandId === TerminalCommandId . Show ) { return await this . _waitForTerminal ( undefined ) ; } } async runCommandWithValue ( commandId , value ? , altKey ? ) { const shouldKeepOpen = ! ! value || commandId === TerminalCommandIdWithValue . NewWithProfile || commandId === TerminalCommandIdWithValue . Rename || ( commandId === TerminalCommandIdWithValue . SelectDefaultProfile && value !== 'PowerShell' ) ; await this . quickaccess . runCommand ( commandId , shouldKeepOpen ) ; await this . quickinput . waitForQuickInputOpened ( ) ; if ( value ) { await this . quickinput . type ( value ) ; } else if ( commandId === TerminalCommandIdWithValue . Rename ) { await this . code . dispatchKeybinding ( 'Backspace' ) ; } await this . code . dispatchKeybinding ( altKey ? 'Alt+Enter' : 'enter' ) ; await this . quickinput . waitForQuickInputClosed ( ) ; } async runCommandInTerminal ( commandText , skipEnter ? ) { await this . code . writeInTerminal ( Selector . Xterm , commandText ) ; if ( ! skipEnter ) { await this . code . dispatchKeybinding ( 'enter' ) ; } } param location async createTerminal ( location ? ) { await this . runCommand ( TerminalCommandId . CreateNew ) ; await this . _waitForTerminal ( location ) ; } async assertEditorGroupCount ( count ) { await this . code . waitForElements ( Selector . EditorGroups , true , editorGroups => editorGroups && editorGroups . length === count ) ; } async assertSingleTab ( label , editor ? ) { await this . assertTabExpected ( editor ? Selector . EditorTab : Selector . SingleTab , undefined , label . name ? new RegExp ( label . name ) : undefined , label . icon , label . color ) ; } async assertTerminalGroups ( expectedGroups ) { let expectedCount = 0 ; expectedGroups . forEach ( g => expectedCount += g . length ) ; let index = 0 ; while ( index < expectedCount ) { for ( let groupIndex = 0 ; groupIndex < expectedGroups . length ; groupIndex ++ ) { let terminalsInGroup = expectedGroups [ groupIndex ] . length ; let indexInGroup = 0 ; const isSplit = terminalsInGroup > 1 ; while ( indexInGroup < terminalsInGroup ) { let instance = expectedGroups [ groupIndex ] [ indexInGroup ] ; const nameRegex = instance . name && isSplit ? new RegExp ( '\\s*[├┌└]\\s*' + instance . name ) : instance . name ? new RegExp ( /^\s*/ + instance . name ) : undefined ; await this . assertTabExpected ( undefined , index , nameRegex , instance . icon , instance . color ) ; indexInGroup ++ ; index ++ ; } } } } async getTerminalGroups ( ) { const tabCount = ( await this . code . waitForElements ( Selector . Tabs , true ) ) . length ; const groups = [ ] ; for ( let i = 0 ; i < tabCount ; i ++ ) { const instance = await this . code . waitForElement ( `${ Selector . Tabs }[data-index="${ i }"] ${ Selector . TabsEntry }` , e => e ?. textContent ?. length ? e ?. textContent ?. length > 1 : false ) ; const label = { name : instance . textContent . replace ( /^[├┌└]\s*/ , '' ) } ; if ( instance . textContent . match ( /^[├└]/ ) ) { groups [ groups . length - 1 ] . push ( label ) ; } else { groups . push ( [ label ] ) ; } } return groups ; } async getSingleTabName ( ) { const tab = await this . code . waitForElement ( Selector . SingleTab , singleTab => ! ! singleTab && singleTab ?. textContent . length > 1 ) ; return tab . textContent ; } private async assertTabExpected ( selector ? , listIndex ? , nameRegex ? , icon ? , color ? ) { if ( listIndex ) { if ( nameRegex ) { await this . code . waitForElement ( `${ Selector . Tabs }[data-index="${ listIndex }"] ${ Selector . TabsEntry }` , entry => ! ! entry && ! ! entry ?. textContent . match ( nameRegex ) ) ; } if ( color ) { await this . code . waitForElement ( `${ Selector . Tabs }[data-index="${ listIndex }"] ${ Selector . TabsEntry } .monaco-icon-label.terminal-icon-terminal_ansi${ color }` ) ; } if ( icon ) { await this . code . waitForElement ( `${ Selector . Tabs }[data-index="${ listIndex }"] ${ Selector . TabsEntry } .codicon-${ icon }` ) ; } } else if ( selector ) { if ( nameRegex ) { await this . code . waitForElement ( `${ selector }` , singleTab => ! ! singleTab && ! ! singleTab ?. textContent . match ( nameRegex ) ) ; } if ( color ) { await this . code . waitForElement ( `${ selector }` , singleTab => ! ! singleTab && ! ! singleTab . className . includes ( `terminal-icon-terminal_ansi${ color }` ) ) ; } if ( icon ) { selector = selector === Selector . EditorTab ? selector : `${ selector } .codicon` ; await this . code . waitForElement ( `${ selector }` , singleTab => ! ! singleTab && ! ! singleTab . className . includes ( icon ) ) ; } } } async assertTerminalViewHidden ( ) { await this . code . waitForElement ( Selector . TerminalView , result => result === undefined ) ; } async clickPlusButton ( ) { await this . code . waitAndClick ( Selector . PlusButton ) ; } async clickSplitButton ( ) { await this . code . waitAndClick ( Selector . SplitButton ) ; } async clickSingleTab ( ) { await this . code . waitAndClick ( Selector . SingleTab ) ; } async waitForTerminalText ( accept , message ? , splitIndex ? ) { try { let selector = Selector . Xterm ; if ( splitIndex !== undefined ) { selector = splitIndex === 0 ? Selector . XtermSplitIndex0 : Selector . XtermSplitIndex1 ; } await this . code . waitForTerminalBuffer ( selector , accept ) ; } catch ( err ) { if ( message ) { throw new Error ( `${ message } \n\nInner exception: \n${ err . message } ` ) ; } throw err ; } } async getPage ( ) { return ( this . code . driver as any ) . page ; } param location private async _waitForTerminal ( location ? ) { await this . code . waitForElement ( Selector . XtermFocused ) ; await this . code . waitForTerminalBuffer ( location === 'editor' ? Selector . XtermEditor : Selector . Xterm , lines => lines . some ( line => line . length > 0 ) ) ; } }