diff --git a/test/automation/src/activityBar.ts b/test/automation/src/activityBar.ts deleted file mode 100644 index 67254fff9fd..00000000000 --- a/test/automation/src/activityBar.ts +++ /dev/null @@ -1,30 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Code } from './code'; - -export const enum ActivityBarPosition { - LEFT = 0, - RIGHT = 1 -} - -export class ActivityBar { - - constructor(private code: Code) { } - - async waitForActivityBar(position: ActivityBarPosition): Promise { - let positionClass: string; - - if (position === ActivityBarPosition.LEFT) { - positionClass = 'left'; - } else if (position === ActivityBarPosition.RIGHT) { - positionClass = 'right'; - } else { - throw new Error('No such position for activity bar defined.'); - } - - await this.code.waitForElement(`.part.activitybar.${positionClass}`); - } -} diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index 5235a845a89..365cd5e60ff 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -6,7 +6,9 @@ import * as cp from 'child_process'; import * as os from 'os'; import * as treekill from 'tree-kill'; -import { IElement, ILocaleInfo, ILocalizedStrings, ILogFile } from './driver'; +// --- Start Positron --- +import { ILogFile } from './driver'; +// --- End Positron --- import { Logger, measureAndLog } from './logger'; import { launch as launchPlaywrightBrowser } from './playwrightBrowser'; import { PlaywrightDriver } from './playwrightDriver'; @@ -130,9 +132,9 @@ export class Code { } // --- End Positron --- - async dispatchKeybinding(keybinding: string): Promise { - await this.driver.dispatchKeybinding(keybinding); - } + // --- Start Positron --- + // Removed function + // --- End Positron --- async didFinishLoad(): Promise { return this.driver.didFinishLoad(); @@ -200,72 +202,17 @@ export class Code { }), 'Code#exit()', this.logger); } - async getElement(selector: string): Promise { - return (await this.driver.getElements(selector))?.[0]; - } - - async getElements(selector: string, recursive: boolean): Promise { - return this.driver.getElements(selector, recursive); - } - - async waitForTextContent(selector: string, textContent?: string, accept?: (result: string) => boolean, retryCount?: number): Promise { - accept = accept || (result => textContent !== undefined ? textContent === result : !!result); - - return await this.poll( - () => this.driver.getElements(selector).then(els => els.length > 0 ? Promise.resolve(els[0].textContent) : Promise.reject(new Error('Element not found for textContent'))), - s => accept!(typeof s === 'string' ? s : ''), - `get text content '${selector}'`, - retryCount - ); - } - - async waitAndClick(selector: string, xoffset?: number, yoffset?: number, retryCount: number = 200): Promise { - await this.poll(() => this.driver.click(selector, xoffset, yoffset), () => true, `click '${selector}'`, retryCount); - } - - async waitForSetValue(selector: string, value: string): Promise { - await this.poll(() => this.driver.setValue(selector, value), () => true, `set value '${selector}'`); - } - - async waitForElements(selector: string, recursive: boolean, accept: (result: IElement[]) => boolean = result => result.length > 0): Promise { - return await this.poll(() => this.driver.getElements(selector, recursive), accept, `get elements '${selector}'`); - } - - async waitForElement(selector: string, accept: (result: IElement | undefined) => boolean = result => !!result, retryCount: number = 200): Promise { - return await this.poll(() => this.driver.getElements(selector).then(els => els[0]), accept, `get element '${selector}'`, retryCount); - } - - async waitForActiveElement(selector: string, retryCount: number = 200): Promise { - await this.poll(() => this.driver.isActiveElement(selector), r => r, `is active element '${selector}'`, retryCount); - } - - async waitForTitle(accept: (title: string) => boolean): Promise { - await this.poll(() => this.driver.getTitle(), accept, `get title`); - } - - async waitForTypeInEditor(selector: string, text: string): Promise { - await this.poll(() => this.driver.typeInEditor(selector, text), () => true, `type in editor '${selector}'`); - } - - async waitForTerminalBuffer(selector: string, accept: (result: string[]) => boolean): Promise { - await this.poll(() => this.driver.getTerminalBuffer(selector), accept, `get terminal buffer '${selector}'`); - } - - async writeInTerminal(selector: string, value: string): Promise { - await this.poll(() => this.driver.writeInTerminal(selector, value), () => true, `writeInTerminal '${selector}'`); - } + // --- Start Positron --- + // Removed functions + // --- End Positron --- async whenWorkbenchRestored(): Promise { await this.poll(() => this.driver.whenWorkbenchRestored(), () => true, `when workbench restored`); } - getLocaleInfo(): Promise { - return this.driver.getLocaleInfo(); - } - - getLocalizedStrings(): Promise { - return this.driver.getLocalizedStrings(); - } + // --- Start Positron --- + // Removed functions + // --- End Positron --- getLogs(): Promise { return this.driver.getLogs(); @@ -312,35 +259,6 @@ export class Code { } } -export function findElement(element: IElement, fn: (element: IElement) => boolean): IElement | null { - const queue = [element]; - - while (queue.length > 0) { - const element = queue.shift()!; - - if (fn(element)) { - return element; - } - - queue.push(...element.children); - } - - return null; -} - -export function findElements(element: IElement, fn: (element: IElement) => boolean): IElement[] { - const result: IElement[] = []; - const queue = [element]; - - while (queue.length > 0) { - const element = queue.shift()!; - - if (fn(element)) { - result.push(element); - } - - queue.push(...element.children); - } - - return result; -} +// --- Start Positron --- +// Removed functions +// --- End Positron --- diff --git a/test/automation/src/debug.ts b/test/automation/src/debug.ts deleted file mode 100644 index b7b7d427f4b..00000000000 --- a/test/automation/src/debug.ts +++ /dev/null @@ -1,153 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Viewlet } from './viewlet'; -import { Commands } from './workbench'; -import { Code, findElement } from './code'; -import { Editors } from './editors'; -import { Editor } from './editor'; -import { IElement } from './driver'; - -const VIEWLET = 'div[id="workbench.view.debug"]'; -const DEBUG_VIEW = `${VIEWLET}`; -const CONFIGURE = `div[id="workbench.parts.sidebar"] .actions-container .codicon-gear`; -const STOP = `.debug-toolbar .action-label[title*="Stop"]`; -const STEP_OVER = `.debug-toolbar .action-label[title*="Step Over"]`; -const STEP_IN = `.debug-toolbar .action-label[title*="Step Into"]`; -const STEP_OUT = `.debug-toolbar .action-label[title*="Step Out"]`; -const CONTINUE = `.debug-toolbar .action-label[title*="Continue"]`; -const GLYPH_AREA = '.margin-view-overlays>:nth-child'; -const BREAKPOINT_GLYPH = '.codicon-debug-breakpoint'; -const PAUSE = `.debug-toolbar .action-label[title*="Pause"]`; -const DEBUG_STATUS_BAR = `.statusbar.debugging`; -const NOT_DEBUG_STATUS_BAR = `.statusbar:not(debugging)`; -const TOOLBAR_HIDDEN = `.debug-toolbar[aria-hidden="true"]`; -const STACK_FRAME = `${VIEWLET} .monaco-list-row .stack-frame`; -const SPECIFIC_STACK_FRAME = (filename: string) => `${STACK_FRAME} .file[title*="${filename}"]`; -const VARIABLE = `${VIEWLET} .debug-variables .monaco-list-row .expression`; -const CONSOLE_OUTPUT = `.repl .output.expression .value`; -const CONSOLE_EVALUATION_RESULT = `.repl .evaluation-result.expression .value`; -const CONSOLE_LINK = `.repl .value a.link`; - -const REPL_FOCUSED = '.repl-input-wrapper .monaco-editor textarea'; - -export interface IStackFrame { - name: string; - lineNumber: number; -} - -function toStackFrame(element: IElement): IStackFrame { - const name = findElement(element, e => /\bfile-name\b/.test(e.className))!; - const line = findElement(element, e => /\bline-number\b/.test(e.className))!; - const lineNumber = line.textContent ? parseInt(line.textContent.split(':').shift() || '0') : 0; - - return { - name: name.textContent || '', - lineNumber - }; -} - -export class Debug extends Viewlet { - - constructor(code: Code, private commands: Commands, private editors: Editors, private editor: Editor) { - super(code); - } - - async openDebugViewlet(): Promise { - if (process.platform === 'darwin') { - await this.code.dispatchKeybinding('cmd+shift+d'); - } else { - await this.code.dispatchKeybinding('ctrl+shift+d'); - } - - await this.code.waitForElement(DEBUG_VIEW); - } - - async configure(): Promise { - await this.code.waitAndClick(CONFIGURE); - await this.editors.waitForEditorFocus('launch.json'); - } - - async setBreakpointOnLine(lineNumber: number): Promise { - await this.code.waitForElement(`${GLYPH_AREA}(${lineNumber})`); - await this.code.waitAndClick(`${GLYPH_AREA}(${lineNumber})`, 5, 5); - await this.code.waitForElement(BREAKPOINT_GLYPH); - } - - async startDebugging(): Promise { - await this.code.dispatchKeybinding('f5'); - await this.code.waitForElement(PAUSE); - await this.code.waitForElement(DEBUG_STATUS_BAR); - const portPrefix = 'Port: '; - - const output = await this.waitForOutput(output => output.some(line => line.indexOf(portPrefix) >= 0)); - const lastOutput = output.filter(line => line.indexOf(portPrefix) >= 0)[0]; - - return lastOutput ? parseInt(lastOutput.substr(portPrefix.length)) : 3000; - } - - async stepOver(): Promise { - await this.code.waitAndClick(STEP_OVER); - } - - async stepIn(): Promise { - await this.code.waitAndClick(STEP_IN); - } - - async stepOut(): Promise { - await this.code.waitAndClick(STEP_OUT); - } - - async continue(): Promise { - await this.code.waitAndClick(CONTINUE); - await this.waitForStackFrameLength(0); - } - - async stopDebugging(): Promise { - await this.code.waitAndClick(STOP); - await this.code.waitForElement(TOOLBAR_HIDDEN); - await this.code.waitForElement(NOT_DEBUG_STATUS_BAR); - } - - async waitForStackFrame(func: (stackFrame: IStackFrame) => boolean, message: string): Promise { - const elements = await this.code.waitForElements(STACK_FRAME, true, elements => elements.some(e => func(toStackFrame(e)))); - return elements.map(toStackFrame).filter(s => func(s))[0]; - } - - async waitForStackFrameLength(length: number): Promise { - await this.code.waitForElements(STACK_FRAME, false, result => result.length === length); - } - - async focusStackFrame(name: string, message: string): Promise { - await this.code.waitAndClick(SPECIFIC_STACK_FRAME(name), 0, 0); - await this.editors.waitForTab(name); - } - - async waitForReplCommand(text: string, accept: (result: string) => boolean): Promise { - await this.commands.runCommand('Debug: Focus on Debug Console View'); - await this.code.waitForActiveElement(REPL_FOCUSED); - await this.code.waitForSetValue(REPL_FOCUSED, text); - - // Wait for the keys to be picked up by the editor model such that repl evaluates what just got typed - await this.editor.waitForEditorContents('debug:replinput', s => s.indexOf(text) >= 0); - await this.code.dispatchKeybinding('enter'); - await this.code.waitForElements(CONSOLE_EVALUATION_RESULT, false, - elements => !!elements.length && accept(elements[elements.length - 1].textContent)); - } - - // Different node versions give different number of variables. As a workaround be more relaxed when checking for variable count - async waitForVariableCount(count: number, alternativeCount: number): Promise { - await this.code.waitForElements(VARIABLE, false, els => els.length === count || els.length === alternativeCount); - } - - async waitForLink(): Promise { - await this.code.waitForElement(CONSOLE_LINK); - } - - private async waitForOutput(fn: (output: string[]) => boolean): Promise { - const elements = await this.code.waitForElements(CONSOLE_OUTPUT, false, elements => fn(elements.map(e => e.textContent))); - return elements.map(e => e.textContent); - } -} diff --git a/test/automation/src/editor.ts b/test/automation/src/editor.ts deleted file mode 100644 index 538866bfc06..00000000000 --- a/test/automation/src/editor.ts +++ /dev/null @@ -1,127 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { References } from './peek'; -import { Commands } from './workbench'; -import { Code } from './code'; - -const RENAME_BOX = '.monaco-editor .monaco-editor.rename-box'; -const RENAME_INPUT = `${RENAME_BOX} .rename-input`; -const EDITOR = (filename: string) => `.monaco-editor[data-uri$="${filename}"]`; -const VIEW_LINES = (filename: string) => `${EDITOR(filename)} .view-lines`; -const LINE_NUMBERS = (filename: string) => `${EDITOR(filename)} .margin .margin-view-overlays .line-numbers`; - -export class Editor { - - private static readonly FOLDING_EXPANDED = '.monaco-editor .margin .margin-view-overlays>:nth-child(${INDEX}) .folding'; - private static readonly FOLDING_COLLAPSED = `${Editor.FOLDING_EXPANDED}.collapsed`; - - constructor(private code: Code, private commands: Commands) { } - - async findReferences(filename: string, term: string, line: number): Promise { - await this.clickOnTerm(filename, term, line); - await this.commands.runCommand('Peek References'); - const references = new References(this.code); - await references.waitUntilOpen(); - return references; - } - - async rename(filename: string, line: number, from: string, to: string): Promise { - await this.clickOnTerm(filename, from, line); - await this.commands.runCommand('Rename Symbol'); - - await this.code.waitForActiveElement(RENAME_INPUT); - await this.code.waitForSetValue(RENAME_INPUT, to); - - await this.code.dispatchKeybinding('enter'); - } - - async gotoDefinition(filename: string, term: string, line: number): Promise { - await this.clickOnTerm(filename, term, line); - await this.commands.runCommand('Go to Implementations'); - } - - async peekDefinition(filename: string, term: string, line: number): Promise { - await this.clickOnTerm(filename, term, line); - await this.commands.runCommand('Peek Definition'); - const peek = new References(this.code); - await peek.waitUntilOpen(); - return peek; - } - - private async getSelector(filename: string, term: string, line: number): Promise { - const lineIndex = await this.getViewLineIndex(filename, line); - const classNames = await this.getClassSelectors(filename, term, lineIndex); - - return `${VIEW_LINES(filename)}>:nth-child(${lineIndex}) span span.${classNames[0]}`; - } - - async foldAtLine(filename: string, line: number): Promise { - const lineIndex = await this.getViewLineIndex(filename, line); - await this.code.waitAndClick(Editor.FOLDING_EXPANDED.replace('${INDEX}', '' + lineIndex)); - await this.code.waitForElement(Editor.FOLDING_COLLAPSED.replace('${INDEX}', '' + lineIndex)); - } - - async unfoldAtLine(filename: string, line: number): Promise { - const lineIndex = await this.getViewLineIndex(filename, line); - await this.code.waitAndClick(Editor.FOLDING_COLLAPSED.replace('${INDEX}', '' + lineIndex)); - await this.code.waitForElement(Editor.FOLDING_EXPANDED.replace('${INDEX}', '' + lineIndex)); - } - - private async clickOnTerm(filename: string, term: string, line: number): Promise { - const selector = await this.getSelector(filename, term, line); - await this.code.waitAndClick(selector); - } - - async waitForEditorFocus(filename: string, lineNumber: number, selectorPrefix = ''): Promise { - const editor = [selectorPrefix || '', EDITOR(filename)].join(' '); - const line = `${editor} .view-lines > .view-line:nth-child(${lineNumber})`; - const textarea = `${editor} textarea`; - - await this.code.waitAndClick(line, 1, 1); - await this.code.waitForActiveElement(textarea); - } - - async waitForTypeInEditor(filename: string, text: string, selectorPrefix = ''): Promise { - if (text.includes('\n')) { - throw new Error('waitForTypeInEditor does not support new lines, use either a long single line or dispatchKeybinding(\'Enter\')'); - } - const editor = [selectorPrefix || '', EDITOR(filename)].join(' '); - - await this.code.waitForElement(editor); - - const textarea = `${editor} textarea`; - await this.code.waitForActiveElement(textarea); - - await this.code.waitForTypeInEditor(textarea, text); - - await this.waitForEditorContents(filename, c => c.indexOf(text) > -1, selectorPrefix); - } - - async waitForEditorContents(filename: string, accept: (contents: string) => boolean, selectorPrefix = ''): Promise { - const selector = [selectorPrefix || '', `${EDITOR(filename)} .view-lines`].join(' '); - return this.code.waitForTextContent(selector, undefined, c => accept(c.replace(/\u00a0/g, ' '))); - } - - private async getClassSelectors(filename: string, term: string, viewline: number): Promise { - const elements = await this.code.waitForElements(`${VIEW_LINES(filename)}>:nth-child(${viewline}) span span`, false, els => els.some(el => el.textContent === term)); - const { className } = elements.filter(r => r.textContent === term)[0]; - return className.split(/\s/g); - } - - private async getViewLineIndex(filename: string, line: number): Promise { - const elements = await this.code.waitForElements(LINE_NUMBERS(filename), false, els => { - return els.some(el => el.textContent === `${line}`); - }); - - for (let index = 0; index < elements.length; index++) { - if (elements[index].textContent === `${line}`) { - return index + 1; - } - } - - throw new Error('Line not found'); - } -} diff --git a/test/automation/src/editors.ts b/test/automation/src/editors.ts deleted file mode 100644 index b175959bd90..00000000000 --- a/test/automation/src/editors.ts +++ /dev/null @@ -1,78 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Code } from './code'; - -export class Editors { - // --- Start Positron --- - activeEditor = this.code.driver.getLocator('div.tab.tab-actions-right.active.selected'); - editorIcon = this.code.driver.getLocator('.monaco-icon-label.file-icon'); - editorPart = this.code.driver.getLocator('.split-view-view .part.editor'); - // --- End Positron --- - - constructor(private code: Code) { } - - async saveOpenedFile(): Promise { - if (process.platform === 'darwin') { - await this.code.dispatchKeybinding('cmd+s'); - } else { - await this.code.dispatchKeybinding('ctrl+s'); - } - } - - async selectTab(fileName: string): Promise { - - // Selecting a tab and making an editor have keyboard focus - // is critical to almost every test. As such, we try our - // best to retry this task in case some other component steals - // focus away from the editor while we attempt to get focus - - let error: unknown | undefined = undefined; - let retries = 0; - while (retries < 10) { - await this.code.waitAndClick(`.tabs-container div.tab[data-resource-name$="${fileName}"]`); - await this.code.dispatchKeybinding(process.platform === 'darwin' ? 'cmd+1' : 'ctrl+1'); // make editor really active if click failed somehow - - try { - await this.waitForEditorFocus(fileName, 50 /* 50 retries * 100ms delay = 5s */); - return; - } catch (e) { - error = e; - retries++; - } - } - - // We failed after 10 retries - throw error; - } - - async waitForEditorFocus(fileName: string, retryCount?: number): Promise { - await this.waitForActiveTab(fileName, undefined, retryCount); - await this.waitForActiveEditor(fileName, retryCount); - } - - async waitForActiveTab(fileName: string, isDirty: boolean = false, retryCount?: number): Promise { - await this.code.waitForElement(`.tabs-container div.tab.active${isDirty ? '.dirty' : ''}[aria-selected="true"][data-resource-name$="${fileName}"]`, undefined, retryCount); - } - - async waitForActiveEditor(fileName: string, retryCount?: number): Promise { - const selector = `.editor-instance .monaco-editor[data-uri$="${fileName}"] textarea`; - return this.code.waitForActiveElement(selector, retryCount); - } - - async waitForTab(fileName: string, isDirty: boolean = false): Promise { - await this.code.waitForElement(`.tabs-container div.tab${isDirty ? '.dirty' : ''}[data-resource-name$="${fileName}"]`); - } - - async newUntitledFile(): Promise { - if (process.platform === 'darwin') { - await this.code.dispatchKeybinding('cmd+n'); - } else { - await this.code.dispatchKeybinding('ctrl+n'); - } - - await this.waitForEditorFocus('Untitled-1'); - } -} diff --git a/test/automation/src/electron.ts b/test/automation/src/electron.ts index 8a9a73974f6..4f19916c395 100644 --- a/test/automation/src/electron.ts +++ b/test/automation/src/electron.ts @@ -5,10 +5,17 @@ import { join } from 'path'; import * as fs from 'fs'; -import { copyExtension } from './extensions'; +// -- Start Positron -- +// Removed import +// -- End Positron -- import { URI } from 'vscode-uri'; import { measureAndLog } from './logger'; import type { LaunchOptions } from './code'; +// -- Start Positron -- +import path = require('path'); +import { promisify } from 'util'; +import { ncp } from 'ncp'; +// -- End Positron -- const root = join(__dirname, '..', '..', '..'); @@ -137,3 +144,14 @@ export function getBuildVersion(root: string): string { return require(join(root, 'resources', 'app', 'package.json')).version; } } + +// -- Start Positron -- +export async function copyExtension(repoPath: string, extensionsPath: string, extId: string): Promise { + const dest = path.join(extensionsPath, extId); + if (!fs.existsSync(dest)) { + const orig = path.join(repoPath, 'extensions', extId); + + return promisify(ncp)(orig, dest); + } +} +// -- End Positron -- diff --git a/test/automation/src/explorer.ts b/test/automation/src/explorer.ts deleted file mode 100644 index 2c1e2eeec28..00000000000 --- a/test/automation/src/explorer.ts +++ /dev/null @@ -1,42 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Viewlet } from './viewlet'; -import { Code } from './code'; - -export class Explorer extends Viewlet { - - private static readonly EXPLORER_VIEWLET = 'div[id="workbench.view.explorer"]'; - private static readonly OPEN_EDITORS_VIEW = `${Explorer.EXPLORER_VIEWLET} .split-view-view:nth-child(1) .title`; - - constructor(code: Code) { - super(code); - } - - async openExplorerView(): Promise { - if (process.platform === 'darwin') { - await this.code.dispatchKeybinding('cmd+shift+e'); - } else { - await this.code.dispatchKeybinding('ctrl+shift+e'); - } - } - - async waitForOpenEditorsViewTitle(fn: (title: string) => boolean): Promise { - await this.code.waitForTextContent(Explorer.OPEN_EDITORS_VIEW, undefined, fn); - } - - getExtensionSelector(fileName: string): string { - const extension = fileName.split('.')[1]; - if (extension === 'js') { - return 'js-ext-file-icon ext-file-icon javascript-lang-file-icon'; - } else if (extension === 'json') { - return 'json-ext-file-icon ext-file-icon json-lang-file-icon'; - } else if (extension === 'md') { - return 'md-ext-file-icon ext-file-icon markdown-lang-file-icon'; - } - throw new Error('No class defined for this file extension'); - } - -} diff --git a/test/automation/src/extensions.ts b/test/automation/src/extensions.ts deleted file mode 100644 index b092a2e8b4d..00000000000 --- a/test/automation/src/extensions.ts +++ /dev/null @@ -1,69 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Viewlet } from './viewlet'; -import { Code } from './code'; -import path = require('path'); -import fs = require('fs'); -import { ncp } from 'ncp'; -import { promisify } from 'util'; -import { Commands } from './workbench'; - - -export class Extensions extends Viewlet { - - constructor(code: Code, private commands: Commands) { - super(code); - } - - async searchForExtension(id: string): Promise { - await this.commands.runCommand('Extensions: Focus on Extensions View', { exactLabelMatch: true }); - await this.code.waitForTypeInEditor('div.extensions-viewlet[id="workbench.view.extensions"] .monaco-editor textarea', `@id:${id}`); - await this.code.waitForTextContent(`div.part.sidebar div.composite.title h2`, 'Extensions: Marketplace'); - - let retrials = 1; - while (retrials++ < 10) { - try { - return await this.code.waitForElement(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[data-extension-id="${id}"]`, undefined, 100); - } catch (error) { - this.code.logger.log(`Extension '${id}' is not found. Retrying count: ${retrials}`); - await this.commands.runCommand('workbench.extensions.action.refreshExtension'); - } - } - throw new Error(`Extension ${id} is not found`); - } - - async openExtension(id: string): Promise { - await this.searchForExtension(id); - await this.code.waitAndClick(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[data-extension-id="${id}"]`); - } - - async closeExtension(title: string): Promise { - try { - await this.code.waitAndClick(`.tabs-container div.tab[aria-label="Extension: ${title}, preview"] div.tab-actions a.action-label.codicon.codicon-close`); - } catch (e) { - this.code.logger.log(`Extension '${title}' not opened as preview. Trying without 'preview'.`); - await this.code.waitAndClick(`.tabs-container div.tab[aria-label="Extension: ${title}"] div.tab-actions a.action-label.codicon.codicon-close`); - } - } - - async installExtension(id: string, waitUntilEnabled: boolean): Promise { - await this.searchForExtension(id); - await this.code.waitAndClick(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[data-extension-id="${id}"] .extension-list-item .monaco-action-bar .action-item:not(.disabled) .extension-action.install`); - await this.code.waitForElement(`.extension-editor .monaco-action-bar .action-item:not(.disabled) .extension-action.uninstall`); - if (waitUntilEnabled) { - await this.code.waitForElement(`.extension-editor .monaco-action-bar .action-item:not(.disabled) a[aria-label="Disable this extension"]`); - } - } -} - -export async function copyExtension(repoPath: string, extensionsPath: string, extId: string): Promise { - const dest = path.join(extensionsPath, extId); - if (!fs.existsSync(dest)) { - const orig = path.join(repoPath, 'extensions', extId); - - return promisify(ncp)(orig, dest); - } -} diff --git a/test/automation/src/index.ts b/test/automation/src/index.ts index 6aa2a31fb26..b3576710ff2 100644 --- a/test/automation/src/index.ts +++ b/test/automation/src/index.ts @@ -3,29 +3,23 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export * from './activityBar'; +// --- Start Positron --- +// Removed existing exports +// --- End Positron --- export * from './application'; export * from './code'; -export * from './debug'; -export * from './editor'; -export * from './editors'; -export * from './explorer'; -export * from './extensions'; -export * from './keybindings'; +// --- Start Positron --- +// Removed existing exports +// --- End Positron --- export * from './logger'; -export * from './peek'; -export * from './problems'; -export * from './quickinput'; -export * from './quickaccess'; -export * from './scm'; -export * from './search'; -export * from './settings'; -export * from './statusbar'; -export * from './terminal'; -export * from './viewlet'; -export * from './localization'; +// --- Start Positron --- +// Removed existing exports +// --- End Positron --- export * from './workbench'; -export * from './task'; +// --- Start Positron --- +// Removed existing exports +// --- End Positron --- + // --- Start Positron --- export * from './positron/positronConsole'; export * from './positron/positronPopups'; diff --git a/test/automation/src/keybindings.ts b/test/automation/src/keybindings.ts deleted file mode 100644 index 9b7babfac4f..00000000000 --- a/test/automation/src/keybindings.ts +++ /dev/null @@ -1,34 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Code } from './code'; - -const SEARCH_INPUT = '.keybindings-header .settings-search-input input'; - -export class KeybindingsEditor { - - constructor(private code: Code) { } - - async updateKeybinding(command: string, commandName: string | undefined, keybinding: string, keybindingTitle: string): Promise { - if (process.platform === 'darwin') { - await this.code.dispatchKeybinding('cmd+k cmd+s'); - } else { - await this.code.dispatchKeybinding('ctrl+k ctrl+s'); - } - - await this.code.waitForActiveElement(SEARCH_INPUT); - await this.code.waitForSetValue(SEARCH_INPUT, `@command:${command}`); - - const commandTitle = commandName ? `${commandName} (${command})` : command; - await this.code.waitAndClick(`.keybindings-table-container .monaco-list-row .command[aria-label="${commandTitle}"]`); - await this.code.waitForElement(`.keybindings-table-container .monaco-list-row.focused.selected .command[aria-label="${commandTitle}"]`); - await this.code.dispatchKeybinding('enter'); - - await this.code.waitForActiveElement('.defineKeybindingWidget .monaco-inputbox input'); - await this.code.dispatchKeybinding(keybinding); - await this.code.dispatchKeybinding('enter'); - await this.code.waitForElement(`.keybindings-table-container .keybinding-label div[aria-label="${keybindingTitle}"]`); - } -} diff --git a/test/automation/src/localization.ts b/test/automation/src/localization.ts deleted file mode 100644 index 6fcba0e0b34..00000000000 --- a/test/automation/src/localization.ts +++ /dev/null @@ -1,19 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Code } from './code'; -import { ILocalizedStrings, ILocaleInfo } from './driver'; - -export class Localization { - constructor(private code: Code) { } - - async getLocaleInfo(): Promise { - return this.code.getLocaleInfo(); - } - - async getLocalizedStrings(): Promise { - return this.code.getLocalizedStrings(); - } -} diff --git a/test/automation/src/notebook.ts b/test/automation/src/notebook.ts deleted file mode 100644 index dff250027db..00000000000 --- a/test/automation/src/notebook.ts +++ /dev/null @@ -1,99 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Code } from './code'; -import { QuickAccess } from './quickaccess'; -import { QuickInput } from './quickinput'; - -const activeRowSelector = `.notebook-editor .monaco-list-row.focused`; - -export class Notebook { - - constructor( - private readonly quickAccess: QuickAccess, - private readonly quickInput: QuickInput, - private readonly code: Code) { - } - - async openNotebook() { - await this.quickAccess.openFileQuickAccessAndWait('notebook.ipynb', 1); - await this.quickInput.selectQuickInputElement(0); - - await this.code.waitForElement(activeRowSelector); - await this.focusFirstCell(); - } - - async focusNextCell() { - await this.code.dispatchKeybinding('down'); - } - - async focusFirstCell() { - await this.quickAccess.runCommand('notebook.focusTop'); - } - - async editCell() { - await this.code.dispatchKeybinding('enter'); - } - - async stopEditingCell() { - await this.quickAccess.runCommand('notebook.cell.quitEdit'); - } - - async waitForTypeInEditor(text: string): Promise { - const editor = `${activeRowSelector} .monaco-editor`; - - await this.code.waitForElement(editor); - - const textarea = `${editor} textarea`; - await this.code.waitForActiveElement(textarea); - - await this.code.waitForTypeInEditor(textarea, text); - - await this._waitForActiveCellEditorContents(c => c.indexOf(text) > -1); - } - - async waitForActiveCellEditorContents(contents: string): Promise { - return this._waitForActiveCellEditorContents(str => str === contents); - } - - private async _waitForActiveCellEditorContents(accept: (contents: string) => boolean): Promise { - const selector = `${activeRowSelector} .monaco-editor .view-lines`; - return this.code.waitForTextContent(selector, undefined, c => accept(c.replace(/\u00a0/g, ' '))); - } - - async waitForMarkdownContents(markdownSelector: string, text: string): Promise { - const selector = `${activeRowSelector} .markdown ${markdownSelector}`; - await this.code.waitForTextContent(selector, text); - } - - async insertNotebookCell(kind: 'markdown' | 'code'): Promise { - if (kind === 'markdown') { - await this.quickAccess.runCommand('notebook.cell.insertMarkdownCellBelow'); - } else { - await this.quickAccess.runCommand('notebook.cell.insertCodeCellBelow'); - } - } - - async deleteActiveCell(): Promise { - await this.quickAccess.runCommand('notebook.cell.delete'); - } - - async focusInCellOutput(): Promise { - await this.quickAccess.runCommand('notebook.cell.focusInOutput'); - await this.code.waitForActiveElement('webview, .webview'); - } - - async focusOutCellOutput(): Promise { - await this.quickAccess.runCommand('notebook.cell.focusOutOutput'); - } - - async executeActiveCell(): Promise { - await this.quickAccess.runCommand('notebook.cell.execute'); - } - - async executeCellAction(selector: string): Promise { - await this.code.waitAndClick(selector); - } -} diff --git a/test/automation/src/peek.ts b/test/automation/src/peek.ts deleted file mode 100644 index 2c9f00c5659..00000000000 --- a/test/automation/src/peek.ts +++ /dev/null @@ -1,52 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Code } from './code'; - -export class References { - - private static readonly REFERENCES_WIDGET = '.monaco-editor .zone-widget .zone-widget-container.peekview-widget.reference-zone-widget.results-loaded'; - private static readonly REFERENCES_TITLE_FILE_NAME = `${References.REFERENCES_WIDGET} .head .peekview-title .filename`; - private static readonly REFERENCES_TITLE_COUNT = `${References.REFERENCES_WIDGET} .head .peekview-title .meta`; - private static readonly REFERENCES = `${References.REFERENCES_WIDGET} .body .ref-tree.inline .monaco-list-row .highlight`; - - constructor(private code: Code) { } - - async waitUntilOpen(): Promise { - await this.code.waitForElement(References.REFERENCES_WIDGET); - } - - async waitForReferencesCountInTitle(count: number): Promise { - await this.code.waitForTextContent(References.REFERENCES_TITLE_COUNT, undefined, titleCount => { - const matches = titleCount.match(/\d+/); - return matches ? parseInt(matches[0]) === count : false; - }); - } - - async waitForReferencesCount(count: number): Promise { - await this.code.waitForElements(References.REFERENCES, false, result => result && result.length === count); - } - - async waitForFile(file: string): Promise { - await this.code.waitForTextContent(References.REFERENCES_TITLE_FILE_NAME, file); - } - - async close(): Promise { - // Sometimes someone else eats up the `Escape` key - let count = 0; - while (true) { - await this.code.dispatchKeybinding('escape'); - - try { - await this.code.waitForElement(References.REFERENCES_WIDGET, el => !el, 10); - return; - } catch (err) { - if (++count > 5) { - throw err; - } - } - } - } -} diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index 775041be6b1..dbd9e46ea3d 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -19,19 +19,10 @@ export class PlaywrightDriver { private static traceCounter = 1; private static screenShotCounter = 1; - private static readonly vscodeToPlaywrightKey: { [key: string]: string } = { - cmd: 'Meta', - ctrl: 'Control', - shift: 'Shift', - enter: 'Enter', - escape: 'Escape', - right: 'ArrowRight', - up: 'ArrowUp', - down: 'ArrowDown', - left: 'ArrowLeft', - home: 'Home', - esc: 'Escape' - }; + + // --- Start Positron --- + // Removed declaration + // --- End Positron --- constructor( private readonly application: playwright.Browser | playwright.ElectronApplication, @@ -231,80 +222,9 @@ export class PlaywrightDriver { } } - async dispatchKeybinding(keybinding: string) { - const chords = keybinding.split(' '); - for (let i = 0; i < chords.length; i++) { - const chord = chords[i]; - if (i > 0) { - await this.wait(100); - } - - if (keybinding.startsWith('Alt') || keybinding.startsWith('Control') || keybinding.startsWith('Backspace')) { - await this.page.keyboard.press(keybinding); - return; - } - - const keys = chord.split('+'); - const keysDown: string[] = []; - for (let i = 0; i < keys.length; i++) { - if (keys[i] in PlaywrightDriver.vscodeToPlaywrightKey) { - keys[i] = PlaywrightDriver.vscodeToPlaywrightKey[keys[i]]; - } - await this.page.keyboard.down(keys[i]); - keysDown.push(keys[i]); - } - while (keysDown.length > 0) { - await this.page.keyboard.up(keysDown.pop()!); - } - } - - await this.wait(100); - } - - async click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined) { - const { x, y } = await this.getElementXY(selector, xoffset, yoffset); - await this.page.mouse.click(x + (xoffset ? xoffset : 0), y + (yoffset ? yoffset : 0)); - } - - async setValue(selector: string, text: string) { - return this.page.evaluate(([driver, selector, text]) => driver.setValue(selector, text), [await this.getDriverHandle(), selector, text] as const); - } - - async getTitle() { - return this.page.title(); - } - - async isActiveElement(selector: string) { - return this.page.evaluate(([driver, selector]) => driver.isActiveElement(selector), [await this.getDriverHandle(), selector] as const); - } - - async getElements(selector: string, recursive: boolean = false) { - return this.page.evaluate(([driver, selector, recursive]) => driver.getElements(selector, recursive), [await this.getDriverHandle(), selector, recursive] as const); - } - - async getElementXY(selector: string, xoffset?: number, yoffset?: number) { - return this.page.evaluate(([driver, selector, xoffset, yoffset]) => driver.getElementXY(selector, xoffset, yoffset), [await this.getDriverHandle(), selector, xoffset, yoffset] as const); - } - - async typeInEditor(selector: string, text: string) { - return this.page.evaluate(([driver, selector, text]) => driver.typeInEditor(selector, text), [await this.getDriverHandle(), selector, text] as const); - } - - async getTerminalBuffer(selector: string) { - return this.page.evaluate(([driver, selector]) => driver.getTerminalBuffer(selector), [await this.getDriverHandle(), selector] as const); - } - - async writeInTerminal(selector: string, text: string) { - return this.page.evaluate(([driver, selector, text]) => driver.writeInTerminal(selector, text), [await this.getDriverHandle(), selector, text] as const); - } - - async getLocaleInfo() { - return this.evaluateWithDriver(([driver]) => driver.getLocaleInfo()); - } - - async getLocalizedStrings() { - return this.evaluateWithDriver(([driver]) => driver.getLocalizedStrings()); - } + // --- Start Positron --- + // Removed functions + // --- End Positron --- async getLogs() { return this.page.evaluate(([driver]) => driver.getLogs(), [await this.getDriverHandle()] as const); diff --git a/test/automation/src/positron/positronTerminal.ts b/test/automation/src/positron/positronTerminal.ts index 9854ff1f355..733cbf8115d 100644 --- a/test/automation/src/positron/positronTerminal.ts +++ b/test/automation/src/positron/positronTerminal.ts @@ -46,7 +46,7 @@ export class PositronTerminal { } private async _waitForTerminal(): Promise { - await this.code.waitForElement('.terminal.xterm.focus'); + await expect(this.code.driver.page.locator('.terminal.xterm.focus')).toBeVisible(); await this.waitForTerminalLines(); } diff --git a/test/automation/src/problems.ts b/test/automation/src/problems.ts deleted file mode 100644 index 026ccf4b061..00000000000 --- a/test/automation/src/problems.ts +++ /dev/null @@ -1,43 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Code } from './code'; -import { QuickAccess } from './quickaccess'; - -export const enum ProblemSeverity { - WARNING = 0, - ERROR = 1 -} - -export class Problems { - - static PROBLEMS_VIEW_SELECTOR = '.panel .markers-panel'; - - constructor(private code: Code, private quickAccess: QuickAccess) { } - - async showProblemsView(): Promise { - await this.quickAccess.runCommand('workbench.panel.markers.view.focus'); - await this.waitForProblemsView(); - } - - async hideProblemsView(): Promise { - await this.quickAccess.runCommand('workbench.actions.view.problems'); - await this.code.waitForElement(Problems.PROBLEMS_VIEW_SELECTOR, el => !el); - } - - async waitForProblemsView(): Promise { - await this.code.waitForElement(Problems.PROBLEMS_VIEW_SELECTOR); - } - - static getSelectorInProblemsView(problemType: ProblemSeverity): string { - const selector = problemType === ProblemSeverity.WARNING ? 'codicon-warning' : 'codicon-error'; - return `div[id="workbench.panel.markers"] .monaco-tl-contents .marker-icon .${selector}`; - } - - static getSelectorInEditor(problemType: ProblemSeverity): string { - const selector = problemType === ProblemSeverity.WARNING ? 'squiggly-warning' : 'squiggly-error'; - return `.view-overlays .cdr.${selector}`; - } -} diff --git a/test/automation/src/quickaccess.ts b/test/automation/src/quickaccess.ts deleted file mode 100644 index 5d94a96f1c4..00000000000 --- a/test/automation/src/quickaccess.ts +++ /dev/null @@ -1,243 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Editors } from './editors'; -import { Code } from './code'; -import { QuickInput } from './quickinput'; -import { basename, isAbsolute } from 'path'; - -enum QuickAccessKind { - Files = 1, - Commands, - Symbols -} - -export class QuickAccess { - - constructor(private code: Code, private editors: Editors, private quickInput: QuickInput) { } - - async openFileQuickAccessAndWait(searchValue: string, expectedFirstElementNameOrExpectedResultCount: string | number): Promise { - - // make sure the file quick access is not "polluted" - // with entries from the editor history when opening - await this.runCommand('workbench.action.clearEditorHistory'); - - const PollingStrategy = { - Stop: true, - Continue: false - }; - - let retries = 0; - let success = false; - - while (++retries < 10) { - let retry = false; - - try { - await this.openQuickAccessWithRetry(QuickAccessKind.Files, searchValue); - await this.quickInput.waitForQuickInputElements(elementNames => { - this.code.logger.log('QuickAccess: resulting elements: ', elementNames); - - // Quick access seems to be still running -> retry - if (elementNames.length === 0) { - this.code.logger.log('QuickAccess: file search returned 0 elements, will continue polling...'); - - return PollingStrategy.Continue; - } - - // Quick access does not seem healthy/ready -> retry - const firstElementName = elementNames[0]; - if (firstElementName === 'No matching results') { - this.code.logger.log(`QuickAccess: file search returned "No matching results", will retry...`); - - retry = true; - - return PollingStrategy.Stop; - } - - // Expected: number of results - if (typeof expectedFirstElementNameOrExpectedResultCount === 'number') { - if (elementNames.length === expectedFirstElementNameOrExpectedResultCount) { - success = true; - - return PollingStrategy.Stop; - } - - this.code.logger.log(`QuickAccess: file search returned ${elementNames.length} results but was expecting ${expectedFirstElementNameOrExpectedResultCount}, will retry...`); - - retry = true; - - return PollingStrategy.Stop; - } - - // Expected: string - else { - if (firstElementName === expectedFirstElementNameOrExpectedResultCount) { - success = true; - - return PollingStrategy.Stop; - } - - this.code.logger.log(`QuickAccess: file search returned ${firstElementName} as first result but was expecting ${expectedFirstElementNameOrExpectedResultCount}, will retry...`); - - retry = true; - - return PollingStrategy.Stop; - } - }); - } catch (error) { - this.code.logger.log(`QuickAccess: file search waitForQuickInputElements threw an error ${error}, will retry...`); - - retry = true; - } - - if (!retry) { - break; - } - - await this.quickInput.closeQuickInput(); - } - - if (!success) { - if (typeof expectedFirstElementNameOrExpectedResultCount === 'string') { - throw new Error(`Quick open file search was unable to find '${expectedFirstElementNameOrExpectedResultCount}' after 10 attempts, giving up.`); - } else { - throw new Error(`Quick open file search was unable to find ${expectedFirstElementNameOrExpectedResultCount} result items after 10 attempts, giving up.`); - } - } - } - - async openFile(path: string): Promise { - if (!isAbsolute(path)) { - // we require absolute paths to get a single - // result back that is unique and avoid hitting - // the search process to reduce chances of - // search needing longer. - throw new Error('QuickAccess.openFile requires an absolute path'); - } - - const fileName = basename(path); - - // quick access shows files with the basename of the path - await this.openFileQuickAccessAndWait(path, basename(path)); - - // open first element - await this.quickInput.selectQuickInputElement(0); - - // wait for editor being focused - await this.editors.waitForActiveTab(fileName); - await this.editors.selectTab(fileName); - } - - private async openQuickAccessWithRetry(kind: QuickAccessKind, value?: string): Promise { - let retries = 0; - - // Other parts of code might steal focus away from quickinput :( - while (retries < 5) { - - // Open via keybinding - switch (kind) { - case QuickAccessKind.Files: - await this.code.dispatchKeybinding(process.platform === 'darwin' ? 'cmd+p' : 'ctrl+p'); - break; - case QuickAccessKind.Symbols: - await this.code.dispatchKeybinding(process.platform === 'darwin' ? 'cmd+shift+o' : 'ctrl+shift+o'); - break; - case QuickAccessKind.Commands: - await this.code.dispatchKeybinding(process.platform === 'darwin' ? 'cmd+shift+p' : 'ctrl+shift+p'); - break; - } - - // Await for quick input widget opened - try { - await this.quickInput.waitForQuickInputOpened(10); - break; - } catch (err) { - if (++retries > 5) { - throw new Error(`QuickAccess.openQuickAccessWithRetry(kind: ${kind}) failed: ${err}`); - } - - // Retry - await this.code.dispatchKeybinding('escape'); - } - } - - // Type value if any - if (value) { - await this.quickInput.type(value); - } - } - - async runCommand(commandId: string, options?: { keepOpen?: boolean; exactLabelMatch?: boolean }): Promise { - const keepOpen = options?.keepOpen; - const exactLabelMatch = options?.exactLabelMatch; - - const openCommandPalletteAndTypeCommand = async (): Promise => { - // open commands picker - await this.openQuickAccessWithRetry(QuickAccessKind.Commands, `>${commandId}`); - - // wait for best choice to be focused - await this.quickInput.waitForQuickInputElementFocused(); - - // Retry for as long as the command not found - const text = await this.quickInput.waitForQuickInputElementText(); - - if (text === 'No matching commands' || (exactLabelMatch && text !== commandId)) { - return false; - } - - return true; - }; - - let hasCommandFound = await openCommandPalletteAndTypeCommand(); - - if (!hasCommandFound) { - - this.code.logger.log(`QuickAccess: No matching commands, will retry...`); - await this.quickInput.closeQuickInput(); - - let retries = 0; - while (++retries < 5) { - hasCommandFound = await openCommandPalletteAndTypeCommand(); - if (hasCommandFound) { - break; - } else { - this.code.logger.log(`QuickAccess: No matching commands, will retry...`); - await this.quickInput.closeQuickInput(); - await this.code.wait(1000); - } - } - - if (!hasCommandFound) { - throw new Error(`QuickAccess.runCommand(commandId: ${commandId}) failed to find command.`); - } - } - - // wait and click on best choice - await this.quickInput.selectQuickInputElement(0, keepOpen); - } - - async openQuickOutline(): Promise { - let retries = 0; - - while (++retries < 10) { - - // open quick outline via keybinding - await this.openQuickAccessWithRetry(QuickAccessKind.Symbols); - - const text = await this.quickInput.waitForQuickInputElementText(); - - // Retry for as long as no symbols are found - if (text === 'No symbol information for the file') { - this.code.logger.log(`QuickAccess: openQuickOutline indicated 'No symbol information for the file', will retry...`); - - // close and retry - await this.quickInput.closeQuickInput(); - - continue; - } - } - } -} diff --git a/test/automation/src/quickinput.ts b/test/automation/src/quickinput.ts deleted file mode 100644 index 65eecc7bccc..00000000000 --- a/test/automation/src/quickinput.ts +++ /dev/null @@ -1,72 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Code } from './code'; - -export class QuickInput { - - private static QUICK_INPUT = '.quick-input-widget'; - private static QUICK_INPUT_INPUT = `${QuickInput.QUICK_INPUT} .quick-input-box input`; - private static QUICK_INPUT_ROW = `${QuickInput.QUICK_INPUT} .quick-input-list .monaco-list-row`; - private static QUICK_INPUT_FOCUSED_ELEMENT = `${QuickInput.QUICK_INPUT_ROW}.focused .monaco-highlighted-label`; - // Note: this only grabs the label and not the description or detail - private static QUICK_INPUT_ENTRY_LABEL = `${QuickInput.QUICK_INPUT_ROW} .quick-input-list-row > .monaco-icon-label .label-name`; - private static QUICK_INPUT_ENTRY_LABEL_SPAN = `${QuickInput.QUICK_INPUT_ROW} .monaco-highlighted-label`; - - // --- Start Positron --- - private static QUICKINPUT_OK_BUTTON = '.quick-input-widget .quick-input-action a:has-text("OK")'; - // --- End Positron --- - - constructor(private code: Code) { } - - async waitForQuickInputOpened(retryCount?: number): Promise { - await this.code.waitForActiveElement(QuickInput.QUICK_INPUT_INPUT, retryCount); - } - - async type(value: string): Promise { - await this.code.waitForSetValue(QuickInput.QUICK_INPUT_INPUT, value); - } - - async waitForQuickInputElementFocused(): Promise { - await this.code.waitForTextContent(QuickInput.QUICK_INPUT_FOCUSED_ELEMENT); - } - - async waitForQuickInputElementText(): Promise { - return this.code.waitForTextContent(QuickInput.QUICK_INPUT_ENTRY_LABEL_SPAN); - } - - async closeQuickInput(): Promise { - await this.code.dispatchKeybinding('escape'); - await this.waitForQuickInputClosed(); - } - - async waitForQuickInputElements(accept: (names: string[]) => boolean): Promise { - await this.code.waitForElements(QuickInput.QUICK_INPUT_ENTRY_LABEL, false, els => accept(els.map(e => e.textContent))); - } - - async waitForQuickInputClosed(): Promise { - await this.code.waitForElement(QuickInput.QUICK_INPUT, r => !!r && r.attributes.style.indexOf('display: none;') !== -1); - } - - async selectQuickInputElement(index: number, keepOpen?: boolean): Promise { - await this.waitForQuickInputOpened(); - for (let from = 0; from < index; from++) { - await this.code.dispatchKeybinding('down'); - } - await this.code.dispatchKeybinding('enter'); - if (!keepOpen) { - await this.waitForQuickInputClosed(); - } - } - // --- Start Positron --- - async selectQuickInputElementContaining(text: string): Promise { - await this.code.driver.page.locator(`${QuickInput.QUICK_INPUT_ROW}[aria-label*="${text}"]`).first().click({ timeout: 10000 }); - } - - async clickOkOnQuickInput(): Promise { - await this.code.driver.page.locator(QuickInput.QUICKINPUT_OK_BUTTON).click(); - } - // --- End Positron --- -} diff --git a/test/automation/src/scm.ts b/test/automation/src/scm.ts deleted file mode 100644 index 9f950f2b16a..00000000000 --- a/test/automation/src/scm.ts +++ /dev/null @@ -1,79 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Viewlet } from './viewlet'; -import { IElement } from './driver'; -import { findElement, findElements, Code } from './code'; - -const VIEWLET = 'div[id="workbench.view.scm"]'; -const SCM_INPUT = `${VIEWLET} .scm-editor textarea`; -const SCM_RESOURCE = `${VIEWLET} .monaco-list-row .resource`; -const REFRESH_COMMAND = `div[id="workbench.parts.sidebar"] .actions-container a.action-label[aria-label="Refresh"]`; -const COMMIT_COMMAND = `div[id="workbench.parts.sidebar"] .actions-container a.action-label[aria-label="Commit"]`; -const SCM_RESOURCE_CLICK = (name: string) => `${SCM_RESOURCE} .monaco-icon-label[aria-label*="${name}"] .label-name`; -const SCM_RESOURCE_ACTION_CLICK = (name: string, actionName: string) => `${SCM_RESOURCE} .monaco-icon-label[aria-label*="${name}"] .actions .action-label[aria-label="${actionName}"]`; - -interface Change { - name: string; - type: string; - actions: string[]; -} - -function toChange(element: IElement): Change { - const name = findElement(element, e => /\blabel-name\b/.test(e.className))!; - const type = element.attributes['data-tooltip'] || ''; - - const actionElementList = findElements(element, e => /\baction-label\b/.test(e.className)); - const actions = actionElementList.map(e => e.attributes['title']); - - return { - name: name.textContent || '', - type, - actions - }; -} - - -export class SCM extends Viewlet { - - constructor(code: Code) { - super(code); - } - - async openSCMViewlet(): Promise { - await this.code.dispatchKeybinding('ctrl+shift+g'); - await this.code.waitForElement(SCM_INPUT); - } - - async waitForChange(name: string, type?: string): Promise { - const func = (change: Change) => change.name === name && (!type || change.type === type); - await this.code.waitForElements(SCM_RESOURCE, true, elements => elements.some(e => func(toChange(e)))); - } - - async refreshSCMViewlet(): Promise { - await this.code.waitAndClick(REFRESH_COMMAND); - } - - async openChange(name: string): Promise { - await this.code.waitAndClick(SCM_RESOURCE_CLICK(name)); - } - - async stage(name: string): Promise { - await this.code.waitAndClick(SCM_RESOURCE_ACTION_CLICK(name, 'Stage Changes')); - await this.waitForChange(name, 'Index Modified'); - } - - async unstage(name: string): Promise { - await this.code.waitAndClick(SCM_RESOURCE_ACTION_CLICK(name, 'Unstage Changes')); - await this.waitForChange(name, 'Modified'); - } - - async commit(message: string): Promise { - await this.code.waitAndClick(SCM_INPUT); - await this.code.waitForActiveElement(SCM_INPUT); - await this.code.waitForSetValue(SCM_INPUT, message); - await this.code.waitAndClick(COMMIT_COMMAND); - } -} diff --git a/test/automation/src/search.ts b/test/automation/src/search.ts deleted file mode 100644 index 5cf6018b72a..00000000000 --- a/test/automation/src/search.ts +++ /dev/null @@ -1,161 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Viewlet } from './viewlet'; -import { Code } from './code'; - -const VIEWLET = '.search-view'; -const INPUT = `${VIEWLET} .search-widget .search-container .monaco-inputbox textarea`; -const INCLUDE_INPUT = `${VIEWLET} .query-details .file-types.includes .monaco-inputbox input`; -const FILE_MATCH = (filename: string) => `${VIEWLET} .results .filematch[data-resource$="${filename}"]`; - -async function retry(setup: () => Promise, attempt: () => Promise) { - let count = 0; - while (true) { - await setup(); - - try { - await attempt(); - return; - } catch (err) { - if (++count > 5) { - throw err; - } - } - } -} - -export class Search extends Viewlet { - - constructor(code: Code) { - super(code); - } - - async clearSearchResults(): Promise { - await retry( - () => this.code.waitAndClick(`.sidebar .title-actions .codicon-search-clear-results`), - () => this.waitForNoResultText(10)); - } - - async openSearchViewlet(): Promise { - if (process.platform === 'darwin') { - await this.code.dispatchKeybinding('cmd+shift+f'); - } else { - await this.code.dispatchKeybinding('ctrl+shift+f'); - } - - await this.waitForInputFocus(INPUT); - } - - async getSearchTooltip(): Promise { - const icon = await this.code.waitForElement(`.activitybar .action-label.codicon.codicon-search-view-icon`, (el) => !!el?.attributes?.['title']); - return icon.attributes['title']; - } - - async searchFor(text: string): Promise { - await this.clearSearchResults(); - await this.waitForInputFocus(INPUT); - await this.code.waitForSetValue(INPUT, text); - await this.submitSearch(); - } - - async hasActivityBarMoved() { - await this.code.waitForElement('.activitybar'); - - const elementBoundingBox = await this.code.driver.getElementXY('.activitybar'); - return elementBoundingBox !== null && elementBoundingBox.x === 48 && elementBoundingBox.y === 375; - } - - async waitForPageUp(): Promise { - await this.code.dispatchKeybinding('PageUp'); - } - - async waitForPageDown(): Promise { - await this.code.dispatchKeybinding('PageDown'); - } - - async submitSearch(): Promise { - await this.waitForInputFocus(INPUT); - - await this.code.dispatchKeybinding('enter'); - await this.code.waitForElement(`${VIEWLET} .messages`); - } - - async setFilesToIncludeText(text: string): Promise { - await this.waitForInputFocus(INCLUDE_INPUT); - await this.code.waitForSetValue(INCLUDE_INPUT, text || ''); - } - - async showQueryDetails(): Promise { - await this.code.waitAndClick(`${VIEWLET} .query-details .more`); - } - - async hideQueryDetails(): Promise { - await this.code.waitAndClick(`${VIEWLET} .query-details.more .more`); - } - - async removeFileMatch(filename: string, expectedText: string): Promise { - const fileMatch = FILE_MATCH(filename); - - // Retry this because the click can fail if the search tree is rerendered at the same time - await retry( - async () => { - await this.code.waitAndClick(fileMatch); - await this.code.waitAndClick(`${fileMatch} .action-label.codicon-search-remove`); - }, - async () => this.waitForResultText(expectedText, 10)); - } - - async expandReplace(): Promise { - await this.code.waitAndClick(`${VIEWLET} .search-widget .monaco-button.toggle-replace-button.codicon-search-hide-replace`); - } - - async collapseReplace(): Promise { - await this.code.waitAndClick(`${VIEWLET} .search-widget .monaco-button.toggle-replace-button.codicon-search-show-replace`); - } - - async setReplaceText(text: string): Promise { - await this.code.waitForSetValue(`${VIEWLET} .search-widget .replace-container .monaco-inputbox textarea[aria-label="Replace"]`, text); - } - - async replaceFileMatch(filename: string, expectedText: string): Promise { - const fileMatch = FILE_MATCH(filename); - - // Retry this because the click can fail if the search tree is rerendered at the same time - await retry( - async () => { - await this.code.waitAndClick(fileMatch); - await this.code.waitAndClick(`${fileMatch} .action-label.codicon.codicon-search-replace-all`); - }, - () => this.waitForResultText(expectedText, 10)); - } - - async waitForResultText(text: string, retryCount?: number): Promise { - // The label can end with " - " depending on whether the search editor is enabled - await this.code.waitForTextContent(`${VIEWLET} .messages .message`, undefined, result => result.startsWith(text), retryCount); - } - - async waitForNoResultText(retryCount?: number): Promise { - await this.code.waitForTextContent(`${VIEWLET} .messages`, undefined, text => text === '' || text.startsWith('Search was canceled before any results could be found'), retryCount); - } - - private async waitForInputFocus(selector: string): Promise { - let retries = 0; - - // other parts of code might steal focus away from input boxes :( - while (retries < 5) { - await this.code.waitAndClick(INPUT, 2, 2); - - try { - await this.code.waitForActiveElement(INPUT, 10); - break; - } catch (err) { - if (++retries > 5) { - throw err; - } - } - } - } -} diff --git a/test/automation/src/settings.ts b/test/automation/src/settings.ts deleted file mode 100644 index 68401eb0eda..00000000000 --- a/test/automation/src/settings.ts +++ /dev/null @@ -1,77 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Editor } from './editor'; -import { Editors } from './editors'; -import { Code } from './code'; -import { QuickAccess } from './quickaccess'; - -const SEARCH_BOX = '.settings-editor .suggest-input-container .monaco-editor textarea'; - -export class SettingsEditor { - constructor(private code: Code, private editors: Editors, private editor: Editor, private quickaccess: QuickAccess) { } - - /** - * Write a single setting key value pair. - * - * Warning: You may need to set `editor.wordWrap` to `"on"` if this is called with a really long - * setting. - */ - async addUserSetting(setting: string, value: string): Promise { - await this.openUserSettingsFile(); - - await this.code.dispatchKeybinding('right'); - await this.editor.waitForTypeInEditor('settings.json', `"${setting}": ${value},`); - await this.editors.saveOpenedFile(); - } - - /** - * Write several settings faster than multiple calls to {@link addUserSetting}. - * - * Warning: You will likely also need to set `editor.wordWrap` to `"on"` if `addUserSetting` is - * called after this in the test. - */ - async addUserSettings(settings: [key: string, value: string][]): Promise { - await this.openUserSettingsFile(); - - await this.code.dispatchKeybinding('right'); - await this.editor.waitForTypeInEditor('settings.json', settings.map(v => `"${v[0]}": ${v[1]},`).join('')); - await this.editors.saveOpenedFile(); - } - - async clearUserSettings(): Promise { - await this.openUserSettingsFile(); - await this.quickaccess.runCommand('editor.action.selectAll'); - await this.code.dispatchKeybinding('Delete'); - await this.editor.waitForTypeInEditor('settings.json', `{`); // will auto close } - await this.editors.saveOpenedFile(); - await this.quickaccess.runCommand('workbench.action.closeActiveEditor'); - } - - async openUserSettingsFile(): Promise { - await this.quickaccess.runCommand('workbench.action.openSettingsJson'); - await this.editor.waitForEditorFocus('settings.json', 1); - } - - async openUserSettingsUI(): Promise { - await this.quickaccess.runCommand('workbench.action.openSettings2'); - await this.code.waitForActiveElement(SEARCH_BOX); - } - - async searchSettingsUI(query: string): Promise { - await this.openUserSettingsUI(); - - await this.code.waitAndClick(SEARCH_BOX); - if (process.platform === 'darwin') { - await this.code.dispatchKeybinding('cmd+a'); - } else { - await this.code.dispatchKeybinding('ctrl+a'); - } - await this.code.dispatchKeybinding('Delete'); - await this.code.waitForElements('.settings-editor .settings-count-widget', false, results => !results || (results?.length === 1 && !results[0].textContent)); - await this.code.waitForTypeInEditor('.settings-editor .suggest-input-container .monaco-editor textarea', query); - await this.code.waitForElements('.settings-editor .settings-count-widget', false, results => results?.length === 1 && results[0].textContent.includes('Found')); - } -} diff --git a/test/automation/src/statusbar.ts b/test/automation/src/statusbar.ts deleted file mode 100644 index 423a7585c04..00000000000 --- a/test/automation/src/statusbar.ts +++ /dev/null @@ -1,63 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Code } from './code'; - -export const enum StatusBarElement { - BRANCH_STATUS = 0, - SYNC_STATUS = 1, - PROBLEMS_STATUS = 2, - SELECTION_STATUS = 3, - INDENTATION_STATUS = 4, - ENCODING_STATUS = 5, - EOL_STATUS = 6, - LANGUAGE_STATUS = 7 -} - -export class StatusBar { - - private readonly mainSelector = 'footer[id="workbench.parts.statusbar"]'; - - constructor(private code: Code) { } - - async waitForStatusbarElement(element: StatusBarElement): Promise { - await this.code.waitForElement(this.getSelector(element)); - } - - async clickOn(element: StatusBarElement): Promise { - await this.code.waitAndClick(this.getSelector(element)); - } - - async waitForEOL(eol: string): Promise { - return this.code.waitForTextContent(this.getSelector(StatusBarElement.EOL_STATUS), eol); - } - - async waitForStatusbarText(title: string, text: string): Promise { - await this.code.waitForTextContent(`${this.mainSelector} .statusbar-item[aria-label="${title}"]`, text); - } - - private getSelector(element: StatusBarElement): string { - switch (element) { - case StatusBarElement.BRANCH_STATUS: - return `.statusbar-item[id^="status.scm."] .codicon.codicon-git-branch`; - case StatusBarElement.SYNC_STATUS: - return `.statusbar-item[id^="status.scm."] .codicon.codicon-sync`; - case StatusBarElement.PROBLEMS_STATUS: - return `.statusbar-item[id="status.problems"]`; - case StatusBarElement.SELECTION_STATUS: - return `.statusbar-item[id="status.editor.selection"]`; - case StatusBarElement.INDENTATION_STATUS: - return `.statusbar-item[id="status.editor.indentation"]`; - case StatusBarElement.ENCODING_STATUS: - return `.statusbar-item[id="status.editor.encoding"]`; - case StatusBarElement.EOL_STATUS: - return `.statusbar-item[id="status.editor.eol"]`; - case StatusBarElement.LANGUAGE_STATUS: - return `.statusbar-item[id="status.editor.mode"]`; - default: - throw new Error(element); - } - } -} diff --git a/test/automation/src/task.ts b/test/automation/src/task.ts deleted file mode 100644 index ab95e9947d5..00000000000 --- a/test/automation/src/task.ts +++ /dev/null @@ -1,86 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Editor } from './editor'; -import { Code } from './code'; -import { QuickAccess } from './quickaccess'; -import { Editors } from './editors'; -import { QuickInput } from './quickinput'; -import { Terminal } from './terminal'; - -interface ITaskConfigurationProperties { - label?: string; - type?: string; - command?: string; - identifier?: string; - group?: string; - isBackground?: boolean; - promptOnClose?: boolean; - icon?: { id?: string; color?: string }; - hide?: boolean; -} - -export enum TaskCommandId { - TerminalRename = 'workbench.action.terminal.rename' -} - -export class Task { - - constructor(private code: Code, private editor: Editor, private editors: Editors, private quickaccess: QuickAccess, private quickinput: QuickInput, private terminal: Terminal) { - - } - - async assertTasks(filter: string, expected: ITaskConfigurationProperties[], type: 'run' | 'configure') { - await this.code.dispatchKeybinding('right'); - await this.editors.saveOpenedFile(); - type === 'run' ? await this.quickaccess.runCommand('workbench.action.tasks.runTask', { keepOpen: true }) : await this.quickaccess.runCommand('workbench.action.tasks.configureTask', { keepOpen: true }); - if (expected.length === 0) { - await this.quickinput.waitForQuickInputElements(e => e.length > 1 && e.every(label => label.trim() !== filter.trim())); - } else { - await this.quickinput.waitForQuickInputElements(e => e.length > 1 && e.some(label => label.trim() === filter.trim())); - } - if (expected.length > 0 && !expected[0].hide) { - // select the expected task - await this.quickinput.selectQuickInputElement(0, true); - // Continue without scanning the output - await this.quickinput.selectQuickInputElement(0); - if (expected[0].icon) { - await this.terminal.assertSingleTab({ color: expected[0].icon.color, icon: expected[0].icon.id || 'tools' }); - } - } - await this.quickinput.closeQuickInput(); - } - - async configureTask(properties: ITaskConfigurationProperties) { - await this.quickaccess.openFileQuickAccessAndWait('tasks.json', 'tasks.json'); - await this.quickinput.selectQuickInputElement(0); - await this.quickaccess.runCommand('editor.action.selectAll'); - await this.code.dispatchKeybinding('Delete'); - const taskStringLines: string[] = [ - '{', // Brackets auto close - '"version": "2.0.0",', - '"tasks": [{' // Brackets auto close - ]; - for (let [key, value] of Object.entries(properties)) { - if (typeof value === 'object') { - value = JSON.stringify(value); - } else if (typeof value === 'boolean') { - value = value; - } else if (typeof value === 'string') { - value = `"${value}"`; - } else { - throw new Error('Unsupported task property value type'); - } - taskStringLines.push(`"${key}": ${value},`); - } - for (const [i, line] of taskStringLines.entries()) { - await this.editor.waitForTypeInEditor('tasks.json', `${line}`); - if (i !== taskStringLines.length - 1) { - await this.code.dispatchKeybinding('Enter'); - } - } - await this.editors.saveOpenedFile(); - } -} diff --git a/test/automation/src/terminal.ts b/test/automation/src/terminal.ts deleted file mode 100644 index 0389a330544..00000000000 --- a/test/automation/src/terminal.ts +++ /dev/null @@ -1,332 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { QuickInput } from './quickinput'; -import { Code } from './code'; -import { QuickAccess } from './quickaccess'; -import { IElement } from './driver'; - -export enum Selector { - TerminalView = `#terminal`, - CommandDecorationPlaceholder = `.terminal-command-decoration.codicon-terminal-decoration-incomplete`, - CommandDecorationSuccess = `.terminal-command-decoration.codicon-terminal-decoration-success`, - CommandDecorationError = `.terminal-command-decoration.codicon-terminal-decoration-error`, - Xterm = `#terminal .terminal-wrapper`, - XtermEditor = `.editor-instance .terminal-wrapper`, - TabsEntry = '.terminal-tabs-entry', - Description = '.label-description', - 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', - Hide = '.hide' -} - -/** - * 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', - WriteDataToTerminal = 'workbench.action.terminal.writeDataToTerminal' -} - -/** - * 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; - description?: string; - icon?: string; - color?: string; -} -type TerminalGroup = TerminalLabel[]; - -interface ICommandDecorationCounts { - placeholder: number; - success: number; - error: number; -} - -export class Terminal { - - constructor(private code: Code, private quickaccess: QuickAccess, private quickinput: QuickInput) { } - - async runCommand(commandId: TerminalCommandId, expectedLocation?: 'editor' | 'panel'): Promise { - const keepOpen = commandId === TerminalCommandId.Join; - await this.quickaccess.runCommand(commandId, { keepOpen }); - if (keepOpen) { - await this.code.dispatchKeybinding('enter'); - await this.quickinput.waitForQuickInputClosed(); - } - switch (commandId) { - case TerminalCommandId.Show: - case TerminalCommandId.CreateNewEditor: - case TerminalCommandId.CreateNew: - case TerminalCommandId.NewWithProfile: - await this._waitForTerminal(expectedLocation === 'editor' || commandId === TerminalCommandId.CreateNewEditor ? 'editor' : 'panel'); - break; - case TerminalCommandId.KillAll: - // HACK: Attempt to kill all terminals to clean things up, this is known to be flaky - // but the reason why isn't known. This is typically called in the after each hook, - // Since it's not actually required that all terminals are killed just continue on - // after 2 seconds. - await Promise.race([ - this.code.waitForElements(Selector.Xterm, true, e => e.length === 0), - this.code.wait(2000) - ]); - break; - } - } - - async runCommandWithValue(commandId: TerminalCommandIdWithValue, value?: string, altKey?: boolean): Promise { - const keepOpen = !!value || commandId === TerminalCommandIdWithValue.NewWithProfile || commandId === TerminalCommandIdWithValue.Rename || (commandId === TerminalCommandIdWithValue.SelectDefaultProfile && value !== 'PowerShell'); - await this.quickaccess.runCommand(commandId, { keepOpen }); - // Running the command should hide the quick input in the following frame, this next wait - // ensures that the quick input is opened again before proceeding to avoid a race condition - // where the enter keybinding below would close the quick input if it's triggered before the - // new quick input shows. - await this.quickinput.waitForQuickInputOpened(); - if (value) { - await this.quickinput.type(value); - } else if (commandId === TerminalCommandIdWithValue.Rename) { - // Reset - await this.code.dispatchKeybinding('Backspace'); - } - await this.code.wait(100); - await this.code.dispatchKeybinding(altKey ? 'Alt+Enter' : 'enter'); - await this.quickinput.waitForQuickInputClosed(); - if (commandId === TerminalCommandIdWithValue.NewWithProfile) { - await this._waitForTerminal(); - } - } - - async runCommandInTerminal(commandText: string, skipEnter?: boolean): Promise { - await this.code.writeInTerminal(Selector.Xterm, commandText); - if (!skipEnter) { - await this.code.dispatchKeybinding('enter'); - } - } - - /** - * Creates a terminal using the new terminal command. - * @param expectedLocation The location to check the terminal for, defaults to panel. - */ - async createTerminal(expectedLocation?: 'editor' | 'panel'): Promise { - await this.runCommand(TerminalCommandId.CreateNew, expectedLocation); - await this._waitForTerminal(expectedLocation); - } - - /** - * Creates an empty terminal by opening a regular terminal and resetting its state such that it - * essentially acts like an Pseudoterminal extension API-based terminal. This can then be paired - * with `TerminalCommandIdWithValue.WriteDataToTerminal` to make more reliable tests. - */ - async createEmptyTerminal(expectedLocation?: 'editor' | 'panel'): Promise { - await this.createTerminal(expectedLocation); - - // Run a command to ensure the shell has started, this is used to ensure the shell's data - // does not leak into the "empty terminal" - await this.runCommandInTerminal('echo "initialized"'); - await this.waitForTerminalText(buffer => buffer.some(line => line.startsWith('initialized'))); - - // Erase all content and reset cursor to top - await this.runCommandWithValue(TerminalCommandIdWithValue.WriteDataToTerminal, `${csi('2J')}${csi('H')}`); - - // Force windows pty mode off; assume all sequences are rendered in correct position - if (process.platform === 'win32') { - await this.runCommandWithValue(TerminalCommandIdWithValue.WriteDataToTerminal, `${vsc('P;IsWindows=False')}`); - } - } - - async assertEditorGroupCount(count: number): Promise { - await this.code.waitForElements(Selector.EditorGroups, true, editorGroups => editorGroups && editorGroups.length === count); - } - - async assertSingleTab(label: TerminalLabel, editor?: boolean): Promise { - // --- Start Positron --- - let regex; - // --- End Positron --- - if (label.name && label.description) { - regex = new RegExp(label.name + ' - ' + label.description); - } else if (label.name) { - regex = new RegExp(label.name); - } - await this.assertTabExpected(editor ? Selector.EditorTab : Selector.SingleTab, undefined, regex, 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++) { - const terminalsInGroup = expectedGroups[groupIndex].length; - let indexInGroup = 0; - const isSplit = terminalsInGroup > 1; - while (indexInGroup < terminalsInGroup) { - const 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, instance.description); - indexInGroup++; - index++; - } - } - } - } - - async getTerminalGroups(): Promise { - const tabCount = (await this.code.waitForElements(Selector.Tabs, true)).length; - const groups: TerminalGroup[] = []; - for (let i = 0; i < tabCount; i++) { - const title = await this.code.waitForElement(`${Selector.Tabs}[data-index="${i}"] ${Selector.TabsEntry}`, e => e?.textContent?.length ? e?.textContent?.length > 1 : false); - const description: IElement | undefined = await this.code.waitForElement(`${Selector.Tabs}[data-index="${i}"] ${Selector.TabsEntry} ${Selector.Description}`, () => true); - - const label: TerminalLabel = { - name: title.textContent.replace(/^[├┌└]\s*/, ''), - description: description?.textContent - }; - // It's a new group if the the tab does not start with ├ or └ - if (title.textContent.match(/^[├└]/)) { - groups[groups.length - 1].push(label); - } else { - groups.push([label]); - } - } - return groups; - } - - async getSingleTabName(): Promise { - const tab = await this.code.waitForElement(Selector.SingleTab, singleTab => !!singleTab && singleTab?.textContent.length > 1); - return tab.textContent; - } - - private async assertTabExpected(selector?: string, listIndex?: number, nameRegex?: RegExp, icon?: string, color?: string, description?: string): Promise { - if (listIndex) { - if (nameRegex) { - await this.code.waitForElement(`${Selector.Tabs}[data-index="${listIndex}"] ${Selector.TabsEntry}`, entry => !!entry && !!entry?.textContent.match(nameRegex)); - if (description) { - await this.code.waitForElement(`${Selector.Tabs}[data-index="${listIndex}"] ${Selector.TabsEntry} ${Selector.Description}`, e => !!e && e.textContent === description); - } - } - 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(): Promise { - await this.code.waitForElement(Selector.TerminalView, result => result === undefined); - } - - async assertCommandDecorations(expectedCounts?: ICommandDecorationCounts, customIcon?: { updatedIcon: string; count: number }, showDecorations?: 'both' | 'gutter' | 'overviewRuler' | 'never'): Promise { - if (expectedCounts) { - const placeholderSelector = showDecorations === 'overviewRuler' ? `${Selector.CommandDecorationPlaceholder}${Selector.Hide}` : Selector.CommandDecorationPlaceholder; - await this.code.waitForElements(placeholderSelector, true, decorations => decorations && decorations.length === expectedCounts.placeholder); - const successSelector = showDecorations === 'overviewRuler' ? `${Selector.CommandDecorationSuccess}${Selector.Hide}` : Selector.CommandDecorationSuccess; - await this.code.waitForElements(successSelector, true, decorations => decorations && decorations.length === expectedCounts.success); - const errorSelector = showDecorations === 'overviewRuler' ? `${Selector.CommandDecorationError}${Selector.Hide}` : Selector.CommandDecorationError; - await this.code.waitForElements(errorSelector, true, decorations => decorations && decorations.length === expectedCounts.error); - } - - if (customIcon) { - await this.code.waitForElements(`.terminal-command-decoration.codicon-${customIcon.updatedIcon}`, true, decorations => decorations && decorations.length === customIcon.count); - } - } - - 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, splitIndex?: 0 | 1): Promise { - try { - let selector: string = Selector.Xterm; - if (splitIndex !== undefined) { - selector = splitIndex === 0 ? Selector.XtermSplitIndex0 : Selector.XtermSplitIndex1; - } - await this.code.waitForTerminalBuffer(selector, accept); - } catch (err: any) { - if (message) { - throw new Error(`${message} \n\nInner exception: \n${err.message} `); - } - throw err; - } - } - - async getPage(): Promise { - return (this.code.driver as any).page; - } - - /** - * Waits for the terminal to be focused and to contain content. - * @param expectedLocation The location to check the terminal for, defaults to panel. - */ - private async _waitForTerminal(expectedLocation?: 'editor' | 'panel'): Promise { - await this.code.waitForElement(Selector.XtermFocused); - await this.code.waitForTerminalBuffer(expectedLocation === 'editor' ? Selector.XtermEditor : Selector.Xterm, lines => lines.some(line => line.length > 0)); - } -} - -function vsc(data: string) { - return setTextParams(`633;${data}`); -} - -function setTextParams(data: string) { - return osc(`${data}\\x07`); -} - -function osc(data: string) { - return `\\x1b]${data}`; -} - -function csi(data: string) { - return `\\x1b[${data}`; -} diff --git a/test/automation/src/viewlet.ts b/test/automation/src/viewlet.ts deleted file mode 100644 index 5a1be8b7033..00000000000 --- a/test/automation/src/viewlet.ts +++ /dev/null @@ -1,15 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Code } from './code'; - -export abstract class Viewlet { - - constructor(protected code: Code) { } - - async waitForTitle(fn: (title: string) => boolean): Promise { - await this.code.waitForTextContent('.monaco-workbench .part.sidebar > .title > .title-label > h2', undefined, fn); - } -} diff --git a/test/automation/src/workbench.ts b/test/automation/src/workbench.ts index 3331ec74468..126ae4dad88 100644 --- a/test/automation/src/workbench.ts +++ b/test/automation/src/workbench.ts @@ -3,25 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Explorer } from './explorer'; -import { ActivityBar } from './activityBar'; -import { QuickAccess } from './quickaccess'; -import { QuickInput } from './quickinput'; -import { Extensions } from './extensions'; -import { Search } from './search'; -import { Editor } from './editor'; -import { SCM } from './scm'; -import { Debug } from './debug'; -import { StatusBar } from './statusbar'; -import { Problems } from './problems'; -import { SettingsEditor } from './settings'; -import { KeybindingsEditor } from './keybindings'; -import { Editors } from './editors'; +// --- Start Positron --- +// Removed existing imports +// --- End Positron --- import { Code } from './code'; -import { Terminal } from './terminal'; -import { Notebook } from './notebook'; -import { Localization } from './localization'; -import { Task } from './task'; +// --- Start Positron --- +// Removed existing imports +// --- End Positron --- // --- Start Positron --- import { PositronInterpreterDropdown } from './positron/positronInterpreterDropdown'; @@ -59,26 +47,10 @@ export interface Commands { export class Workbench { - readonly quickaccess: QuickAccess; - readonly quickinput: QuickInput; - readonly editors: Editors; - readonly explorer: Explorer; - readonly activitybar: ActivityBar; - readonly search: Search; - readonly extensions: Extensions; - readonly editor: Editor; - readonly scm: SCM; - readonly debug: Debug; - readonly statusbar: StatusBar; - readonly problems: Problems; - readonly settingsEditor: SettingsEditor; - readonly keybindingsEditor: KeybindingsEditor; - readonly terminal: Terminal; - readonly notebook: Notebook; - readonly localization: Localization; - readonly task: Task; - // --- Start Positron --- + + // removed existing properties + readonly positronInterpreterDropdown: PositronInterpreterDropdown; readonly positronPopups: PositronPopups; readonly positronConsole: PositronConsole; @@ -109,26 +81,10 @@ export class Workbench { // --- End Positron --- constructor(code: Code) { - this.editors = new Editors(code); - this.quickinput = new QuickInput(code); - this.quickaccess = new QuickAccess(code, this.editors, this.quickinput); - this.explorer = new Explorer(code); - this.activitybar = new ActivityBar(code); - this.search = new Search(code); - this.extensions = new Extensions(code, this.quickaccess); - this.editor = new Editor(code, this.quickaccess); - this.scm = new SCM(code); - this.debug = new Debug(code, this.quickaccess, this.editors, this.editor); - this.statusbar = new StatusBar(code); - this.problems = new Problems(code, this.quickaccess); - this.settingsEditor = new SettingsEditor(code, this.editors, this.editor, this.quickaccess); - this.keybindingsEditor = new KeybindingsEditor(code); - this.terminal = new Terminal(code, this.quickaccess, this.quickinput); - this.notebook = new Notebook(this.quickaccess, this.quickinput, code); - this.localization = new Localization(code); - this.task = new Task(code, this.editor, this.editors, this.quickaccess, this.quickinput, this.terminal); - // --- Start Positron --- + + // removed existing initializations + this.positronPopups = new PositronPopups(code); this.positronInterpreterDropdown = new PositronInterpreterDropdown(code); this.positronVariables = new PositronVariables(code); diff --git a/test/smoke/.gitignore b/test/smoke/.gitignore deleted file mode 100644 index ce23528315e..00000000000 --- a/test/smoke/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -.DS_Store -npm-debug.log -Thumbs.db -node_modules/ -out/ -keybindings.*.json -test_data/ -src/vscode/driver.d.ts -vscode-server*/ diff --git a/test/smoke/.yarnrc b/test/smoke/.yarnrc deleted file mode 100644 index f7076df9502..00000000000 --- a/test/smoke/.yarnrc +++ /dev/null @@ -1,5 +0,0 @@ -disturl "" -target "" -ms_build_id "" -runtime "node" -build_from_source "" diff --git a/test/smoke/Audit.md b/test/smoke/Audit.md deleted file mode 100644 index fd8913b44e2..00000000000 --- a/test/smoke/Audit.md +++ /dev/null @@ -1,15 +0,0 @@ -# VS Code Smoke Tests Failures History - -This file contains a history of smoke test failures which could be avoided if particular techniques were used in the test (e.g. binding test elements with HTML5 `data-*` attribute). - -To better understand what can be employed in smoke test to ensure its stability, it is important to understand patterns that led to smoke test breakage. This markdown is a result of work on [this issue](https://github.com/microsoft/vscode/issues/27906). - -## Log - -1. This following change led to the smoke test failure because DOM element's attribute `a[title]` was changed: - [eac49a3](https://github.com/microsoft/vscode/commit/eac49a321b84cb9828430e9dcd3f34243a3480f7) - - This attribute was used in the smoke test to grab the contents of SCM part in status bar: - [0aec2d6](https://github.com/microsoft/vscode/commit/0aec2d6838b5e65cc74c33b853ffbd9fa191d636) - -2. To be continued... diff --git a/test/smoke/README.md b/test/smoke/README.md deleted file mode 100644 index 87956c3eceb..00000000000 --- a/test/smoke/README.md +++ /dev/null @@ -1,6 +0,0 @@ - - - -These tests are currently not active. Please see the [E2E tests section](https://github.com/posit-dev/positron/blob/main/test/e2e/README.md). - - diff --git a/test/smoke/package-lock.json b/test/smoke/package-lock.json deleted file mode 100644 index af2f72d890a..00000000000 --- a/test/smoke/package-lock.json +++ /dev/null @@ -1,1000 +0,0 @@ -{ - "name": "code-oss-dev-smoke-test", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "code-oss-dev-smoke-test", - "version": "0.1.0", - "license": "MIT", - "dependencies": { - "ncp": "^2.0.0", - "node-fetch": "^2.6.7", - "rimraf": "3.0.2" - }, - "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/ncp": "2.0.1", - "@types/node": "20.x", - "@types/node-fetch": "^2.5.10", - "@types/rimraf": "3.0.2", - "npm-run-all": "^4.1.5", - "watch": "^1.0.2" - } - }, - "node_modules/@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", - "dev": true - }, - "node_modules/@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", - "dev": true, - "dependencies": { - "@types/events": "*", - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "node_modules/@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true - }, - "node_modules/@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", - "dev": true - }, - "node_modules/@types/ncp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/ncp/-/ncp-2.0.1.tgz", - "integrity": "sha512-TeiJ7uvv/92ugSqZ0v9l0eNXzutlki0aK+R1K5bfA5SYUil46ITlxLW4iNTCf55P4L5weCmaOdtxGeGWvudwPg==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/node-fetch": { - "version": "2.5.10", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.10.tgz", - "integrity": "sha512-IpkX0AasN44hgEad0gEF/V6EgR5n69VEqPEgnmoM8GsIGro3PowbWs4tR6IhxUTyPLpOn+fiGG6nrQhcmoCuIQ==", - "dev": true, - "dependencies": { - "@types/node": "*", - "form-data": "^3.0.0" - } - }, - "node_modules/@types/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ==", - "dev": true, - "dependencies": { - "@types/glob": "*", - "@types/node": "*" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k= sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c= sha512-9Y0g0Q8rmSt+H33DfKv7FOc3v+iRI+o1lbzt8jGcIosYW37IIW/2XVYq5NPdmaD5NQ59Nk26Kl/vZbwW9Fr8vg==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/call-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", - "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "dependencies": { - "object-keys": "^1.0.12" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk= sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "dev": true, - "dependencies": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/exec-sh": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.2.tgz", - "integrity": "sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw==", - "dev": true, - "dependencies": { - "merge": "^1.2.0" - } - }, - "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8= sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/get-intrinsic": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", - "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0= sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", - "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs= sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI= sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", - "dev": true, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/merge": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", - "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", - "dev": true - }, - "node_modules/mime-db": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", - "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.31", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", - "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", - "dev": true, - "dependencies": { - "mime-db": "1.48.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "node_modules/ncp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M= sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", - "bin": { - "ncp": "bin/ncp" - } - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/npm-run-all": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", - "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" - }, - "bin": { - "npm-run-all": "bin/npm-run-all/index.js", - "run-p": "bin/run-p/index.js", - "run-s": "bin/run-s/index.js" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E= sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18= sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pidtree": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", - "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "dev": true, - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", - "dev": true, - "dependencies": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shell-quote": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", - "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", - "dev": true - }, - "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", - "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", - "dev": true - }, - "node_modules/string.prototype.padend": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.1.tgz", - "integrity": "sha512-eCzTASPnoCr5Ht+Vn1YXgm8SB015hHKgEIMu9Nr9bQmLhRBxKRfmzSj/IQsxDFc8JInJDDFA0qXwK+xxI7wDkg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", - "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", - "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/watch": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/watch/-/watch-1.0.2.tgz", - "integrity": "sha1-NApxe952Vyb6CqB9ch4BR6VR3ww= sha512-1u+Z5n9Jc1E2c7qDO8SinPoZuHj7FgbgU1olSFoyaklduDvvtX7GMMtlE6OC9FTXq4KvNAOfj6Zu4vI1e9bAKA==", - "dev": true, - "dependencies": { - "exec-sh": "^0.2.0", - "minimist": "^1.2.0" - }, - "bin": { - "watch": "cli.js" - }, - "engines": { - "node": ">=0.1.95" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0= sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - } - } -} diff --git a/test/smoke/package.json b/test/smoke/package.json deleted file mode 100644 index e156b2fa629..00000000000 --- a/test/smoke/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "code-oss-dev-smoke-test", - "version": "0.1.0", - "license": "MIT", - "main": "./src/main.js", - "scripts": { - "compile": "cd ../automation && npm run compile && cd ../smoke && node ../../node_modules/typescript/bin/tsc", - "watch-automation": "cd ../automation && npm run watch", - "watch-smoke": "node ../../node_modules/typescript/bin/tsc --watch --preserveWatchOutput", - "watch": "npm-run-all -lp watch-automation watch-smoke", - "mocha": "node ../node_modules/mocha/bin/mocha" - }, - "dependencies": { - "canvas": "^2.11.2", - "mkdirp": "^1.0.4", - "ncp": "^2.0.0", - "node-fetch": "^2.6.7", - "resemblejs": "5.0.0", - "rimraf": "3.0.2" - }, - "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/ncp": "2.0.1", - "@types/node": "20.x", - "@types/node-fetch": "^2.5.10", - "@types/resemblejs": "4.1.3", - "@types/rimraf": "3.0.2", - "archiver": "^7.0.1", - "npm-run-all": "^4.1.5", - "watch": "^1.0.2" - } -} diff --git a/test/smoke/src/areas/extensions/extensions.test.ts b/test/smoke/src/areas/extensions/extensions.test.ts deleted file mode 100644 index c78cbe87089..00000000000 --- a/test/smoke/src/areas/extensions/extensions.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Application, Logger } from '../../../../automation'; -import { installAllHandlers } from '../../utils'; - -export function setup(logger: Logger) { - describe('Extensions', () => { - - // Shared before/after handling - installAllHandlers(logger); - - it('install and enable vscode-smoketest-check extension', async function () { - const app = this.app as Application; - - await app.workbench.extensions.installExtension('ms-vscode.vscode-smoketest-check', true); - - // Close extension editor because keybindings dispatch is not working when web views are opened and focused - // https://github.com/microsoft/vscode/issues/110276 - await app.workbench.extensions.closeExtension('vscode-smoketest-check'); - - await app.workbench.quickaccess.runCommand('Smoke Test Check'); - }); - }); -} diff --git a/test/smoke/src/areas/languages/languages.test.ts b/test/smoke/src/areas/languages/languages.test.ts deleted file mode 100644 index 9ec05b0c9e2..00000000000 --- a/test/smoke/src/areas/languages/languages.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { join } from 'path'; -import { Application, ProblemSeverity, Problems, Logger } from '../../../../automation'; -import { installAllHandlers } from '../../utils'; - -export function setup(logger: Logger) { - describe('Language Features', () => { - - // Shared before/after handling - installAllHandlers(logger); - - it('verifies quick outline (js)', async function () { - const app = this.app as Application; - await app.workbench.quickaccess.openFile(join(app.workspacePathOrFolder, 'bin', 'www')); - - await app.workbench.quickaccess.openQuickOutline(); - await app.workbench.quickinput.waitForQuickInputElements(names => names.length >= 6); - await app.workbench.quickinput.closeQuickInput(); - }); - - it('verifies quick outline (css)', async function () { - const app = this.app as Application; - await app.workbench.quickaccess.openFile(join(app.workspacePathOrFolder, 'public', 'stylesheets', 'style.css')); - - await app.workbench.quickaccess.openQuickOutline(); - await app.workbench.quickinput.waitForQuickInputElements(names => names.length === 2); - await app.workbench.quickinput.closeQuickInput(); - }); - - it('verifies problems view (css)', async function () { - const app = this.app as Application; - await app.workbench.quickaccess.openFile(join(app.workspacePathOrFolder, 'public', 'stylesheets', 'style.css')); - await app.workbench.editor.waitForTypeInEditor('style.css', '.foo{}'); - - await app.code.waitForElement(Problems.getSelectorInEditor(ProblemSeverity.WARNING)); - - await app.workbench.problems.showProblemsView(); - await app.code.waitForElement(Problems.getSelectorInProblemsView(ProblemSeverity.WARNING)); - await app.workbench.problems.hideProblemsView(); - }); - - it('verifies settings (css)', async function () { - const app = this.app as Application; - await app.workbench.settingsEditor.addUserSetting('css.lint.emptyRules', '"error"'); - await app.workbench.quickaccess.openFile(join(app.workspacePathOrFolder, 'public', 'stylesheets', 'style.css')); - - await app.code.waitForElement(Problems.getSelectorInEditor(ProblemSeverity.ERROR)); - - await app.workbench.problems.showProblemsView(); - await app.code.waitForElement(Problems.getSelectorInProblemsView(ProblemSeverity.ERROR)); - await app.workbench.problems.hideProblemsView(); - }); - }); -} diff --git a/test/smoke/src/areas/multiroot/multiroot.test.ts b/test/smoke/src/areas/multiroot/multiroot.test.ts deleted file mode 100644 index c4e6e46f9a4..00000000000 --- a/test/smoke/src/areas/multiroot/multiroot.test.ts +++ /dev/null @@ -1,75 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { writeFileSync } from 'fs'; -import { join, dirname } from 'path'; -import { Application, Logger } from '../../../../automation'; -import { installAllHandlers } from '../../utils'; - -function toUri(path: string): string { - if (process.platform === 'win32') { - return `${path.replace(/\\/g, '/')}`; - } - - return `${path}`; -} - -function createWorkspaceFile(workspacePath: string): string { - const workspaceFilePath = join(dirname(workspacePath), 'smoketest.code-workspace'); - const workspace = { - folders: [ - { path: toUri(join(workspacePath, 'public')) }, - { path: toUri(join(workspacePath, 'routes')) }, - { path: toUri(join(workspacePath, 'views')) } - ], - settings: { - 'workbench.startupEditor': 'none', - 'workbench.enableExperiments': false, - 'typescript.disableAutomaticTypeAcquisition': true, - 'json.schemaDownload.enable': false, - 'npm.fetchOnlinePackageInfo': false, - 'npm.autoDetect': 'off', - 'workbench.editor.languageDetection': false, - "workbench.localHistory.enabled": false - } - }; - - writeFileSync(workspaceFilePath, JSON.stringify(workspace, null, '\t')); - - return workspaceFilePath; -} - -export function setup(logger: Logger) { - describe('Multiroot', () => { - - // Shared before/after handling - installAllHandlers(logger, opts => { - const workspacePath = createWorkspaceFile(opts.workspacePath); - return { ...opts, workspacePath }; - }); - - it('shows results from all folders', async function () { - const app = this.app as Application; - const expectedNames = [ - 'index.js', - 'users.js', - 'style.css', - 'error.pug', - 'index.pug', - 'layout.pug' - ]; - - await app.workbench.quickaccess.openFileQuickAccessAndWait('*.*', 6); - await app.workbench.quickinput.waitForQuickInputElements(names => expectedNames.every(expectedName => names.some(name => expectedName === name))); - await app.workbench.quickinput.closeQuickInput(); - }); - - it('shows workspace name in title', async function () { - const app = this.app as Application; - - await app.code.waitForTitle(title => /smoketest \(Workspace\)/i.test(title)); - }); - }); -} diff --git a/test/smoke/src/areas/notebook/notebook.test.ts b/test/smoke/src/areas/notebook/notebook.test.ts deleted file mode 100644 index da4d527fe4f..00000000000 --- a/test/smoke/src/areas/notebook/notebook.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as cp from 'child_process'; -import { Application, Logger } from '../../../../automation'; -import { installAllHandlers } from '../../utils'; - -export function setup(logger: Logger) { - describe('Notebooks', () => { // https://github.com/microsoft/vscode/issues/140575 - - // Shared before/after handling - installAllHandlers(logger); - - afterEach(async function () { - const app = this.app as Application; - await app.workbench.quickaccess.runCommand('workbench.action.files.save'); - await app.workbench.quickaccess.runCommand('workbench.action.closeActiveEditor'); - }); - - after(async function () { - const app = this.app as Application; - cp.execSync('git checkout . --quiet', { cwd: app.workspacePathOrFolder }); - cp.execSync('git reset --hard HEAD --quiet', { cwd: app.workspacePathOrFolder }); - }); - - it.skip('check heap leaks', async function () { - const app = this.app as Application; - await app.profiler.checkHeapLeaks(['NotebookTextModel', 'NotebookCellTextModel', 'NotebookEventDispatcher'], async () => { - await app.workbench.notebook.openNotebook(); - await app.workbench.quickaccess.runCommand('workbench.action.files.save'); - await app.workbench.quickaccess.runCommand('workbench.action.closeActiveEditor'); - }); - }); - - it('check object leaks', async function () { - const app = this.app as Application; - await app.profiler.checkObjectLeaks(['NotebookTextModel', 'NotebookCellTextModel', 'NotebookEventDispatcher'], async () => { - await app.workbench.notebook.openNotebook(); - await app.workbench.quickaccess.runCommand('workbench.action.files.save'); - await app.workbench.quickaccess.runCommand('workbench.action.closeActiveEditor'); - }); - }); - - it.skip('inserts/edits code cell', async function () { - const app = this.app as Application; - await app.workbench.notebook.openNotebook(); - await app.workbench.notebook.focusNextCell(); - await app.workbench.notebook.insertNotebookCell('code'); - await app.workbench.notebook.waitForTypeInEditor('// some code'); - await app.workbench.notebook.stopEditingCell(); - }); - - it.skip('inserts/edits markdown cell', async function () { - const app = this.app as Application; - await app.workbench.notebook.openNotebook(); - await app.workbench.notebook.focusNextCell(); - await app.workbench.notebook.insertNotebookCell('markdown'); - await app.workbench.notebook.waitForTypeInEditor('## hello2! '); - await app.workbench.notebook.stopEditingCell(); - await app.workbench.notebook.waitForMarkdownContents('h2', 'hello2!'); - }); - - it.skip('moves focus as it inserts/deletes a cell', async function () { - const app = this.app as Application; - await app.workbench.notebook.openNotebook(); - await app.workbench.notebook.insertNotebookCell('code'); - await app.workbench.notebook.waitForActiveCellEditorContents(''); - await app.workbench.notebook.stopEditingCell(); - await app.workbench.notebook.deleteActiveCell(); - await app.workbench.notebook.waitForMarkdownContents('p', 'Markdown Cell'); - }); - - it.skip('moves focus in and out of output', async function () { // TODO@rebornix https://github.com/microsoft/vscode/issues/139270 - const app = this.app as Application; - await app.workbench.notebook.openNotebook(); - await app.workbench.notebook.executeActiveCell(); - await app.workbench.notebook.focusInCellOutput(); - await app.workbench.notebook.focusOutCellOutput(); - await app.workbench.notebook.waitForActiveCellEditorContents('code()'); - }); - - it.skip('cell action execution', async function () { // TODO@rebornix https://github.com/microsoft/vscode/issues/139270 - const app = this.app as Application; - await app.workbench.notebook.openNotebook(); - await app.workbench.notebook.insertNotebookCell('code'); - await app.workbench.notebook.executeCellAction('.notebook-editor .monaco-list-row.focused div.monaco-toolbar .codicon-debug'); - await app.workbench.notebook.waitForActiveCellEditorContents('test'); - }); - }); -} diff --git a/test/smoke/src/areas/preferences/preferences.test.ts b/test/smoke/src/areas/preferences/preferences.test.ts deleted file mode 100644 index 14f618ef30a..00000000000 --- a/test/smoke/src/areas/preferences/preferences.test.ts +++ /dev/null @@ -1,89 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Application, ActivityBarPosition, Logger } from '../../../../automation'; -import { installAllHandlers } from '../../utils'; - -export function setup(logger: Logger) { - describe('Preferences', () => { - - // Shared before/after handling - installAllHandlers(logger); - - it('turns off editor line numbers and verifies the live change', async function () { - const app = this.app as Application; - - await app.workbench.settingsEditor.openUserSettingsFile(); - await app.code.waitForElements('.line-numbers', false, elements => !!elements.length); - - await app.workbench.settingsEditor.addUserSetting('editor.lineNumbers', '"off"'); - await app.code.waitForElements('.line-numbers', false, elements => !elements || elements.length === 0); - }); - - it('changes "workbench.action.toggleSidebarPosition" command key binding and verifies it', async function () { - const app = this.app as Application; - - await app.workbench.activitybar.waitForActivityBar(ActivityBarPosition.LEFT); - - await app.workbench.keybindingsEditor.updateKeybinding('workbench.action.toggleSidebarPosition', 'View: Toggle Primary Side Bar Position', 'ctrl+u', 'Control+U'); - - await app.code.dispatchKeybinding('ctrl+u'); - await app.workbench.activitybar.waitForActivityBar(ActivityBarPosition.RIGHT); - }); - }); - - describe('Settings editor', () => { - - // Shared before/after handling - installAllHandlers(logger); - - it('shows a modified indicator on a modified setting', async function () { - const app = this.app as Application; - - await app.workbench.settingsEditor.searchSettingsUI('@id:editor.tabSize'); - await app.code.waitForSetValue('.settings-editor .setting-item-contents .setting-item-control input', '6'); - await app.code.waitForElement('.settings-editor .setting-item-contents .setting-item-modified-indicator'); - await app.code.waitForSetValue('.settings-editor .setting-item-contents .setting-item-control input', '4'); - }); - - // Skipping test due to it being flaky. - it.skip('turns off editor line numbers and verifies the live change', async function () { - const app = this.app as Application; - - await app.workbench.editors.newUntitledFile(); - await app.code.dispatchKeybinding('enter'); - await app.code.waitForElements('.line-numbers', false, elements => !!elements.length); - - // Turn off line numbers - await app.workbench.settingsEditor.searchSettingsUI('editor.lineNumbers'); - await app.code.waitAndClick('.settings-editor .monaco-list-rows .setting-item-control select', 2, 2); - await app.code.waitAndClick('.context-view .option-text', 2, 2); - - await app.workbench.editors.selectTab('Untitled-1'); - await app.code.waitForElements('.line-numbers', false, elements => !elements || elements.length === 0); - }); - - // Skipping test due to it being flaky. - it.skip('hides the toc when searching depending on the search behavior', async function () { - const app = this.app as Application; - - // Hide ToC when searching - await app.workbench.settingsEditor.searchSettingsUI('workbench.settings.settingsSearchTocBehavior'); - await app.code.waitAndClick('.settings-editor .monaco-list-rows .setting-item-control select', 2, 2); - await app.code.waitAndClick('.context-view .monaco-list-row:nth-child(1) .option-text', 2, 2); - await app.workbench.settingsEditor.searchSettingsUI('test'); - await app.code.waitForElements('.settings-editor .settings-toc-container', false, elements => elements.length === 1 && elements[0].attributes['style'].includes('width: 0px')); - await app.code.waitForElements('.settings-editor .settings-body .monaco-sash', false, elements => elements.length === 1 && elements[0].className.includes('disabled')); - - // Show ToC when searching - await app.workbench.settingsEditor.searchSettingsUI('workbench.settings.settingsSearchTocBehavior'); - await app.code.waitAndClick('.settings-editor .monaco-list-rows .setting-item-control select', 2, 2); - await app.code.waitAndClick('.context-view .monaco-list-row:nth-child(2) .option-text', 2, 2); - await app.workbench.settingsEditor.searchSettingsUI('test'); - await app.code.waitForElements('.settings-editor .settings-toc-container', false, elements => elements.length === 1 && !elements[0].attributes['style'].includes('width: 0px')); - await app.code.waitForElements('.settings-editor .settings-body .monaco-sash', false, elements => elements.length === 1 && !elements[0].className.includes('disabled')); - }); - }); -} diff --git a/test/smoke/src/areas/search/search.test.ts b/test/smoke/src/areas/search/search.test.ts deleted file mode 100644 index 78f79b61838..00000000000 --- a/test/smoke/src/areas/search/search.test.ts +++ /dev/null @@ -1,115 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as cp from 'child_process'; -import { Application, Logger } from '../../../../automation'; -import { installAllHandlers, retry } from '../../utils'; - -export function setup(logger: Logger) { - describe('Search', () => { - - // Shared before/after handling - installAllHandlers(logger); - - after(function () { - const app = this.app as Application; - retry(async () => cp.execSync('git checkout . --quiet', { cwd: app.workspacePathOrFolder }), 0, 5); - retry(async () => cp.execSync('git reset --hard HEAD --quiet', { cwd: app.workspacePathOrFolder }), 0, 5); - }); - - it('verifies the sidebar moves to the right', async function () { - const app = this.app as Application; - await app.workbench.search.openSearchViewlet(); - - await app.code.dispatchKeybinding('PageUp'); - await app.workbench.search.hasActivityBarMoved(); - - await app.code.dispatchKeybinding('PageUp'); - await app.workbench.search.hasActivityBarMoved(); - }); - - it('searches for body & checks for correct result number', async function () { - const app = this.app as Application; - await app.workbench.search.openSearchViewlet(); - await app.workbench.search.searchFor('body'); - - await app.workbench.search.waitForResultText('6 results in 3 files'); - }); - - it('searches only for *.js files & checks for correct result number', async function () { - const app = this.app as Application; - try { - await app.workbench.search.setFilesToIncludeText('*.js'); - await app.workbench.search.searchFor('body'); - await app.workbench.search.showQueryDetails(); - - await app.workbench.search.waitForResultText('4 results in 1 file'); - } finally { - await app.workbench.search.setFilesToIncludeText(''); - await app.workbench.search.hideQueryDetails(); - } - }); - - it('dismisses result & checks for correct result number', async function () { - const app = this.app as Application; - await app.workbench.search.searchFor('body'); - await app.workbench.search.waitForResultText('6 results in 3 files'); - await app.workbench.search.removeFileMatch('app.js', '2 results in 2 files'); - }); - - it.skip('replaces first search result with a replace term', async function () { // TODO@roblourens https://github.com/microsoft/vscode/issues/137195 - const app = this.app as Application; - - await app.workbench.search.searchFor('body'); - await app.workbench.search.waitForResultText('6 results in 3 files'); - await app.workbench.search.expandReplace(); - await app.workbench.search.setReplaceText('ydob'); - await app.workbench.search.replaceFileMatch('app.js', '2 results in 2 files'); - - await app.workbench.search.searchFor('ydob'); - await app.workbench.search.waitForResultText('4 results in 1 file'); - await app.workbench.search.setReplaceText('body'); - await app.workbench.search.replaceFileMatch('app.js', '0 results in 0 files'); - await app.workbench.search.waitForResultText('0 results in 0 files'); - }); - }); - - describe('Quick Open', () => { - - // Shared before/after handling - installAllHandlers(logger); - - it('quick open search produces correct result', async function () { - const app = this.app as Application; - const expectedNames = [ - '.eslintrc.json', - 'tasks.json', - 'settings.json', - 'app.js', - 'index.js', - 'users.js', - 'package.json', - 'jsconfig.json' - ]; - - await app.workbench.quickaccess.openFileQuickAccessAndWait('.js', 8); - await app.workbench.quickinput.waitForQuickInputElements(names => expectedNames.every(expectedName => names.some(name => expectedName === name))); - await app.workbench.quickinput.closeQuickInput(); - }); - - it('quick open respects fuzzy matching', async function () { - const app = this.app as Application; - const expectedNames = [ - 'tasks.json', - 'app.js', - 'package.json' - ]; - - await app.workbench.quickaccess.openFileQuickAccessAndWait('a.s', 3); - await app.workbench.quickinput.waitForQuickInputElements(names => expectedNames.every(expectedName => names.some(name => expectedName === name))); - await app.workbench.quickinput.closeQuickInput(); - }); - }); -} diff --git a/test/smoke/src/areas/statusbar/statusbar.test.ts b/test/smoke/src/areas/statusbar/statusbar.test.ts deleted file mode 100644 index ccfbeb5772f..00000000000 --- a/test/smoke/src/areas/statusbar/statusbar.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { join } from 'path'; -import { Application, StatusBarElement, Logger } from '../../../../automation'; -import { installAllHandlers } from '../../utils'; - -export function setup(logger: Logger) { - describe('Statusbar', () => { - - // Shared before/after handling - installAllHandlers(logger); - - it('verifies presence of all default status bar elements', async function () { - const app = this.app as Application; - await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.BRANCH_STATUS); - await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.SYNC_STATUS); - await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.PROBLEMS_STATUS); - - await app.workbench.quickaccess.openFile(join(app.workspacePathOrFolder, 'readme.md')); - await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.ENCODING_STATUS); - await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.EOL_STATUS); - await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.INDENTATION_STATUS); - await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.LANGUAGE_STATUS); - await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.SELECTION_STATUS); - }); - - it(`verifies that 'quick input' opens when clicking on status bar elements`, async function () { - const app = this.app as Application; - await app.workbench.statusbar.clickOn(StatusBarElement.BRANCH_STATUS); - await app.workbench.quickinput.waitForQuickInputOpened(); - await app.workbench.quickinput.closeQuickInput(); - - await app.workbench.quickaccess.openFile(join(app.workspacePathOrFolder, 'readme.md')); - await app.workbench.statusbar.clickOn(StatusBarElement.INDENTATION_STATUS); - await app.workbench.quickinput.waitForQuickInputOpened(); - await app.workbench.quickinput.closeQuickInput(); - await app.workbench.statusbar.clickOn(StatusBarElement.ENCODING_STATUS); - await app.workbench.quickinput.waitForQuickInputOpened(); - await app.workbench.quickinput.closeQuickInput(); - await app.workbench.statusbar.clickOn(StatusBarElement.EOL_STATUS); - await app.workbench.quickinput.waitForQuickInputOpened(); - await app.workbench.quickinput.closeQuickInput(); - await app.workbench.statusbar.clickOn(StatusBarElement.LANGUAGE_STATUS); - await app.workbench.quickinput.waitForQuickInputOpened(); - await app.workbench.quickinput.closeQuickInput(); - }); - - it(`verifies that 'Problems View' appears when clicking on 'Problems' status element`, async function () { - const app = this.app as Application; - await app.workbench.statusbar.clickOn(StatusBarElement.PROBLEMS_STATUS); - await app.workbench.problems.waitForProblemsView(); - }); - - it(`verifies if changing EOL is reflected in the status bar`, async function () { - const app = this.app as Application; - await app.workbench.quickaccess.openFile(join(app.workspacePathOrFolder, 'readme.md')); - await app.workbench.statusbar.clickOn(StatusBarElement.EOL_STATUS); - - await app.workbench.quickinput.selectQuickInputElement(1); - - await app.workbench.statusbar.waitForEOL('CRLF'); - }); - }); -} diff --git a/test/smoke/src/areas/task/task-quick-pick.test.ts b/test/smoke/src/areas/task/task-quick-pick.test.ts deleted file mode 100644 index e88b28670bb..00000000000 --- a/test/smoke/src/areas/task/task-quick-pick.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Application, Task, Terminal, TerminalCommandId } from '../../../../automation/'; - -export function setup(options?: { skipSuite: boolean }) { - describe('Task Quick Pick', () => { - let app: Application; - let task: Task; - let terminal: Terminal; - - // Acquire automation API - before(async function () { - app = this.app as Application; - task = app.workbench.task; - terminal = app.workbench.terminal; - }); - - afterEach(async () => { - // Kill all terminals between every test for a consistent testing environment - await terminal.runCommand(TerminalCommandId.KillAll); - }); - - describe('Tasks: Run Task', () => { - const label = "name"; - const type = "shell"; - const command = "echo 'test'"; - it('hide property - true', async () => { - await task.configureTask({ type, command, label, hide: true }); - await task.assertTasks(label, [], 'run'); - }); - it('hide property - false', async () => { - await task.configureTask({ type, command, label, hide: false }); - await task.assertTasks(label, [{ label }], 'run'); - }); - it('hide property - undefined', async () => { - await task.configureTask({ type, command, label }); - await task.assertTasks(label, [{ label }], 'run'); - }); - (options?.skipSuite ? it.skip : it)('icon - icon only', async () => { - const config = { label, type, command, icon: { id: "lightbulb" } }; - await task.configureTask(config); - await task.assertTasks(label, [config], 'run'); - }); - (options?.skipSuite ? it.skip : it)('icon - color only', async () => { - const config = { label, type, command, icon: { color: "terminal.ansiRed" } }; - await task.configureTask(config); - await task.assertTasks(label, [{ label, type, command, icon: { color: "Red" } }], 'run'); - }); - (options?.skipSuite ? it.skip : it)('icon - icon & color', async () => { - const config = { label, type, command, icon: { id: "lightbulb", color: "terminal.ansiRed" } }; - await task.configureTask(config); - await task.assertTasks(label, [{ label, type, command, icon: { id: "lightbulb", color: "Red" } }], 'run'); - }); - }); - //TODO: why won't this command run - describe.skip('Tasks: Configure Task', () => { - const label = "name"; - const type = "shell"; - const command = "echo 'test'"; - describe('hide', () => { - it('true should still show the task', async () => { - await task.configureTask({ type, command, label, hide: true }); - await task.assertTasks(label, [{ label }], 'configure'); - }); - }); - }); - }); -} diff --git a/test/smoke/src/areas/task/task.test.ts b/test/smoke/src/areas/task/task.test.ts deleted file mode 100644 index d006ec2b9e8..00000000000 --- a/test/smoke/src/areas/task/task.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Logger } from '../../../../automation'; -import { installAllHandlers } from '../../utils'; -import { setup as setupTaskQuickPickTests } from './task-quick-pick.test'; - -export function setup(logger: Logger) { - describe('Task', function () { - - // Retry tests 3 times to minimize build failures due to any flakiness - this.retries(3); - - // Shared before/after handling - installAllHandlers(logger); - - // Refs https://github.com/microsoft/vscode/issues/225250 - // Pty spawning fails with invalid fd error in product CI while development CI - // works fine, we need additional logging to investigate. - setupTaskQuickPickTests({ skipSuite: process.platform === 'linux' }); - }); -} diff --git a/test/smoke/src/areas/terminal/terminal-editors.test.ts b/test/smoke/src/areas/terminal/terminal-editors.test.ts deleted file mode 100644 index 39d1b6ced7f..00000000000 --- a/test/smoke/src/areas/terminal/terminal-editors.test.ts +++ /dev/null @@ -1,81 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Application, Terminal, TerminalCommandId, TerminalCommandIdWithValue, SettingsEditor } from '../../../../automation'; -import { setTerminalTestSettings } from './terminal-helpers'; - -export function setup(options?: { skipSuite: boolean }) { - (options?.skipSuite ? describe.skip : describe)('Terminal Editors', () => { - let app: Application; - let terminal: Terminal; - let settingsEditor: SettingsEditor; - - // Acquire automation API - before(async function () { - app = this.app as Application; - terminal = app.workbench.terminal; - settingsEditor = app.workbench.settingsEditor; - await setTerminalTestSettings(app); - }); - - after(async function () { - await settingsEditor.clearUserSettings(); - }); - - 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 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); - }); - - it('should create a terminal in the editor area by default', async () => { - await app.workbench.settingsEditor.addUserSetting('terminal.integrated.defaultLocation', '"editor"'); - // Close the settings editor - await app.workbench.quickaccess.runCommand('workbench.action.closeAllEditors'); - await terminal.createTerminal('editor'); - await terminal.assertEditorGroupCount(1); - await terminal.assertTerminalViewHidden(); - await app.workbench.settingsEditor.clearUserSettings(); - }); - }); -} diff --git a/test/smoke/src/areas/terminal/terminal-helpers.ts b/test/smoke/src/areas/terminal/terminal-helpers.ts deleted file mode 100644 index c333df0bc5a..00000000000 --- a/test/smoke/src/areas/terminal/terminal-helpers.ts +++ /dev/null @@ -1,23 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Application } from '../../../../automation'; - -export async function setTerminalTestSettings(app: Application, additionalSettings: [key: string, value: string][] = []) { - await app.workbench.settingsEditor.addUserSettings([ - // Work wrap is required when calling settingsEditor.addUserSetting multiple times or the - // click to focus will fail - ['editor.wordWrap', '"on"'], - // Always show tabs to make getting terminal groups easier - ['terminal.integrated.tabs.hideCondition', '"never"'], - // Use the DOM renderer for smoke tests so they can be inspected in the playwright trace - // viewer - ['terminal.integrated.gpuAcceleration', '"off"'], - ...additionalSettings - ]); - - // Close the settings editor - await app.workbench.quickaccess.runCommand('workbench.action.closeAllEditors'); -} diff --git a/test/smoke/src/areas/terminal/terminal-input.test.ts b/test/smoke/src/areas/terminal/terminal-input.test.ts deleted file mode 100644 index 46fe72ba79f..00000000000 --- a/test/smoke/src/areas/terminal/terminal-input.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Application, Terminal, SettingsEditor } from '../../../../automation'; -import { setTerminalTestSettings } from './terminal-helpers'; - -export function setup(options?: { skipSuite: boolean }) { - (options?.skipSuite ? describe.skip : describe)('Terminal Input', () => { - let terminal: Terminal; - let settingsEditor: SettingsEditor; - - // Acquire automation API - before(async function () { - const app = this.app as Application; - terminal = app.workbench.terminal; - settingsEditor = app.workbench.settingsEditor; - await setTerminalTestSettings(app); - }); - - after(async function () { - await settingsEditor.clearUserSettings(); - }); - - describe('Auto replies', function () { - - // HACK: Retry this suite only on Windows because conpty can rarely lead to unexpected behavior which would - // cause flakiness. If this does happen, the feature is expected to fail. - if (process.platform === 'win32') { - this.retries(3); - } - - async function writeTextForAutoReply(text: string): Promise { - // Put the matching word in quotes to avoid powershell coloring the first word and - // on a new line to avoid cursor move/line switching sequences - await terminal.runCommandInTerminal(`"\r${text}`, true); - } - - it('should automatically reply to a custom entry', async () => { - await settingsEditor.addUserSetting('terminal.integrated.autoReplies', '{ "foo": "bar" }'); - await terminal.createTerminal(); - await writeTextForAutoReply('foo'); - await terminal.waitForTerminalText(buffer => buffer.some(line => line.match(/foo.*bar/))); - }); - }); - }); -} diff --git a/test/smoke/src/areas/terminal/terminal-persistence.test.ts b/test/smoke/src/areas/terminal/terminal-persistence.test.ts deleted file mode 100644 index 455d1f016d4..00000000000 --- a/test/smoke/src/areas/terminal/terminal-persistence.test.ts +++ /dev/null @@ -1,83 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Application, Terminal, TerminalCommandId, TerminalCommandIdWithValue, SettingsEditor } from '../../../../automation'; -import { setTerminalTestSettings } from './terminal-helpers'; - -export function setup(options?: { skipSuite: boolean }) { - (options?.skipSuite ? describe.skip : describe)('Terminal Persistence', () => { - // Acquire automation API - let terminal: Terminal; - let settingsEditor: SettingsEditor; - - before(async function () { - const app = this.app as Application; - terminal = app.workbench.terminal; - settingsEditor = app.workbench.settingsEditor; - await setTerminalTestSettings(app); - }); - - after(async function () { - await settingsEditor.clearUserSettings(); - }); - - describe('detach/attach', () => { - // https://github.com/microsoft/vscode/issues/137799 - it('should support basic reconnection', async () => { - await terminal.createTerminal(); - // TODO: Handle passing in an actual regex, not string - await terminal.assertTerminalGroups([ - [{ name: '.*' }] - ]); - - // Get the terminal name - await terminal.assertTerminalGroups([ - [{ name: '.*' }] - ]); - const name = (await terminal.getTerminalGroups())[0][0].name!; - - // Detach - await terminal.runCommand(TerminalCommandId.DetachSession); - await terminal.assertTerminalViewHidden(); - - // Attach - await terminal.runCommandWithValue(TerminalCommandIdWithValue.AttachToSession, name); - await terminal.assertTerminalGroups([ - [{ name }] - ]); - }); - - it.skip('should persist buffer content', async () => { - await terminal.createTerminal(); - // TODO: Handle passing in an actual regex, not string - await terminal.assertTerminalGroups([ - [{ name: '.*' }] - ]); - - // Get the terminal name - await terminal.assertTerminalGroups([ - [{ name: '.*' }] - ]); - const name = (await terminal.getTerminalGroups())[0][0].name!; - - // Write in terminal - await terminal.runCommandInTerminal('echo terminal_test_content'); - await terminal.waitForTerminalText(buffer => buffer.some(e => e.includes('terminal_test_content'))); - - // Detach - await terminal.runCommand(TerminalCommandId.DetachSession); - await terminal.assertTerminalViewHidden(); - - // Attach - await terminal.runCommandWithValue(TerminalCommandIdWithValue.AttachToSession, name); - await terminal.assertTerminalGroups([ - [{ name }] - ]); - // There can be line wrapping, so remove newlines and carriage returns #216464 - await terminal.waitForTerminalText(buffer => buffer.some(e => e.replaceAll(/[\r\n]/g, '').includes('terminal_test_content'))); - }); - }); - }); -} diff --git a/test/smoke/src/areas/terminal/terminal-profiles.test.ts b/test/smoke/src/areas/terminal/terminal-profiles.test.ts deleted file mode 100644 index e87509bc8e5..00000000000 --- a/test/smoke/src/areas/terminal/terminal-profiles.test.ts +++ /dev/null @@ -1,83 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Application, Terminal, TerminalCommandId, TerminalCommandIdWithValue, SettingsEditor } from '../../../../automation'; -import { setTerminalTestSettings } from './terminal-helpers'; - -const CONTRIBUTED_PROFILE_NAME = `JavaScript Debug Terminal`; -const ANY_PROFILE_NAME = '^((?!JavaScript Debug Terminal).)*$'; - -export function setup(options?: { skipSuite: boolean }) { - (options?.skipSuite ? describe.skip : describe)('Terminal Profiles', () => { - // Acquire automation API - let terminal: Terminal; - let settingsEditor: SettingsEditor; - - before(async function () { - const app = this.app as Application; - terminal = app.workbench.terminal; - settingsEditor = app.workbench.settingsEditor; - await setTerminalTestSettings(app); - }); - - after(async function () { - await settingsEditor.clearUserSettings(); - }); - - it('should launch the default profile', async () => { - await terminal.runCommand(TerminalCommandId.Show); - await terminal.assertSingleTab({ name: ANY_PROFILE_NAME }); - }); - - it('should set the default profile to a contributed one', async () => { - await terminal.runCommandWithValue(TerminalCommandIdWithValue.SelectDefaultProfile, CONTRIBUTED_PROFILE_NAME); - await terminal.createTerminal(); - await terminal.assertSingleTab({ name: CONTRIBUTED_PROFILE_NAME }); - }); - - it('should use the default contributed profile on panel open and for splitting', async () => { - await terminal.runCommandWithValue(TerminalCommandIdWithValue.SelectDefaultProfile, CONTRIBUTED_PROFILE_NAME); - await terminal.runCommand(TerminalCommandId.Show); - await terminal.runCommand(TerminalCommandId.Split); - await terminal.assertTerminalGroups([[{ name: CONTRIBUTED_PROFILE_NAME }, { name: CONTRIBUTED_PROFILE_NAME }]]); - }); - - it('should set the default profile', async () => { - await terminal.runCommandWithValue(TerminalCommandIdWithValue.SelectDefaultProfile, process.platform === 'win32' ? 'PowerShell' : undefined); - await terminal.createTerminal(); - 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 terminal.assertSingleTab({ name: ANY_PROFILE_NAME }); - await terminal.runCommand(TerminalCommandId.Split); - await terminal.assertTerminalGroups([[{}, {}]]); - }); - - it('createWithProfile command should create a terminal with a profile', async () => { - await terminal.runCommandWithValue(TerminalCommandIdWithValue.NewWithProfile); - await terminal.assertSingleTab({ name: ANY_PROFILE_NAME }); - }); - - it('createWithProfile command should create a terminal with a contributed profile', async () => { - 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.runCommandWithValue(TerminalCommandIdWithValue.NewWithProfile, undefined, true); - await terminal.assertTerminalGroups([[{}, {}]]); - }); - - it('createWithProfile command should create a split terminal with a contributed profile', async () => { - await terminal.runCommand(TerminalCommandId.Show); - 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-shellIntegration.test.ts b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts deleted file mode 100644 index 23f94e89f93..00000000000 --- a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts +++ /dev/null @@ -1,154 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Application, Terminal, SettingsEditor, TerminalCommandIdWithValue, TerminalCommandId } from '../../../../automation'; -import { setTerminalTestSettings } from './terminal-helpers'; - -export function setup(options?: { skipSuite: boolean }) { - (options?.skipSuite ? describe.skip : describe)('Terminal Shell Integration', () => { - let terminal: Terminal; - let settingsEditor: SettingsEditor; - let app: Application; - // Acquire automation API - before(async function () { - app = this.app as Application; - terminal = app.workbench.terminal; - settingsEditor = app.workbench.settingsEditor; - }); - - afterEach(async function () { - await app.workbench.terminal.runCommand(TerminalCommandId.KillAll); - }); - - async function createShellIntegrationProfile() { - await terminal.runCommandWithValue(TerminalCommandIdWithValue.NewWithProfile, process.platform === 'win32' ? 'PowerShell' : 'bash'); - } - - // TODO: Some agents may not have pwsh installed? - (process.platform === 'win32' ? describe.skip : describe)(`Process-based tests`, function () { - before(async function () { - await setTerminalTestSettings(app, [['terminal.integrated.shellIntegration.enabled', 'true']]); - }); - after(async function () { - await settingsEditor.clearUserSettings(); - }); - describe('Decorations', function () { - describe('Should show default icons', function () { - it('Placeholder', async () => { - await createShellIntegrationProfile(); - await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); - }); - it('Success', async () => { - await createShellIntegrationProfile(); - await terminal.runCommandInTerminal(`echo "success"`); - await terminal.assertCommandDecorations({ placeholder: 1, success: 1, error: 0 }); - }); - it('Error', async () => { - await createShellIntegrationProfile(); - await terminal.runCommandInTerminal(`false`); - await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 1 }); - }); - }); - describe('terminal.integrated.shellIntegration.decorationsEnabled should determine gutter and overview ruler decoration visibility', function () { - beforeEach(async () => { - await settingsEditor.clearUserSettings(); - await setTerminalTestSettings(app, [['terminal.integrated.shellIntegration.enabled', 'true']]); - await createShellIntegrationProfile(); - await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); - await terminal.runCommandInTerminal(`echo "foo"`); - await terminal.runCommandInTerminal(`bar`); - await terminal.assertCommandDecorations({ placeholder: 1, success: 1, error: 1 }); - }); - afterEach(async () => { - await app.workbench.terminal.runCommand(TerminalCommandId.KillAll); - }); - it('never', async () => { - await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationsEnabled', '"never"'); - await terminal.assertCommandDecorations({ placeholder: 0, success: 0, error: 0 }, undefined, 'never'); - }); - it('both', async () => { - await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationsEnabled', '"both"'); - await terminal.assertCommandDecorations({ placeholder: 1, success: 1, error: 1 }, undefined, 'both'); - }); - it('gutter', async () => { - await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationsEnabled', '"gutter"'); - await terminal.assertCommandDecorations({ placeholder: 1, success: 1, error: 1 }, undefined, 'gutter'); - }); - it('overviewRuler', async () => { - await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationsEnabled', '"overviewRuler"'); - await terminal.assertCommandDecorations({ placeholder: 1, success: 1, error: 1 }, undefined, 'overviewRuler'); - }); - }); - }); - }); - - // These are integration tests that only test the UI side by simulating process writes. - // Because of this, they do not test the shell integration scripts, only what the scripts - // are expected to write. - describe('Write data-based tests', () => { - before(async function () { - await setTerminalTestSettings(app); - }); - after(async function () { - await settingsEditor.clearUserSettings(); - }); - beforeEach(async function () { - // Create the simplest system profile to get as little process interaction as possible - await terminal.createTerminal(); - // Erase all content and reset cursor to top - await terminal.runCommandWithValue(TerminalCommandIdWithValue.WriteDataToTerminal, `${csi('2J')}${csi('H')}`); - }); - describe('VS Code sequences', () => { - it('should handle the simple case', async () => { - await terminal.runCommandWithValue(TerminalCommandIdWithValue.WriteDataToTerminal, `${vsc('A')}Prompt> ${vsc('B')}exitcode 0`); - await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); - await terminal.runCommandWithValue(TerminalCommandIdWithValue.WriteDataToTerminal, `\\r\\n${vsc('C')}Success\\r\\n${vsc('D;0')}`); - await terminal.assertCommandDecorations({ placeholder: 0, success: 1, error: 0 }); - await terminal.runCommandWithValue(TerminalCommandIdWithValue.WriteDataToTerminal, `${vsc('A')}Prompt> ${vsc('B')}exitcode 1`); - await terminal.assertCommandDecorations({ placeholder: 1, success: 1, error: 0 }); - await terminal.runCommandWithValue(TerminalCommandIdWithValue.WriteDataToTerminal, `\\r\\n${vsc('C')}Failure\\r\\n${vsc('D;1')}`); - await terminal.assertCommandDecorations({ placeholder: 0, success: 1, error: 1 }); - await terminal.runCommandWithValue(TerminalCommandIdWithValue.WriteDataToTerminal, `${vsc('A')}Prompt> ${vsc('B')}`); - await terminal.assertCommandDecorations({ placeholder: 1, success: 1, error: 1 }); - }); - }); - // TODO: This depends on https://github.com/microsoft/vscode/issues/146587 - describe.skip('Final Term sequences', () => { - it('should handle the simple case', async () => { - await terminal.runCommandWithValue(TerminalCommandIdWithValue.WriteDataToTerminal, `${ft('A')}Prompt> ${ft('B')}exitcode 0`); - await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); - await terminal.runCommandWithValue(TerminalCommandIdWithValue.WriteDataToTerminal, `\\r\\n${ft('C')}Success\\r\\n${ft('D;0')}`); - await terminal.assertCommandDecorations({ placeholder: 0, success: 1, error: 0 }); - await terminal.runCommandWithValue(TerminalCommandIdWithValue.WriteDataToTerminal, `${ft('A')}Prompt> ${ft('B')}exitcode 1`); - await terminal.assertCommandDecorations({ placeholder: 1, success: 1, error: 0 }); - await terminal.runCommandWithValue(TerminalCommandIdWithValue.WriteDataToTerminal, `\\r\\n${ft('C')}Failure\\r\\n${ft('D;1')}`); - await terminal.assertCommandDecorations({ placeholder: 0, success: 1, error: 1 }); - await terminal.runCommandWithValue(TerminalCommandIdWithValue.WriteDataToTerminal, `${ft('A')}Prompt> ${ft('B')}exitcode 1`); - await terminal.assertCommandDecorations({ placeholder: 1, success: 1, error: 1 }); - }); - }); - }); - }); -} - -function ft(data: string) { - return setTextParams(`133;${data}`); -} - -function vsc(data: string) { - return setTextParams(`633;${data}`); -} - -function setTextParams(data: string) { - return osc(`${data}\\x07`); -} - -function osc(data: string) { - return `\\x1b]${data}`; -} - -function csi(data: string) { - return `\\x1b[${data}`; -} diff --git a/test/smoke/src/areas/terminal/terminal-splitCwd.test.ts b/test/smoke/src/areas/terminal/terminal-splitCwd.test.ts deleted file mode 100644 index df6ab27eeec..00000000000 --- a/test/smoke/src/areas/terminal/terminal-splitCwd.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Application, Terminal, SettingsEditor } from '../../../../automation'; -import { setTerminalTestSettings } from './terminal-helpers'; - -export function setup(options?: { skipSuite: boolean }) { - (options?.skipSuite ? describe.skip : describe)('Terminal splitCwd', () => { - // Acquire automation API - let terminal: Terminal; - let settingsEditor: SettingsEditor; - before(async function () { - const app = this.app as Application; - terminal = app.workbench.terminal; - settingsEditor = app.workbench.settingsEditor; - await setTerminalTestSettings(app, [ - ['terminal.integrated.splitCwd', '"inherited"'] - ]); - }); - - after(async function () { - await settingsEditor.clearUserSettings(); - }); - - it('should inherit cwd when split and update the tab description - alt click', async () => { - await terminal.createTerminal(); - const cwd = 'test'; - await terminal.runCommandInTerminal(`mkdir ${cwd}`); - await terminal.runCommandInTerminal(`cd ${cwd}`); - const page = await terminal.getPage(); - page.keyboard.down('Alt'); - await terminal.clickSingleTab(); - page.keyboard.up('Alt'); - await terminal.assertTerminalGroups([[{ description: cwd }, { description: cwd }]]); - }); - }); -} diff --git a/test/smoke/src/areas/terminal/terminal-stickyScroll.test.ts b/test/smoke/src/areas/terminal/terminal-stickyScroll.test.ts deleted file mode 100644 index 495a3bf033f..00000000000 --- a/test/smoke/src/areas/terminal/terminal-stickyScroll.test.ts +++ /dev/null @@ -1,98 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Application, Terminal, SettingsEditor, TerminalCommandIdWithValue } from '../../../../automation'; -import { setTerminalTestSettings } from './terminal-helpers'; - -export function setup(options?: { skipSuite: boolean }) { - (options?.skipSuite ? describe.skip : describe)('Terminal stickyScroll', () => { - // Acquire automation API - let app: Application; - let terminal: Terminal; - let settingsEditor: SettingsEditor; - before(async function () { - app = this.app as Application; - terminal = app.workbench.terminal; - settingsEditor = app.workbench.settingsEditor; - await setTerminalTestSettings(app, [ - ['terminal.integrated.stickyScroll.enabled', 'true'] - ]); - }); - - after(async function () { - await settingsEditor.clearUserSettings(); - }); - - // A polling approach is used to avoid test flakiness. While it's not ideal that this - // occurs, the main purpose of the tests is to verify sticky scroll shows and updates, - // not edge case race conditions on terminal start up - async function checkCommandAndOutput( - command: string, - exitCode: number, - prompt: string = 'Prompt> ', - expectedLineCount: number = 1 - ): Promise { - const data = generateCommandAndOutput(prompt, command, exitCode); - await terminal.runCommandWithValue(TerminalCommandIdWithValue.WriteDataToTerminal, data); - // Verify line count - await app.code.waitForElements('.terminal-sticky-scroll .xterm-rows > *', true, e => e.length === expectedLineCount); - // Verify content - const element = await app.code.getElement('.terminal-sticky-scroll .xterm-rows'); - if ( - element && - // New lines don't come through in textContent - element.textContent.indexOf(`${prompt.replace(/\\r\\n/g, '')}${command}`) >= 0 - ) { - return; - } - throw new Error(`Failed for command ${command}, exitcode ${exitCode}, text content ${element?.textContent}`); - } - - beforeEach(async () => { - // Create the simplest system profile to get as little process interaction as possible - await terminal.createEmptyTerminal(); - }); - - it('should show sticky scroll when appropriate', async () => { - // Write prompt, fill viewport, finish command, print new prompt, verify sticky scroll - await checkCommandAndOutput('sticky scroll 1', 0); - - // And again with a failed command - await checkCommandAndOutput('sticky scroll 2', 1); - }); - - it('should support multi-line prompt', async () => { - // Standard multi-line prompt - await checkCommandAndOutput('sticky scroll 1', 0, "Multi-line\\r\\nPrompt> ", 2); - - // New line before prompt - await checkCommandAndOutput('sticky scroll 2', 0, "\\r\\nMulti-line Prompt> ", 1); - - // New line before multi-line prompt - await checkCommandAndOutput('sticky scroll 3', 0, "\\r\\nMulti-line\\r\\nPrompt> ", 2); - }); - }); -} - -function generateCommandAndOutput(prompt: string, command: string, exitCode: number): string { - return [ - `${vsc('A')}${prompt}${vsc('B')}${command}`, - `\\r\\n${vsc('C')}`, - `\\r\\ndata`.repeat(50), - `\\r\\n${vsc(`D;${exitCode}`)}`, - ].join(''); -} - -function vsc(data: string) { - return setTextParams(`633;${data}`); -} - -function setTextParams(data: string) { - return osc(`${data}\\x07`); -} - -function osc(data: string) { - return `\\x1b]${data}`; -} diff --git a/test/smoke/src/areas/terminal/terminal-tabs.test.ts b/test/smoke/src/areas/terminal/terminal-tabs.test.ts deleted file mode 100644 index 0a34a0dfe95..00000000000 --- a/test/smoke/src/areas/terminal/terminal-tabs.test.ts +++ /dev/null @@ -1,110 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Application, Terminal, TerminalCommandId, TerminalCommandIdWithValue, SettingsEditor } from '../../../../automation'; -import { setTerminalTestSettings } from './terminal-helpers'; - -export function setup(options?: { skipSuite: boolean }) { - (options?.skipSuite ? describe.skip : describe)('Terminal Tabs', () => { - // Acquire automation API - let terminal: Terminal; - let settingsEditor: SettingsEditor; - - before(async function () { - const app = this.app as Application; - terminal = app.workbench.terminal; - settingsEditor = app.workbench.settingsEditor; - await setTerminalTestSettings(app); - }); - - after(async function () { - await settingsEditor.clearUserSettings(); - }); - - it('clicking the plus button should create a terminal and display the tabs view showing no split decorations', async () => { - await terminal.createTerminal(); - await terminal.clickPlusButton(); - await terminal.assertTerminalGroups([[{}], [{}]]); - }); - - it('should rename the single tab', async () => { - await terminal.createTerminal(); - const name = 'my terminal name'; - await terminal.runCommandWithValue(TerminalCommandIdWithValue.Rename, name); - await terminal.assertSingleTab({ name }); - }); - - // DEBT: Flaky https://github.com/microsoft/vscode/issues/216564 - it.skip('should reset the tab name to the default value when no name is provided', async () => { - await terminal.createTerminal(); - const defaultName = await terminal.getSingleTabName(); - const name = 'my terminal name'; - await terminal.runCommandWithValue(TerminalCommandIdWithValue.Rename, name); - await terminal.assertSingleTab({ name }); - await terminal.runCommandWithValue(TerminalCommandIdWithValue.Rename, undefined); - await terminal.assertSingleTab({ name: defaultName }); - }); - - it('should rename the tab in the tabs list', async () => { - await terminal.createTerminal(); - await terminal.runCommand(TerminalCommandId.Split); - const name = 'my terminal 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.createTerminal(); - const page = await terminal.getPage(); - page.keyboard.down('Alt'); - await terminal.clickSingleTab(); - page.keyboard.up('Alt'); - 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 terminal.assertTerminalGroups([[{}]]); - }); - - it('should do nothing when join tabs is run with only split terminals', async () => { - await terminal.runCommand(TerminalCommandId.Show); - await terminal.runCommand(TerminalCommandId.Split); - await terminal.runCommand(TerminalCommandId.Join); - await terminal.assertTerminalGroups([[{}], [{}]]); - }); - - it('should join tabs when more than one non-split terminal', async () => { - await terminal.runCommand(TerminalCommandId.Show); - await terminal.createTerminal(); - await terminal.runCommand(TerminalCommandId.Join); - await terminal.assertTerminalGroups([[{}, {}]]); - }); - - it('should do nothing when unsplit tabs called with no splits', async () => { - await terminal.runCommand(TerminalCommandId.Show); - await terminal.createTerminal(); - await terminal.assertTerminalGroups([[{}], [{}]]); - await terminal.runCommand(TerminalCommandId.Unsplit); - await terminal.assertTerminalGroups([[{}], [{}]]); - }); - - it('should unsplit tabs', async () => { - await terminal.runCommand(TerminalCommandId.Show); - await terminal.runCommand(TerminalCommandId.Split); - await terminal.assertTerminalGroups([[{}, {}]]); - await terminal.runCommand(TerminalCommandId.Unsplit); - 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/areas/terminal/terminal.test.ts b/test/smoke/src/areas/terminal/terminal.test.ts deleted file mode 100644 index dc28a7c1190..00000000000 --- a/test/smoke/src/areas/terminal/terminal.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Application, Terminal, TerminalCommandId, Logger } from '../../../../automation'; -import { installAllHandlers } from '../../utils'; -import { setup as setupTerminalEditorsTests } from './terminal-editors.test'; -import { setup as setupTerminalInputTests } from './terminal-input.test'; -import { setup as setupTerminalPersistenceTests } from './terminal-persistence.test'; -import { setup as setupTerminalProfileTests } from './terminal-profiles.test'; -import { setup as setupTerminalTabsTests } from './terminal-tabs.test'; -import { setup as setupTerminalSplitCwdTests } from './terminal-splitCwd.test'; -import { setup as setupTerminalStickyScrollTests } from './terminal-stickyScroll.test'; -import { setup as setupTerminalShellIntegrationTests } from './terminal-shellIntegration.test'; - -export function setup(logger: Logger) { - describe('Terminal', function () { - - // Retry tests 3 times to minimize build failures due to any flakiness - this.retries(3); - - // Shared before/after handling - installAllHandlers(logger); - - let app: Application; - let terminal: Terminal; - before(async function () { - // Fetch terminal automation API - app = this.app as Application; - terminal = app.workbench.terminal; - }); - - afterEach(async () => { - // Kill all terminals between every test for a consistent testing environment - await terminal.runCommand(TerminalCommandId.KillAll); - }); - - // https://github.com/microsoft/vscode/issues/216564 - // The pty host can crash on Linux in smoke tests for an unknown reason. We need more user - // reports to investigate - setupTerminalEditorsTests({ skipSuite: process.platform === 'linux' }); - setupTerminalInputTests({ skipSuite: process.platform === 'linux' }); - setupTerminalPersistenceTests({ skipSuite: process.platform === 'linux' }); - setupTerminalProfileTests({ skipSuite: process.platform === 'linux' }); - setupTerminalTabsTests({ skipSuite: process.platform === 'linux' }); - setupTerminalShellIntegrationTests({ skipSuite: process.platform === 'linux' }); - setupTerminalStickyScrollTests({ skipSuite: process.platform === 'linux' }); - // https://github.com/microsoft/vscode/pull/141974 - // Windows is skipped here as well as it was never enabled from the start - setupTerminalSplitCwdTests({ skipSuite: process.platform === 'linux' || process.platform === 'win32' }); - }); -} diff --git a/test/smoke/src/areas/workbench/data-loss.test.ts b/test/smoke/src/areas/workbench/data-loss.test.ts deleted file mode 100644 index 9788813f225..00000000000 --- a/test/smoke/src/areas/workbench/data-loss.test.ts +++ /dev/null @@ -1,275 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { join } from 'path'; -import { Application, ApplicationOptions, Logger, Quality } from '../../../../automation'; -import { createApp, timeout, installDiagnosticsHandler, installAppAfterHandler, getRandomUserDataDir, suiteLogsPath, suiteCrashPath } from '../../utils'; - -export function setup(ensureStableCode: () => string | undefined, logger: Logger) { - describe('Data Loss (insiders -> insiders)', function () { - - // Double the timeout since these tests involve 2 startups - this.timeout(4 * 60 * 1000); - - let app: Application | undefined = undefined; - - // Shared before/after handling - installDiagnosticsHandler(logger, () => app); - installAppAfterHandler(() => app); - - it('verifies opened editors are restored', async function () { - app = createApp({ - ...this.defaultOptions, - logsPath: suiteLogsPath(this.defaultOptions, 'test_verifies_opened_editors_are_restored'), - crashesPath: suiteCrashPath(this.defaultOptions, 'test_verifies_opened_editors_are_restored') - }); - await app.start(); - - // Open 3 editors - await app.workbench.quickaccess.openFile(join(app.workspacePathOrFolder, 'bin', 'www')); - await app.workbench.quickaccess.runCommand('View: Keep Editor'); - await app.workbench.quickaccess.openFile(join(app.workspacePathOrFolder, 'app.js')); - await app.workbench.quickaccess.runCommand('View: Keep Editor'); - await app.workbench.editors.newUntitledFile(); - - await app.restart(); - - // Verify 3 editors are open - await app.workbench.editors.selectTab('Untitled-1'); - await app.workbench.editors.selectTab('app.js'); - await app.workbench.editors.selectTab('www'); - - await app.stop(); - app = undefined; - }); - - it('verifies editors can save and restore', async function () { - app = createApp({ - ...this.defaultOptions, - logsPath: suiteLogsPath(this.defaultOptions, 'test_verifies_editors_can_save_and_restore'), - crashesPath: suiteCrashPath(this.defaultOptions, 'test_verifies_editors_can_save_and_restore') - }); - await app.start(); - - const textToType = 'Hello, Code'; - - // open editor and type - await app.workbench.quickaccess.openFile(join(app.workspacePathOrFolder, 'app.js')); - await app.workbench.editor.waitForTypeInEditor('app.js', textToType); - await app.workbench.editors.waitForTab('app.js', true); - - // save - await app.workbench.editors.saveOpenedFile(); - await app.workbench.editors.waitForTab('app.js', false); - - // restart - await app.restart(); - - // verify contents - await app.workbench.editor.waitForEditorContents('app.js', contents => contents.indexOf(textToType) > -1); - - await app.stop(); - app = undefined; - }); - - it('verifies that "hot exit" works for dirty files (without delay)', function () { - return testHotExit.call(this, 'test_verifies_that_hot_exit_works_for_dirty_files_without_delay', undefined, undefined); - }); - - it('verifies that "hot exit" works for dirty files (with delay)', function () { - return testHotExit.call(this, 'test_verifies_that_hot_exit_works_for_dirty_files_with_delay', 2000, undefined); - }); - - it('verifies that auto save triggers on shutdown', function () { - return testHotExit.call(this, 'test_verifies_that_auto_save_triggers_on_shutdown', undefined, true); - }); - - async function testHotExit(this: import('mocha').Context, title: string, restartDelay: number | undefined, autoSave: boolean | undefined) { - app = createApp({ - ...this.defaultOptions, - logsPath: suiteLogsPath(this.defaultOptions, title), - crashesPath: suiteCrashPath(this.defaultOptions, title) - }); - await app.start(); - - if (autoSave) { - await app.workbench.settingsEditor.addUserSetting('files.autoSave', '"afterDelay"'); - } - - const textToTypeInUntitled = 'Hello from Untitled'; - - await app.workbench.editors.newUntitledFile(); - await app.workbench.editor.waitForTypeInEditor('Untitled-1', textToTypeInUntitled); - await app.workbench.editors.waitForTab('Untitled-1', true); - - const textToType = 'Hello, Code'; - await app.workbench.quickaccess.openFile(join(app.workspacePathOrFolder, 'readme.md')); - await app.workbench.editor.waitForTypeInEditor('readme.md', textToType); - await app.workbench.editors.waitForTab('readme.md', !autoSave); - - if (typeof restartDelay === 'number') { - // this is an OK use of a timeout in a smoke test: - // we want to simulate a user having typed into - // the editor and pausing for a moment before - // terminating - await timeout(restartDelay); - } - - await app.restart(); - - await app.workbench.editors.waitForTab('readme.md', !autoSave); - await app.workbench.editors.waitForTab('Untitled-1', true); - - await app.workbench.editors.selectTab('readme.md'); - await app.workbench.editor.waitForEditorContents('readme.md', contents => contents.indexOf(textToType) > -1); - - await app.workbench.editors.selectTab('Untitled-1'); - await app.workbench.editor.waitForEditorContents('Untitled-1', contents => contents.indexOf(textToTypeInUntitled) > -1); - - await app.stop(); - app = undefined; - } - }); - - describe('Data Loss (stable -> insiders)', function () { - - // Double the timeout since these tests involve 2 startups - this.timeout(4 * 60 * 1000); - - let insidersApp: Application | undefined = undefined; - let stableApp: Application | undefined = undefined; - - // Shared before/after handling - installDiagnosticsHandler(logger, () => insidersApp ?? stableApp); - installAppAfterHandler(() => insidersApp ?? stableApp, async () => stableApp?.stop()); - - it('verifies opened editors are restored', async function () { - const stableCodePath = ensureStableCode(); - if (!stableCodePath) { - this.skip(); - } - - // macOS: the first launch of stable Code will trigger - // additional checks in the OS (notarization validation) - // so it can take a very long time. as such we install - // a retry handler to make sure we do not fail as a - // consequence. - if (process.platform === 'darwin') { - this.retries(2); - } - - const userDataDir = getRandomUserDataDir(this.defaultOptions); - const logsPath = suiteLogsPath(this.defaultOptions, 'test_verifies_opened_editors_are_restored_from_stable'); - const crashesPath = suiteCrashPath(this.defaultOptions, 'test_verifies_opened_editors_are_restored_from_stable'); - - const stableOptions: ApplicationOptions = Object.assign({}, this.defaultOptions); - stableOptions.codePath = stableCodePath; - stableOptions.userDataDir = userDataDir; - stableOptions.quality = Quality.Stable; - stableOptions.logsPath = logsPath; - stableOptions.crashesPath = crashesPath; - - stableApp = new Application(stableOptions); - await stableApp.start(); - - // Open 3 editors - await stableApp.workbench.quickaccess.openFile(join(stableApp.workspacePathOrFolder, 'bin', 'www')); - await stableApp.workbench.quickaccess.runCommand('View: Keep Editor'); - await stableApp.workbench.quickaccess.openFile(join(stableApp.workspacePathOrFolder, 'app.js')); - await stableApp.workbench.quickaccess.runCommand('View: Keep Editor'); - await stableApp.workbench.editors.newUntitledFile(); - - await stableApp.stop(); - stableApp = undefined; - - const insiderOptions: ApplicationOptions = Object.assign({}, this.defaultOptions); - insiderOptions.userDataDir = userDataDir; - insiderOptions.logsPath = logsPath; - insiderOptions.crashesPath = crashesPath; - - insidersApp = new Application(insiderOptions); - await insidersApp.start(); - - // Verify 3 editors are open - await insidersApp.workbench.editors.selectTab('Untitled-1'); - await insidersApp.workbench.editors.selectTab('app.js'); - await insidersApp.workbench.editors.selectTab('www'); - - await insidersApp.stop(); - insidersApp = undefined; - }); - - it('verifies that "hot exit" works for dirty files (without delay)', async function () { - return testHotExit.call(this, `test_verifies_that_hot_exit_works_for_dirty_files_without_delay_from_stable`, undefined); - }); - - it('verifies that "hot exit" works for dirty files (with delay)', async function () { - return testHotExit.call(this, `test_verifies_that_hot_exit_works_for_dirty_files_with_delay_from_stable`, 2000); - }); - - async function testHotExit(this: import('mocha').Context, title: string, restartDelay: number | undefined) { - const stableCodePath = ensureStableCode(); - if (!stableCodePath) { - this.skip(); - } - - const userDataDir = getRandomUserDataDir(this.defaultOptions); - const logsPath = suiteLogsPath(this.defaultOptions, title); - const crashesPath = suiteCrashPath(this.defaultOptions, title); - - const stableOptions: ApplicationOptions = Object.assign({}, this.defaultOptions); - stableOptions.codePath = stableCodePath; - stableOptions.userDataDir = userDataDir; - stableOptions.quality = Quality.Stable; - stableOptions.logsPath = logsPath; - stableOptions.crashesPath = crashesPath; - - stableApp = new Application(stableOptions); - await stableApp.start(); - - const textToTypeInUntitled = 'Hello from Untitled'; - - await stableApp.workbench.editors.newUntitledFile(); - await stableApp.workbench.editor.waitForTypeInEditor('Untitled-1', textToTypeInUntitled); - await stableApp.workbench.editors.waitForTab('Untitled-1', true); - - const textToType = 'Hello, Code'; - await stableApp.workbench.quickaccess.openFile(join(stableApp.workspacePathOrFolder, 'readme.md')); - await stableApp.workbench.editor.waitForTypeInEditor('readme.md', textToType); - await stableApp.workbench.editors.waitForTab('readme.md', true); - - if (typeof restartDelay === 'number') { - // this is an OK use of a timeout in a smoke test - // we want to simulate a user having typed into - // the editor and pausing for a moment before - // terminating - await timeout(restartDelay); - } - - await stableApp.stop(); - stableApp = undefined; - - const insiderOptions: ApplicationOptions = Object.assign({}, this.defaultOptions); - insiderOptions.userDataDir = userDataDir; - insiderOptions.logsPath = logsPath; - insiderOptions.crashesPath = crashesPath; - - insidersApp = new Application(insiderOptions); - await insidersApp.start(); - - await insidersApp.workbench.editors.waitForTab('readme.md', true); - await insidersApp.workbench.editors.waitForTab('Untitled-1', true); - - await insidersApp.workbench.editors.selectTab('readme.md'); - await insidersApp.workbench.editor.waitForEditorContents('readme.md', contents => contents.indexOf(textToType) > -1); - - await insidersApp.workbench.editors.selectTab('Untitled-1'); - await insidersApp.workbench.editor.waitForEditorContents('Untitled-1', contents => contents.indexOf(textToTypeInUntitled) > -1); - - await insidersApp.stop(); - insidersApp = undefined; - } - }); -} diff --git a/test/smoke/src/areas/workbench/launch.test.ts b/test/smoke/src/areas/workbench/launch.test.ts deleted file mode 100644 index 25383a42329..00000000000 --- a/test/smoke/src/areas/workbench/launch.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { join } from 'path'; -import { Application, Logger } from '../../../../automation'; -import { installAllHandlers } from '../../utils'; - -export function setup(logger: Logger) { - describe('Launch', () => { - - // Shared before/after handling - installAllHandlers(logger, opts => ({ ...opts, userDataDir: join(opts.userDataDir, 'ø') })); - - it('verifies that application launches when user data directory has non-ascii characters', async function () { - const app = this.app as Application; - await app.workbench.explorer.openExplorerView(); - }); - }); -} diff --git a/test/smoke/src/areas/workbench/localization.test.ts b/test/smoke/src/areas/workbench/localization.test.ts deleted file mode 100644 index 12e49ce549e..00000000000 --- a/test/smoke/src/areas/workbench/localization.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Logger, Application } from '../../../../automation'; -import { installAllHandlers } from '../../utils'; - -export function setup(logger: Logger) { - - describe('Localization', () => { - // Shared before/after handling - installAllHandlers(logger); - - it('starts with "DE" locale and verifies title and viewlets text is in German', async function () { - const app = this.app as Application; - - await app.workbench.extensions.installExtension('ms-ceintl.vscode-language-pack-de', false); - await app.restart({ extraArgs: ['--locale=DE'] }); - - const result = await app.workbench.localization.getLocalizedStrings(); - const localeInfo = await app.workbench.localization.getLocaleInfo(); - - if (localeInfo.locale === undefined || localeInfo.locale.toLowerCase() !== 'de') { - throw new Error(`The requested locale for VS Code was not German. The received value is: ${localeInfo.locale === undefined ? 'not set' : localeInfo.locale}`); - } - - if (localeInfo.language.toLowerCase() !== 'de') { - throw new Error(`The UI language is not German. It is ${localeInfo.language}`); - } - - if (result.open.toLowerCase() !== 'öffnen' || result.close.toLowerCase() !== 'schließen' || result.find.toLowerCase() !== 'finden') { - throw new Error(`Received wrong German localized strings: ${JSON.stringify(result, undefined, 0)}`); - } - }); - }); -} diff --git a/test/smoke/src/utils.ts b/test/smoke/src/utils.ts deleted file mode 100644 index b7cda86919e..00000000000 --- a/test/smoke/src/utils.ts +++ /dev/null @@ -1,189 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Suite, Context } from 'mocha'; -import { dirname, join } from 'path'; -import { Application, ApplicationOptions, Logger } from '../../automation'; - -export function describeRepeat(n: number, description: string, callback: (this: Suite) => void): void { - for (let i = 0; i < n; i++) { - describe(`${description} (iteration ${i})`, callback); - } -} - -export function itRepeat(n: number, description: string, callback: (this: Context) => any): void { - for (let i = 0; i < n; i++) { - it(`${description} (iteration ${i})`, callback); - } -} - -export function installAllHandlers(logger: Logger, optionsTransform?: (opts: ApplicationOptions) => ApplicationOptions) { - installDiagnosticsHandler(logger); - installAppBeforeHandler(optionsTransform); - installAppAfterHandler(); -} - -export function installDiagnosticsHandler(logger: Logger, appFn?: () => Application | undefined) { - - // Before each suite - before(async function () { - const suiteTitle = this.currentTest?.parent?.title; - logger.log(''); - logger.log(`>>> Suite start: '${suiteTitle ?? 'unknown'}' <<<`); - logger.log(''); - }); - - // Before each test - beforeEach(async function () { - const testTitle = this.currentTest?.title; - logger.log(''); - logger.log(`>>> Test start: '${testTitle ?? 'unknown'}' <<<`); - logger.log(''); - - const app: Application = appFn?.() ?? this.app; - await app?.startTracing(testTitle ?? 'unknown'); - }); - - // After each test - afterEach(async function () { - const currentTest = this.currentTest; - if (!currentTest) { - return; - } - - const failed = currentTest.state === 'failed'; - const testTitle = currentTest.title; - logger.log(''); - if (failed) { - logger.log(`>>> !!! FAILURE !!! Test end: '${testTitle}' !!! FAILURE !!! <<<`); - } else { - logger.log(`>>> Test end: '${testTitle}' <<<`); - } - logger.log(''); - - const app: Application = appFn?.() ?? this.app; - // --- Start Positron --- - // state is undefined during retry - await app?.stopTracing(testTitle.replace(/[^a-z0-9\-]/ig, '_'), failed || (currentTest.state === undefined)); - // --- End Positron --- - }); -} - -let logsCounter = 1; -let crashCounter = 1; - -export function suiteLogsPath(options: ApplicationOptions, suiteName: string): string { - return join(dirname(options.logsPath), `${logsCounter++}_suite_${suiteName.replace(/[^a-z0-9\-]/ig, '_')}`); -} - -export function suiteCrashPath(options: ApplicationOptions, suiteName: string): string { - return join(dirname(options.crashesPath), `${crashCounter++}_suite_${suiteName.replace(/[^a-z0-9\-]/ig, '_')}`); -} - -function installAppBeforeHandler(optionsTransform?: (opts: ApplicationOptions) => ApplicationOptions) { - before(async function () { - const suiteName = this.test?.parent?.title ?? 'unknown'; - - this.app = createApp({ - ...this.defaultOptions, - logsPath: suiteLogsPath(this.defaultOptions, suiteName), - crashesPath: suiteCrashPath(this.defaultOptions, suiteName) - }, optionsTransform); - await this.app.start(); - }); -} - -export function installAppAfterHandler(appFn?: () => Application | undefined, joinFn?: () => Promise) { - after(async function () { - const app: Application = appFn?.() ?? this.app; - if (app) { - await app.stop(); - } - - if (joinFn) { - await joinFn(); - } - }); -} - -export function createApp(options: ApplicationOptions, optionsTransform?: (opts: ApplicationOptions) => ApplicationOptions): Application { - if (optionsTransform) { - options = optionsTransform({ ...options }); - } - - const app = new Application({ - ...options, - userDataDir: getRandomUserDataDir(options) - }); - - return app; -} - -export function getRandomUserDataDir(options: ApplicationOptions): string { - - // Pick a random user data dir suffix that is not - // too long to not run into max path length issues - // https://github.com/microsoft/vscode/issues/34988 - const userDataPathSuffix = [...Array(8)].map(() => Math.random().toString(36)[3]).join(''); - - return options.userDataDir.concat(`-${userDataPathSuffix}`); -} - -export function timeout(i: number) { - return new Promise(resolve => { - setTimeout(() => { - resolve(); - }, i); - }); -} - -export async function retryWithRestart(app: Application, testFn: () => Promise, retries = 3, timeoutMs = 20000): Promise { - let lastError: Error | undefined = undefined; - for (let i = 0; i < retries; i++) { - const result = await Promise.race([ - testFn().then(() => true, error => { - lastError = error; - return false; - }), - timeout(timeoutMs).then(() => false) - ]); - - if (result) { - return; - } - - await app.restart(); - } - - throw lastError ?? new Error('retryWithRestart failed with an unknown error'); -} - -export interface ITask { - (): T; -} - -export async function retry(task: ITask>, delay: number, retries: number, onBeforeRetry?: () => Promise): Promise { - let lastError: Error | undefined; - - for (let i = 0; i < retries; i++) { - try { - if (i > 0 && typeof onBeforeRetry === 'function') { - try { - await onBeforeRetry(); - } catch (error) { - console.warn(`onBeforeRetry failed with: ${error}`); - } - } - - return await task(); - } catch (error) { - lastError = error as Error; - - await timeout(delay); - } - } - - throw lastError; -} diff --git a/test/smoke/tsconfig.json b/test/smoke/tsconfig.json deleted file mode 100644 index 02cd3a44d7c..00000000000 --- a/test/smoke/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "noImplicitAny": false, - "removeComments": false, - "preserveConstEnums": true, - "target": "es2020", - "strict": true, - "noUnusedParameters": false, - "noUnusedLocals": true, - "outDir": "out", - "sourceMap": true, - "skipLibCheck": true, - "lib": [ - "esnext", // for #201187 - "dom" - ] - }, - "exclude": [ - "node_modules" - ] -}