From f66bc86da89d9f0ca91715b2b9c8569b06dafa03 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 18 Nov 2021 15:11:40 -0800 Subject: [PATCH] add terminal editor and tab smoke tests (#137393) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add a bunch of tests * tweak tests * add profile tests, now breaking (no profiles showing up) * uncomment tests * update expected * fix test * merge * all passing * bunch of improvements * improve readability * all should be passing and cleaned up * all passing * fix #137243 and clean-up * remove .only * fix #137247 * get rid of enum * see if this fixes timing issue * use keybinding for show terminal instead * remove delay, implement better accept function * get rid of unneeded index * try something else * wait for empty editor to be active * remove .only * decrease redundancy in names * re-add xterm selector * try something else * undo change that broke things worse * add terminal-tabs test * all tabs tests passing locally * kill all * await before running each * fix tests * remove .only * try waiting for focused xterm * remove .only * clean up * ๐Ÿงน * fix profiles tests by using this.app.props * revert to use default shell * re-add conditional * Get tests passing on WebKit On WebKit the smoke tests were failing because the quick pick was triggering a focus event on the terminal after the blur event had already happened, this caused the view service to think the terminal was still focused when it wasn't. The fix was to reset the context key also in IViewsService.closeView. * Safari -> WebKit * Prefer arrow functions * remove beforeEach timeout * add timeouts to contributed profile tests * add terminal editor tests, fix #137377 * add a bunch of tests * refactor * get rid of profile specific command * more polish * refactor getTabs -> assertTerminalGroups * fix failing test * more polish * remove .only * add assertSingleTab * remove unused import * fix error * fix more failures * more changes * fix almost all except for plus button * fix failing plus button test * all passing * modify error that gets thrown * modify error message again * remove unused wildcard/ ANY_NAME, fix icon code * fix icon code * large refactor, improvements * finish polish * 1 failing * get test to pass Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com> --- src/vs/platform/driver/browser/baseDriver.ts | 1 - .../terminal/browser/terminalService.ts | 1 + test/automation/src/terminal.ts | 161 ++++++++++++------ .../areas/terminal/terminal-editors.test.ts | 76 +++++++++ .../areas/terminal/terminal-profiles.test.ts | 71 +++----- .../src/areas/terminal/terminal-tabs.test.ts | 82 ++++----- test/smoke/src/main.ts | 2 + 7 files changed, 246 insertions(+), 148 deletions(-) create mode 100644 test/smoke/src/areas/terminal/terminal-editors.test.ts diff --git a/src/vs/platform/driver/browser/baseDriver.ts b/src/vs/platform/driver/browser/baseDriver.ts index 832ee351654..40e5e2227ab 100644 --- a/src/vs/platform/driver/browser/baseDriver.ts +++ b/src/vs/platform/driver/browser/baseDriver.ts @@ -57,7 +57,6 @@ export abstract class BaseWindowDriver implements IWindowDriver { async getElements(selector: string, recursive: boolean): Promise { const query = document.querySelectorAll(selector); const result: IElement[] = []; - for (let i = 0; i < query.length; i++) { const element = query.item(i); result.push(this.serializeElement(element, recursive)); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 8d73e0fff9d..8f64411583d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -687,6 +687,7 @@ export class TerminalService implements ITerminalService { } if (source.target !== TerminalLocation.Editor) { + await this._terminalGroupService.showPanel(true); return; } source.target = TerminalLocation.Panel; diff --git a/test/automation/src/terminal.ts b/test/automation/src/terminal.ts index 42afbd78bf7..03ec6d53937 100644 --- a/test/automation/src/terminal.ts +++ b/test/automation/src/terminal.ts @@ -3,90 +3,153 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IElement, QuickInput } from '.'; +import { QuickInput } from '.'; import { Code } from './code'; import { QuickAccess } from './quickaccess'; -const TERMINAL_VIEW_SELECTOR = `#terminal`; -const XTERM_SELECTOR = `${TERMINAL_VIEW_SELECTOR} .terminal-wrapper`; -const CONTRIBUTED_PROFILE_NAME = `JavaScript Debug Terminal`; -const TABS = '.tabs-list .terminal-tabs-entry'; -const XTERM_FOCUSED_SELECTOR = '.terminal.xterm.focus'; +export enum Selector { + TerminalView = `#terminal`, + Xterm = `#terminal .terminal-wrapper`, + TabsEntry = '.terminal-tabs-entry', + XtermFocused = '.terminal.xterm.focus', + PlusButton = '.codicon-plus', + EditorGroups = '.editor .split-view-view', + EditorTab = '.terminal-tab', + EditorTabIcon = '.terminal-tab.codicon-', + SingleTab = '.single-terminal-tab', + Tabs = '.tabs-list .monaco-list-row', + SplitButton = '.editor .codicon-split-horizontal' +} -const enum TerminalCommandId { +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' +} + +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', CreateNew = 'workbench.action.terminal.new', + 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' } +interface TerminalLabel { + name?: string, + icon?: string, + color?: string +} +type TerminalGroup = TerminalLabel[]; export class Terminal { constructor(private code: Code, private quickaccess: QuickAccess, private quickinput: QuickInput) { } - // TODO: Strongly type using non-const enum TerminalCommandId - async runCommand(commandId: string, value?: string): Promise { - await this.quickaccess.runCommand(commandId, !!value || commandId === TerminalCommandId.Join); + async runCommand(commandId: TerminalCommandId): Promise { + await this.quickaccess.runCommand(commandId, commandId === TerminalCommandId.Join); if (commandId === TerminalCommandId.Show || commandId === TerminalCommandId.CreateNew) { return await this._waitForTerminal(); } - if (value) { - await this.code.waitForSetValue(QuickInput.QUICK_INPUT_INPUT, value); - } await this.code.dispatchKeybinding('enter'); await this.quickinput.waitForQuickInputClosed(); } - async runCommandInTerminal(commandText: string): Promise { - await this.code.writeInTerminal(XTERM_SELECTOR, commandText); - // hold your horses - await new Promise(c => setTimeout(c, 500)); - await this.code.dispatchKeybinding('enter'); - } - - // TODO: Return something more robust: - // export interface ITerminalInstance { - // name: string; - // icon: string; - // } - // export type TerminalGroup = ITerminalInstance[]; - // export type TerminalLayout = TerminalGroup[]; - async getTabLabels(expectedCount: number, splits?: boolean, accept?: (result: IElement[]) => boolean): Promise { - const result: string[] = []; - const tabs = await this.code.waitForElements(TABS, true, e => accept ? accept(e) : e.length === expectedCount && (!splits || e.some(e => e.textContent.startsWith('โ”Œ'))) && e.every(element => element.textContent.trim().length > 1)); - for (const t of tabs) { - result.push(t.textContent); - } - if (!result[0].startsWith('โ”Œ')) { - const first = result[1]; - const second = result[0]; - return [first, second]; - } - return result; - } - - async runProfileCommand(command: string, contributed?: boolean, altKey?: boolean): Promise { - await this.quickaccess.runCommand(command, true); - if (contributed) { - await this.code.waitForSetValue(QuickInput.QUICK_INPUT_INPUT, CONTRIBUTED_PROFILE_NAME); + async runCommandWithValue(commandId: TerminalCommandIdWithValue, value?: string, altKey?: boolean): Promise { + const shouldKeepOpen = !!value || commandId === TerminalCommandIdWithValue.SelectDefaultProfile || commandId === TerminalCommandIdWithValue.NewWithProfile; + await this.quickaccess.runCommand(commandId, shouldKeepOpen); + if (value) { + await this.code.waitForSetValue(QuickInput.QUICK_INPUT_INPUT, value); } await this.code.dispatchKeybinding(altKey ? 'Alt+Enter' : 'enter'); await this.quickinput.waitForQuickInputClosed(); } + async runCommandInTerminal(commandText: string): Promise { + await this.code.writeInTerminal(Selector.Xterm, commandText); + // hold your horses + await new Promise(c => setTimeout(c, 500)); + await this.code.dispatchKeybinding('enter'); + } + + async assertEditorGroupCount(count: number): Promise { + await this.code.waitForElements(Selector.EditorGroups, true, editorGroups => editorGroups && editorGroups.length === count); + } + + async assertSingleTab(label: TerminalLabel, editor?: boolean): Promise { + await this.assertTabExpected(editor ? Selector.EditorTab : Selector.SingleTab, undefined, label.name ? new RegExp(label.name) : undefined, label.icon, label.color); + } + + async assertTerminalGroups(expectedGroups: TerminalGroup[]): Promise { + 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++; + } + } + } + } + + private async assertTabExpected(selector?: string, listIndex?: number, nameRegex?: RegExp, icon?: string, color?: string): Promise { + 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}.terminal-icon-terminal_ansi${color}`); + } + if (icon) { + await this.code.waitForElement(selector === Selector.EditorTab ? `${Selector.EditorTabIcon}${icon}` : `${selector} .codicon-${icon}`); + } + } + } + + async clickPlusButton(): Promise { + await this.code.waitAndClick(Selector.PlusButton); + } + + async clickSplitButton(): Promise { + await this.code.waitAndClick(Selector.SplitButton); + } + + async clickSingleTab(): Promise { + await this.code.waitAndClick(Selector.SingleTab); + } + async waitForTerminalText(accept: (buffer: string[]) => boolean, message?: string): Promise { try { - await this.code.waitForTerminalBuffer(XTERM_SELECTOR, accept); + await this.code.waitForTerminalBuffer(Selector.Xterm, accept); } catch (err: any) { if (message) { - throw new Error(`${message}\n\nInner exception:\n${err.message}`); + throw new Error(`${message} \n\nInner exception: \n${err.message} `); } throw err; } @@ -97,7 +160,7 @@ export class Terminal { } private async _waitForTerminal(): Promise { - await this.code.waitForElement(XTERM_FOCUSED_SELECTOR); - await this.code.waitForTerminalBuffer(XTERM_SELECTOR, lines => lines.some(line => line.length > 0)); + await this.code.waitForElement(Selector.XtermFocused); + await this.code.waitForTerminalBuffer(Selector.Xterm, lines => lines.some(line => line.length > 0)); } } diff --git a/test/smoke/src/areas/terminal/terminal-editors.test.ts b/test/smoke/src/areas/terminal/terminal-editors.test.ts new file mode 100644 index 00000000000..747c2cd761c --- /dev/null +++ b/test/smoke/src/areas/terminal/terminal-editors.test.ts @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ParsedArgs } from 'minimist'; +import { Terminal, TerminalCommandId, TerminalCommandIdWithValue } from '../../../../automation/out'; +import { afterSuite, beforeSuite } from '../../utils'; + +export function setup(opts: ParsedArgs) { + describe('Terminal Editors', () => { + let terminal: Terminal; + + beforeSuite(opts); + afterSuite(opts); + + before(function () { + terminal = this.app.workbench.terminal; + }); + + afterEach(async () => { + await terminal.runCommand(TerminalCommandId.KillAll); + }); + + it('should update color of the tab', async () => { + await terminal.runCommand(TerminalCommandId.CreateNewEditor); + const color = 'Cyan'; + await terminal.runCommandWithValue(TerminalCommandIdWithValue.ChangeColor, color); + await terminal.assertSingleTab({ color }, true); + }); + + it('should update icon of the tab', async () => { + await terminal.runCommand(TerminalCommandId.CreateNewEditor); + const icon = 'symbol-method'; + await terminal.runCommandWithValue(TerminalCommandIdWithValue.ChangeIcon, icon); + await terminal.assertSingleTab({ icon }, true); + }); + + it('should rename the tab', async () => { + await terminal.runCommand(TerminalCommandId.CreateNewEditor); + const name = 'my terminal name'; + await terminal.runCommandWithValue(TerminalCommandIdWithValue.Rename, name); + await terminal.assertSingleTab({ name }, true); + }); + + it('should show the panel when the terminal is moved there and close the editor', async () => { + await terminal.runCommand(TerminalCommandId.CreateNewEditor); + await terminal.runCommand(TerminalCommandId.MoveToPanel); + await terminal.assertSingleTab({}); + }); + + it('should open a terminal in a new group for open to the side', async () => { + await terminal.runCommand(TerminalCommandId.CreateNewEditor); + await terminal.runCommand(TerminalCommandId.SplitEditor); + await terminal.assertEditorGroupCount(2); + }); + + it('should open a terminal in a new group when the split button is pressed', async () => { + await terminal.runCommand(TerminalCommandId.CreateNewEditor); + await terminal.clickSplitButton(); + await terminal.assertEditorGroupCount(2); + }); + + it('should create new terminals in the active editor group via command', async () => { + await terminal.runCommand(TerminalCommandId.CreateNewEditor); + await terminal.runCommand(TerminalCommandId.CreateNewEditor); + await terminal.assertEditorGroupCount(1); + }); + + it('should create new terminals in the active editor group via plus button', async () => { + await terminal.runCommand(TerminalCommandId.CreateNewEditor); + await terminal.clickPlusButton(); + await terminal.assertEditorGroupCount(1); + }); + }); +} diff --git a/test/smoke/src/areas/terminal/terminal-profiles.test.ts b/test/smoke/src/areas/terminal/terminal-profiles.test.ts index 0f202d6e40c..0d111333689 100644 --- a/test/smoke/src/areas/terminal/terminal-profiles.test.ts +++ b/test/smoke/src/areas/terminal/terminal-profiles.test.ts @@ -3,31 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ok } from 'assert'; import { ParsedArgs } from 'minimist'; -import { Code, Terminal } from '../../../../automation'; +import { Terminal, TerminalCommandId, TerminalCommandIdWithValue } from '../../../../automation'; import { afterSuite, beforeSuite } from '../../utils'; -const ContributedProfileName = `JavaScript Debug Terminal`; +const CONTRIBUTED_PROFILE_NAME = `JavaScript Debug Terminal`; +const ANY_PROFILE_NAME = '^((?!JavaScript Debug Terminal).)*$'; export function setup(opts: ParsedArgs) { - describe('Terminal Profiles', () => { - let code: Code; let terminal: Terminal; - const enum TerminalCommandId { - Split = 'workbench.action.terminal.split', - KillAll = 'workbench.action.terminal.killAll', - Show = 'workbench.action.terminal.toggleTerminal', - CreateNew = 'workbench.action.terminal.new', - NewWithProfile = 'workbench.action.terminal.newWithProfile', - SelectDefaultProfile = 'workbench.action.terminal.selectDefaultShell' - } + beforeSuite(opts); afterSuite(opts); before(function () { - code = this.app.code; terminal = this.app.workbench.terminal; }); @@ -37,73 +27,56 @@ export function setup(opts: ParsedArgs) { it('should launch the default profile', async () => { await terminal.runCommand(TerminalCommandId.Show); - // TODO: Use getSingleTabLabel? Share logic with getTabLabel? - await code.waitForElement('.single-terminal-tab', e => e ? !e.textContent.endsWith(ContributedProfileName) : false); + await terminal.assertSingleTab({ name: ANY_PROFILE_NAME }); }); it.skip('should set the default profile to a contributed one', async () => { - await terminal.runProfileCommand(TerminalCommandId.SelectDefaultProfile, true); + await terminal.runCommandWithValue(TerminalCommandIdWithValue.SelectDefaultProfile, CONTRIBUTED_PROFILE_NAME); await terminal.runCommand(TerminalCommandId.CreateNew); - await code.waitForElement('.single-terminal-tab', e => e ? e.textContent.endsWith(ContributedProfileName) : false); + await terminal.assertSingleTab({ name: CONTRIBUTED_PROFILE_NAME }); }); it.skip('should use the default contributed profile on panel open and for splitting', async () => { - await terminal.runProfileCommand(TerminalCommandId.SelectDefaultProfile, true); + await terminal.runCommandWithValue(TerminalCommandIdWithValue.SelectDefaultProfile, CONTRIBUTED_PROFILE_NAME); await terminal.runCommand(TerminalCommandId.Show); await terminal.runCommand(TerminalCommandId.Split); - const tabs = await terminal.getTabLabels(2); - console.log('DEBUG: tabs', tabs); - ok(tabs[0].startsWith('โ”Œ') && tabs[0].endsWith(ContributedProfileName)); - ok(tabs[1].startsWith('โ””') && tabs[1].endsWith(ContributedProfileName)); + await terminal.assertTerminalGroups([[{ name: CONTRIBUTED_PROFILE_NAME }, { name: CONTRIBUTED_PROFILE_NAME }]]); }); it('should set the default profile', async () => { - await terminal.runProfileCommand(TerminalCommandId.SelectDefaultProfile, undefined); + await terminal.runCommandWithValue(TerminalCommandIdWithValue.SelectDefaultProfile); await terminal.runCommand(TerminalCommandId.CreateNew); - await code.waitForElement('.single-terminal-tab', e => e ? !e.textContent.endsWith(ContributedProfileName) : false); + await terminal.assertSingleTab({ name: ANY_PROFILE_NAME }); }); it('should use the default profile on panel open and for splitting', async () => { await terminal.runCommand(TerminalCommandId.Show); - await code.waitForElement('.single-terminal-tab', e => e ? !e.textContent.endsWith(ContributedProfileName) : false); + await terminal.assertSingleTab({ name: ANY_PROFILE_NAME }); await terminal.runCommand(TerminalCommandId.Split); - const tabs = await terminal.getTabLabels(2, true); - ok(tabs[0].startsWith('โ”Œ') && !tabs[0].endsWith(ContributedProfileName)); - ok(tabs[1].startsWith('โ””') && !tabs[1].endsWith(ContributedProfileName)); - }); - - it('clicking the plus button should create a terminal and display the tabs view showing no split decorations', async () => { - await terminal.runCommand(TerminalCommandId.Show); - await code.waitAndClick('li.action-item.monaco-dropdown-with-primary > div.action-container.menu-entry > a'); - const tabLabels = await terminal.getTabLabels(2); - ok(!tabLabels[0].startsWith('โ”Œ') && !tabLabels[1].startsWith('โ””')); + await terminal.assertTerminalGroups([[{}, {}]]); }); it('createWithProfile command should create a terminal with a profile', async () => { - await terminal.runProfileCommand(TerminalCommandId.NewWithProfile); - await code.waitForElement('.single-terminal-tab', e => e ? !e.textContent.endsWith(ContributedProfileName) : false); + await terminal.runCommandWithValue(TerminalCommandIdWithValue.NewWithProfile); + await terminal.assertSingleTab({ name: ANY_PROFILE_NAME }); }); it.skip('createWithProfile command should create a terminal with a contributed profile', async () => { - await terminal.runProfileCommand(TerminalCommandId.NewWithProfile, true); - await code.waitForElement('.single-terminal-tab', e => e ? e.textContent.endsWith(ContributedProfileName) : false); + await terminal.runCommandWithValue(TerminalCommandIdWithValue.NewWithProfile, CONTRIBUTED_PROFILE_NAME); + await terminal.assertSingleTab({ name: CONTRIBUTED_PROFILE_NAME }); }); it('createWithProfile command should create a split terminal with a profile', async () => { await terminal.runCommand(TerminalCommandId.Show); - await terminal.runProfileCommand(TerminalCommandId.NewWithProfile, undefined, true); - const tabs = await terminal.getTabLabels(2, true); - ok(tabs[0].startsWith('โ”Œ') && !tabs[0].endsWith(ContributedProfileName)); - ok(tabs[1].startsWith('โ””') && !tabs[1].endsWith(ContributedProfileName)); + await terminal.runCommandWithValue(TerminalCommandIdWithValue.NewWithProfile, undefined, true); + await terminal.assertTerminalGroups([[{}, {}]]); }); it.skip('createWithProfile command should create a split terminal with a contributed profile', async () => { await terminal.runCommand(TerminalCommandId.Show); - await code.waitForElement('.single-terminal-tab', e => e ? !e.textContent.endsWith(ContributedProfileName) : false); - await terminal.runProfileCommand(TerminalCommandId.NewWithProfile, true, true); - const tabs = await terminal.getTabLabels(2, true); - ok(tabs[0].startsWith('โ”Œ') && !tabs[0].endsWith(ContributedProfileName)); - ok(tabs[1].startsWith('โ””') && tabs[1].endsWith(ContributedProfileName)); + await terminal.assertSingleTab({}); + await terminal.runCommandWithValue(TerminalCommandIdWithValue.NewWithProfile, CONTRIBUTED_PROFILE_NAME, true); + await terminal.assertTerminalGroups([[{ name: ANY_PROFILE_NAME }, { name: CONTRIBUTED_PROFILE_NAME }]]); }); }); } diff --git a/test/smoke/src/areas/terminal/terminal-tabs.test.ts b/test/smoke/src/areas/terminal/terminal-tabs.test.ts index 1a5897eb0fd..161f077eeef 100644 --- a/test/smoke/src/areas/terminal/terminal-tabs.test.ts +++ b/test/smoke/src/areas/terminal/terminal-tabs.test.ts @@ -3,35 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ok } from 'assert'; + import { ParsedArgs } from 'minimist'; -import { Code, Terminal } from '../../../../automation/out'; +import { Terminal, TerminalCommandId, TerminalCommandIdWithValue } from '../../../../automation/out'; import { afterSuite, beforeSuite } from '../../utils'; export function setup(opts: ParsedArgs) { - // TODO: Re-enable when stable - describe.skip('Terminal Tabs', () => { - let code: Code; + describe('Terminal Tabs', () => { let terminal: Terminal; - // TODO: Move into automation/terminal - const enum TerminalCommandId { - Rename = 'workbench.action.terminal.rename', - ChangeColor = 'workbench.action.terminal.changeColor', - ChangeIcon = 'workbench.action.terminal.changeIcon', - 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', - CreateNew = 'workbench.action.terminal.new' - } - beforeSuite(opts); afterSuite(opts); before(function () { - code = this.app.code; terminal = this.app.workbench.terminal; }); @@ -41,99 +25,99 @@ export function setup(opts: ParsedArgs) { it('clicking the plus button should create a terminal and display the tabs view showing no split decorations', async () => { await terminal.runCommand(TerminalCommandId.Show); - await code.waitAndClick('li.action-item.monaco-dropdown-with-primary > div.action-container.menu-entry > a'); - const tabLabels = await terminal.getTabLabels(2); - ok(!tabLabels[0].startsWith('โ”Œ') && !tabLabels[1].startsWith('โ””')); + await terminal.runCommand(TerminalCommandId.CreateNew); + await terminal.clickPlusButton(); + await terminal.assertTerminalGroups([[{}], [{}]]); }); it('should update color of the single tab', async () => { await terminal.runCommand(TerminalCommandId.Show); const color = 'Cyan'; - await terminal.runCommand(TerminalCommandId.ChangeColor, color); - const singleTab = await code.waitForElement('.single-terminal-tab'); - ok(singleTab.className.includes(`terminal-icon-terminal_ansi${color}`)); + await terminal.runCommandWithValue(TerminalCommandIdWithValue.ChangeColor, color); + await terminal.assertSingleTab({ color }); }); it('should update color of the tab in the tabs list', async () => { await terminal.runCommand(TerminalCommandId.Show); await terminal.runCommand(TerminalCommandId.Split); - const tabs = await terminal.getTabLabels(2); - ok(tabs[0].startsWith('โ”Œ')); - ok(tabs[1].startsWith('โ””')); const color = 'Cyan'; - await terminal.runCommand(TerminalCommandId.ChangeColor, color); - await code.waitForElement(`.terminal-tabs-entry .terminal-icon-terminal_ansi${color}`); + await terminal.runCommandWithValue(TerminalCommandIdWithValue.ChangeColor, color); + await terminal.assertTerminalGroups([[{}, { color }]]); }); it('should update icon of the single tab', async () => { await terminal.runCommand(TerminalCommandId.Show); const icon = 'symbol-method'; - await terminal.runCommand(TerminalCommandId.ChangeIcon, icon); - await code.waitForElement(`.single-terminal-tab .codicon-${icon}`); + await terminal.runCommandWithValue(TerminalCommandIdWithValue.ChangeIcon, icon); + await terminal.assertSingleTab({ icon }); }); it('should update icon of the tab in the tabs list', async () => { await terminal.runCommand(TerminalCommandId.Show); await terminal.runCommand(TerminalCommandId.Split); - const tabs = await terminal.getTabLabels(2); - ok(tabs[0].startsWith('โ”Œ')); - ok(tabs[1].startsWith('โ””')); const icon = 'symbol-method'; - await terminal.runCommand(TerminalCommandId.ChangeIcon, icon); - await code.waitForElement(`.terminal-tabs-entry .codicon-${icon}`); + await terminal.runCommandWithValue(TerminalCommandIdWithValue.ChangeIcon, icon); + await terminal.assertTerminalGroups([[{}, { icon }]]); }); it('should rename the single tab', async () => { await terminal.runCommand(TerminalCommandId.Show); const name = 'my terminal name'; - await terminal.runCommand(TerminalCommandId.Rename, name); - await code.waitForElement('.single-terminal-tab', e => e ? e?.textContent.includes(name) : false); + await terminal.runCommandWithValue(TerminalCommandIdWithValue.Rename, name); + await terminal.assertSingleTab({ name }); }); it('should rename the tab in the tabs list', async () => { await terminal.runCommand(TerminalCommandId.Show); await terminal.runCommand(TerminalCommandId.Split); const name = 'my terminal name'; - await terminal.runCommand(TerminalCommandId.Rename, name); - await terminal.getTabLabels(2, true, t => t.some(element => element.textContent.includes(name))); + await terminal.runCommandWithValue(TerminalCommandIdWithValue.Rename, name); + await terminal.assertTerminalGroups([[{}, { name }]]); }); it('should create a split terminal when single tab is alt clicked', async () => { await terminal.runCommand(TerminalCommandId.Show); const page = await terminal.getPage(); page.keyboard.down('Alt'); - await code.waitAndClick('.single-terminal-tab'); + await terminal.clickSingleTab(); page.keyboard.up('Alt'); - await terminal.getTabLabels(2, true); + await terminal.assertTerminalGroups([[{}, {}]]); }); it('should do nothing when join tabs is run with only one terminal', async () => { await terminal.runCommand(TerminalCommandId.Show); await terminal.runCommand(TerminalCommandId.Join); - await code.waitForElement('.single-terminal-tab'); + await terminal.assertSingleTab({}); }); it('should join tabs when more than one terminal', async () => { await terminal.runCommand(TerminalCommandId.Show); await terminal.runCommand(TerminalCommandId.CreateNew); await terminal.runCommand(TerminalCommandId.Join); - await terminal.getTabLabels(2, true); + await terminal.assertTerminalGroups([[{}, {}]]); }); it('should do nothing when unsplit tabs called with no splits', async () => { await terminal.runCommand(TerminalCommandId.Show); await terminal.runCommand(TerminalCommandId.CreateNew); - await terminal.getTabLabels(2, false); + await terminal.assertTerminalGroups([[{}], [{}]]); await terminal.runCommand(TerminalCommandId.Unsplit); - await terminal.getTabLabels(2, false); + await terminal.assertTerminalGroups([[{}], [{}]]); }); it('should unsplit tabs', async () => { await terminal.runCommand(TerminalCommandId.Show); await terminal.runCommand(TerminalCommandId.Split); - await terminal.getTabLabels(2, true); + await terminal.assertTerminalGroups([[{}, {}]]); await terminal.runCommand(TerminalCommandId.Unsplit); - await terminal.getTabLabels(2, false, t => t.every(label => !label.textContent.startsWith('โ”Œ') && !label.textContent.startsWith('โ””'))); + await terminal.assertTerminalGroups([[{}], [{}]]); + }); + + it('should move the terminal to the editor area', async () => { + await terminal.runCommand(TerminalCommandId.Show); + await terminal.assertSingleTab({}); + await terminal.runCommand(TerminalCommandId.MoveToEditor); + await terminal.assertEditorGroupCount(1); }); }); } diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 7f95e942bec..734aef1733d 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -29,6 +29,7 @@ import { setup as setupLocalizationTests } from './areas/workbench/localization. import { setup as setupLaunchTests } from './areas/workbench/launch.test'; import { setup as setupTerminalProfileTests } from './areas/terminal/terminal-profiles.test'; import { setup as setupTerminalTabsTests } from './areas/terminal/terminal-tabs.test'; +import { setup as setupTerminalEditorsTests } from './areas/terminal/terminal-editors.test'; const testDataPath = path.join(os.tmpdir(), 'vscsmoke'); if (fs.existsSync(testDataPath)) { @@ -362,4 +363,5 @@ describe(`VSCode Smoke Tests (${opts.web ? 'Web' : 'Electron'})`, () => { // TODO: Enable terminal tests for non-web if (opts.web) { setupTerminalProfileTests(opts); } if (opts.web) { setupTerminalTabsTests(opts); } + if (opts.web) { setupTerminalEditorsTests(opts); } });