diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 2f5a931e850..2a67103d2b9 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -50,7 +50,6 @@ const vscodeEntryPoints = _.flatten([ const vscodeResources = [ 'out-build/main.js', 'out-build/cli.js', - 'out-build/driver.js', 'out-build/bootstrap.js', 'out-build/bootstrap-fork.js', 'out-build/bootstrap-amd.js', diff --git a/src/buildfile.js b/src/buildfile.js index 4a174a51436..8c30339da6e 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -76,7 +76,6 @@ exports.code = [ createModuleDescription('vs/code/node/cliProcessMain', ['vs/code/node/cli']), createModuleDescription('vs/code/electron-sandbox/issue/issueReporterMain'), createModuleDescription('vs/code/electron-browser/sharedProcess/sharedProcessMain'), - createModuleDescription('vs/platform/driver/node/driver'), createModuleDescription('vs/code/electron-sandbox/processExplorer/processExplorerMain') ]; diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index f48212b5add..eec246062ff 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -37,7 +37,6 @@ import { ElectronExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/el import { IDiagnosticsService } from 'vs/platform/diagnostics/common/diagnostics'; import { DiagnosticsMainService, IDiagnosticsMainService } from 'vs/platform/diagnostics/electron-main/diagnosticsMainService'; import { DialogMainService, IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogMainService'; -import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; import { IEncryptionMainService } from 'vs/platform/encryption/common/encryptionService'; import { EncryptionMainService } from 'vs/platform/encryption/node/encryptionMainService'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; @@ -523,14 +522,6 @@ export class CodeApplication extends Disposable { // Services const appInstantiationService = await this.initServices(machineId, sharedProcess, sharedProcessReady); - // Create driver - if (this.environmentMainService.driverHandle) { - const server = await serveDriver(mainProcessElectronServer, this.environmentMainService.driverHandle, appInstantiationService); - - this.logService.info('Driver started at:', this.environmentMainService.driverHandle); - this._register(server); - } - // Setup Auth Handler this._register(appInstantiationService.createInstance(ProxyAuthHandler)); diff --git a/src/vs/platform/driver/common/driver.ts b/src/vs/platform/driver/common/driver.ts index 8a573487f6d..1c593be20d1 100644 --- a/src/vs/platform/driver/common/driver.ts +++ b/src/vs/platform/driver/common/driver.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; - // !! Do not remove the following START and END markers, they are parsed by the smoketest build //*START @@ -19,14 +17,7 @@ export interface IElement { } export interface ILocaleInfo { - /** - * The UI language used. - */ language: string; - - /** - * The requested locale - */ locale?: string; } @@ -36,27 +27,6 @@ export interface ILocalizedStrings { find: string; } -export interface IDriver { - readonly _serviceBrand: undefined; - - getWindowIds(): Promise; - startTracing(windowId: number, name: string): Promise; - stopTracing(windowId: number, name: string, persist: boolean): Promise; - exitApplication(): Promise; - dispatchKeybinding(windowId: number, keybinding: string): Promise; - click(windowId: number, selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise; - setValue(windowId: number, selector: string, text: string): Promise; - getTitle(windowId: number): Promise; - isActiveElement(windowId: number, selector: string): Promise; - getElements(windowId: number, selector: string, recursive?: boolean): Promise; - getElementXY(windowId: number, selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number }>; - typeInEditor(windowId: number, selector: string, text: string): Promise; - getTerminalBuffer(windowId: number, selector: string): Promise; - writeInTerminal(windowId: number, selector: string, text: string): Promise; - getLocaleInfo(windowId: number): Promise; - getLocalizedStrings(windowId: number): Promise; -} - export interface IWindowDriver { click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise; setValue(selector: string, text: string): Promise; @@ -72,11 +42,3 @@ export interface IWindowDriver { exitApplication(): Promise; } //*END - -export const ID = 'driverService'; -export const IDriver = createDecorator(ID); - -export interface IWindowDriverRegistry { - registerWindowDriver(windowId: number): Promise; - reloadWindowDriver(windowId: number): Promise; -} diff --git a/src/vs/platform/driver/common/driverIpc.ts b/src/vs/platform/driver/common/driverIpc.ts deleted file mode 100644 index dc095ee06fc..00000000000 --- a/src/vs/platform/driver/common/driverIpc.ts +++ /dev/null @@ -1,105 +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 { Event } from 'vs/base/common/event'; -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IElement, ILocaleInfo, ILocalizedStrings as ILocalizedStrings, IWindowDriver, IWindowDriverRegistry } from 'vs/platform/driver/common/driver'; - -export class WindowDriverChannel implements IServerChannel { - - constructor(private driver: IWindowDriver) { } - - listen(_: unknown, event: string): Event { - throw new Error(`No event found: ${event}`); - } - - call(_: unknown, command: string, arg?: any): Promise { - switch (command) { - case 'click': return this.driver.click(arg[0], arg[1], arg[2]); - case 'setValue': return this.driver.setValue(arg[0], arg[1]); - case 'getTitle': return this.driver.getTitle(); - case 'isActiveElement': return this.driver.isActiveElement(arg); - case 'getElements': return this.driver.getElements(arg[0], arg[1]); - case 'getElementXY': return this.driver.getElementXY(arg[0], arg[1], arg[2]); - case 'typeInEditor': return this.driver.typeInEditor(arg[0], arg[1]); - case 'getTerminalBuffer': return this.driver.getTerminalBuffer(arg); - case 'writeInTerminal': return this.driver.writeInTerminal(arg[0], arg[1]); - case 'getLocaleInfo': return this.driver.getLocaleInfo(); - case 'getLocalizedStrings': return this.driver.getLocalizedStrings(); - } - - throw new Error(`Call not found: ${command}`); - } -} - -export class WindowDriverChannelClient implements IWindowDriver { - - declare readonly _serviceBrand: undefined; - - constructor(private channel: IChannel) { } - - click(selector: string, xoffset?: number, yoffset?: number): Promise { - return this.channel.call('click', [selector, xoffset, yoffset]); - } - - setValue(selector: string, text: string): Promise { - return this.channel.call('setValue', [selector, text]); - } - - getTitle(): Promise { - return this.channel.call('getTitle'); - } - - isActiveElement(selector: string): Promise { - return this.channel.call('isActiveElement', selector); - } - - getElements(selector: string, recursive: boolean): Promise { - return this.channel.call('getElements', [selector, recursive]); - } - - getElementXY(selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number }> { - return this.channel.call('getElementXY', [selector, xoffset, yoffset]); - } - - typeInEditor(selector: string, text: string): Promise { - return this.channel.call('typeInEditor', [selector, text]); - } - - getTerminalBuffer(selector: string): Promise { - return this.channel.call('getTerminalBuffer', selector); - } - - writeInTerminal(selector: string, text: string): Promise { - return this.channel.call('writeInTerminal', [selector, text]); - } - - getLocaleInfo(): Promise { - return this.channel.call('getLocaleInfo'); - } - - getLocalizedStrings(): Promise { - return this.channel.call('getLocalizedStrings'); - } - - exitApplication(): Promise { - return this.channel.call('exitApplication'); - } -} - -export class WindowDriverRegistryChannelClient implements IWindowDriverRegistry { - - declare readonly _serviceBrand: undefined; - - constructor(private channel: IChannel) { } - - registerWindowDriver(windowId: number): Promise { - return this.channel.call('registerWindowDriver', windowId); - } - - reloadWindowDriver(windowId: number): Promise { - return this.channel.call('reloadWindowDriver', windowId); - } -} diff --git a/src/vs/platform/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts deleted file mode 100644 index 0e6956956b8..00000000000 --- a/src/vs/platform/driver/electron-main/driver.ts +++ /dev/null @@ -1,244 +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 { timeout } from 'vs/base/common/async'; -import { Emitter, Event } from 'vs/base/common/event'; -import { KeybindingParser } from 'vs/base/common/keybindingParser'; -import { KeyCode } from 'vs/base/common/keyCodes'; -import { SimpleKeybinding, ScanCodeBinding } from 'vs/base/common/keybindings'; -import { combinedDisposable, IDisposable } from 'vs/base/common/lifecycle'; -import { OS } from 'vs/base/common/platform'; -import { IPCServer, StaticRouter } from 'vs/base/parts/ipc/common/ipc'; -import { serve as serveNet } from 'vs/base/parts/ipc/node/ipc.net'; -import { IDriver, IElement, ILocaleInfo, ILocalizedStrings, IWindowDriver, IWindowDriverRegistry } from 'vs/platform/driver/common/driver'; -import { WindowDriverChannelClient } from 'vs/platform/driver/common/driverIpc'; -import { DriverChannel, WindowDriverRegistryChannel } from 'vs/platform/driver/node/driver'; -import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; -import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; -import { IFileService } from 'vs/platform/files/common/files'; -import { URI } from 'vs/base/common/uri'; -import { join } from 'vs/base/common/path'; -import { VSBuffer } from 'vs/base/common/buffer'; -import { ILogService } from 'vs/platform/log/common/log'; - -function isSilentKeyCode(keyCode: KeyCode) { - return keyCode < KeyCode.Digit0; -} - -export class Driver implements IDriver, IWindowDriverRegistry { - - declare readonly _serviceBrand: undefined; - - private registeredWindowIds = new Set(); - private reloadingWindowIds = new Set(); - private readonly onDidReloadingChange = new Emitter(); - - constructor( - private windowServer: IPCServer, - @IWindowsMainService private readonly windowsMainService: IWindowsMainService, - @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, - @IFileService private readonly fileService: IFileService, - @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, - @ILogService private readonly logService: ILogService - ) { } - - async registerWindowDriver(windowId: number): Promise { - this.logService.info(`[driver] registerWindowDriver(${windowId})`); - - this.registeredWindowIds.add(windowId); - this.reloadingWindowIds.delete(windowId); - this.onDidReloadingChange.fire(); - } - - async reloadWindowDriver(windowId: number): Promise { - this.logService.info(`[driver] reloadWindowDriver(${windowId})`); - - this.reloadingWindowIds.add(windowId); - } - - async getWindowIds(): Promise { - const windowIds = this.windowsMainService.getWindows() - .map(window => window.id) - .filter(windowId => this.registeredWindowIds.has(windowId) && !this.reloadingWindowIds.has(windowId)); - - return windowIds; - } - - - async startTracing(windowId: number, name: string): Promise { - // ignore - tracing is not implemented yet - } - - async stopTracing(windowId: number, name: string, persist: boolean): Promise { - if (!persist) { - return; - } - - const raw = await this.capturePage(windowId); - const buffer = Buffer.from(raw, 'base64'); - - await this.fileService.writeFile(URI.file(join(this.environmentMainService.logsPath, `${name}.png`)), VSBuffer.wrap(buffer)); - } - - private async capturePage(windowId: number): Promise { - const window = this.windowsMainService.getWindowById(windowId) ?? this.windowsMainService.getLastActiveWindow(); // fallback to active window to ensure we capture window - if (!window?.win) { - throw new Error('Invalid window'); - } - - const webContents = window.win.webContents; - const image = await webContents.capturePage(); - return image.toPNG().toString('base64'); - } - - async exitApplication(): Promise { - this.logService.info(`[driver] exitApplication()`); - - await this.lifecycleMainService.quit(); - } - - async dispatchKeybinding(windowId: number, keybinding: string): Promise { - await this.whenUnfrozen(windowId); - - const parts = KeybindingParser.parseUserBinding(keybinding); - - for (let part of parts) { - await this._dispatchKeybinding(windowId, part); - } - } - - private async _dispatchKeybinding(windowId: number, keybinding: SimpleKeybinding | ScanCodeBinding): Promise { - if (keybinding instanceof ScanCodeBinding) { - throw new Error('ScanCodeBindings not supported'); - } - - const window = this.windowsMainService.getWindowById(windowId); - if (!window?.win) { - throw new Error('Invalid window'); - } - const webContents = window.win.webContents; - const noModifiedKeybinding = new SimpleKeybinding(false, false, false, false, keybinding.keyCode); - const resolvedKeybinding = new USLayoutResolvedKeybinding(noModifiedKeybinding.toChord(), OS); - const keyCode = resolvedKeybinding.getElectronAccelerator(); - - const modifiers: string[] = []; - - if (keybinding.ctrlKey) { - modifiers.push('ctrl'); - } - - if (keybinding.metaKey) { - modifiers.push('meta'); - } - - if (keybinding.shiftKey) { - modifiers.push('shift'); - } - - if (keybinding.altKey) { - modifiers.push('alt'); - } - - webContents.sendInputEvent({ type: 'keyDown', keyCode, modifiers } as any); - - if (!isSilentKeyCode(keybinding.keyCode)) { - webContents.sendInputEvent({ type: 'char', keyCode, modifiers } as any); - } - - webContents.sendInputEvent({ type: 'keyUp', keyCode, modifiers } as any); - - await timeout(100); - } - - async click(windowId: number, selector: string, xoffset?: number, yoffset?: number): Promise { - const windowDriver = await this.getWindowDriver(windowId); - await windowDriver.click(selector, xoffset, yoffset); - } - - async setValue(windowId: number, selector: string, text: string): Promise { - const windowDriver = await this.getWindowDriver(windowId); - await windowDriver.setValue(selector, text); - } - - async getTitle(windowId: number): Promise { - const windowDriver = await this.getWindowDriver(windowId); - return await windowDriver.getTitle(); - } - - async isActiveElement(windowId: number, selector: string): Promise { - const windowDriver = await this.getWindowDriver(windowId); - return await windowDriver.isActiveElement(selector); - } - - async getElements(windowId: number, selector: string, recursive: boolean): Promise { - const windowDriver = await this.getWindowDriver(windowId); - return await windowDriver.getElements(selector, recursive); - } - - async getElementXY(windowId: number, selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number }> { - const windowDriver = await this.getWindowDriver(windowId); - return await windowDriver.getElementXY(selector, xoffset, yoffset); - } - - async typeInEditor(windowId: number, selector: string, text: string): Promise { - const windowDriver = await this.getWindowDriver(windowId); - await windowDriver.typeInEditor(selector, text); - } - - async getTerminalBuffer(windowId: number, selector: string): Promise { - const windowDriver = await this.getWindowDriver(windowId); - return await windowDriver.getTerminalBuffer(selector); - } - - async writeInTerminal(windowId: number, selector: string, text: string): Promise { - const windowDriver = await this.getWindowDriver(windowId); - await windowDriver.writeInTerminal(selector, text); - } - - async getLocaleInfo(windowId: number): Promise { - const windowDriver = await this.getWindowDriver(windowId); - return await windowDriver.getLocaleInfo(); - } - - async getLocalizedStrings(windowId: number): Promise { - const windowDriver = await this.getWindowDriver(windowId); - return await windowDriver.getLocalizedStrings(); - } - - private async getWindowDriver(windowId: number): Promise { - await this.whenUnfrozen(windowId); - - const id = `window:${windowId}`; - const router = new StaticRouter(ctx => ctx === id); - const windowDriverChannel = this.windowServer.getChannel('windowDriver', router); - return new WindowDriverChannelClient(windowDriverChannel); - } - - private async whenUnfrozen(windowId: number): Promise { - while (this.reloadingWindowIds.has(windowId)) { - await Event.toPromise(this.onDidReloadingChange.event); - } - } -} - -export async function serve( - windowServer: IPCServer, - handle: string, - instantiationService: IInstantiationService -): Promise { - const driver = instantiationService.createInstance(Driver, windowServer); - - const windowDriverRegistryChannel = new WindowDriverRegistryChannel(driver); - windowServer.registerChannel('windowDriverRegistry', windowDriverRegistryChannel); - - const server = await serveNet(handle); - const channel = new DriverChannel(driver); - server.registerChannel('driver', channel); - - return combinedDisposable(server, windowServer); -} diff --git a/src/vs/platform/driver/electron-sandbox/driver.ts b/src/vs/platform/driver/electron-sandbox/driver.ts index b398749230b..fb9b9a596ff 100644 --- a/src/vs/platform/driver/electron-sandbox/driver.ts +++ b/src/vs/platform/driver/electron-sandbox/driver.ts @@ -3,13 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { timeout } from 'vs/base/common/async'; -import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { BrowserWindowDriver } from 'vs/platform/driver/browser/driver'; -import { WindowDriverChannel, WindowDriverRegistryChannelClient } from 'vs/platform/driver/common/driverIpc'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; -import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; interface INativeWindowDriverHelper { exitApplication(): Promise; @@ -29,50 +23,3 @@ class NativeWindowDriver extends BrowserWindowDriver { export function registerWindowDriver(helper: INativeWindowDriverHelper): void { Object.assign(window, { driver: new NativeWindowDriver(helper) }); } - -class LegacyNativeWindowDriver extends BrowserWindowDriver { - - constructor( - @INativeHostService private readonly nativeHostService: INativeHostService - ) { - super(); - } - - override click(selector: string, xoffset?: number, yoffset?: number): Promise { - const offset = typeof xoffset === 'number' && typeof yoffset === 'number' ? { x: xoffset, y: yoffset } : undefined; - - return this.doClick(selector, 1, offset); - } - - private async doClick(selector: string, clickCount: number, offset?: { x: number; y: number }): Promise { - const { x, y } = await this._getElementXY(selector, offset); - - await this.nativeHostService.sendInputEvent({ type: 'mouseDown', x, y, button: 'left', clickCount } as any); - await timeout(10); - - await this.nativeHostService.sendInputEvent({ type: 'mouseUp', x, y, button: 'left', clickCount } as any); - await timeout(100); - } -} - -/** - * Old school window driver that is implemented by us - * from the main process. - * - * @deprecated - */ -export async function registerLegacyWindowDriver(accessor: ServicesAccessor, windowId: number): Promise { - const instantiationService = accessor.get(IInstantiationService); - const mainProcessService = accessor.get(IMainProcessService); - - const windowDriver = instantiationService.createInstance(LegacyNativeWindowDriver); - const windowDriverChannel = new WindowDriverChannel(windowDriver); - mainProcessService.registerChannel('windowDriver', windowDriverChannel); - - const windowDriverRegistryChannel = mainProcessService.getChannel('windowDriverRegistry'); - const windowDriverRegistry = new WindowDriverRegistryChannelClient(windowDriverRegistryChannel); - - await windowDriverRegistry.registerWindowDriver(windowId); - - return toDisposable(() => windowDriverRegistry.reloadWindowDriver(windowId)); -} diff --git a/src/vs/platform/driver/node/driver.ts b/src/vs/platform/driver/node/driver.ts deleted file mode 100644 index 6ce00b50e53..00000000000 --- a/src/vs/platform/driver/node/driver.ts +++ /dev/null @@ -1,138 +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 { Event } from 'vs/base/common/event'; -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { Client } from 'vs/base/parts/ipc/common/ipc.net'; -import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net'; -import { IDriver, IElement, ILocaleInfo, ILocalizedStrings, IWindowDriverRegistry } from 'vs/platform/driver/common/driver'; - -export class DriverChannel implements IServerChannel { - - constructor(private driver: IDriver) { } - - listen(_: unknown, event: string): Event { - throw new Error('No event found'); - } - - call(_: unknown, command: string, arg?: any): Promise { - switch (command) { - case 'getWindowIds': return this.driver.getWindowIds(); - case 'startTracing': return this.driver.startTracing(arg[0], arg[1]); - case 'stopTracing': return this.driver.stopTracing(arg[0], arg[1], arg[2]); - case 'exitApplication': return this.driver.exitApplication(); - case 'dispatchKeybinding': return this.driver.dispatchKeybinding(arg[0], arg[1]); - case 'click': return this.driver.click(arg[0], arg[1], arg[2], arg[3]); - case 'setValue': return this.driver.setValue(arg[0], arg[1], arg[2]); - case 'getTitle': return this.driver.getTitle(arg[0]); - case 'isActiveElement': return this.driver.isActiveElement(arg[0], arg[1]); - case 'getElements': return this.driver.getElements(arg[0], arg[1], arg[2]); - case 'getElementXY': return this.driver.getElementXY(arg[0], arg[1], arg[2]); - case 'typeInEditor': return this.driver.typeInEditor(arg[0], arg[1], arg[2]); - case 'getTerminalBuffer': return this.driver.getTerminalBuffer(arg[0], arg[1]); - case 'writeInTerminal': return this.driver.writeInTerminal(arg[0], arg[1], arg[2]); - case 'getLocaleInfo': return this.driver.getLocaleInfo(arg); - case 'getLocalizedStrings': return this.driver.getLocalizedStrings(arg); - } - - throw new Error(`Call not found: ${command}`); - } -} - -export class DriverChannelClient implements IDriver { - - declare readonly _serviceBrand: undefined; - - constructor(private channel: IChannel) { } - - getWindowIds(): Promise { - return this.channel.call('getWindowIds'); - } - - startTracing(windowId: number, name: string): Promise { - return this.channel.call('startTracing', [windowId, name]); - } - - stopTracing(windowId: number, name: string, persist: boolean): Promise { - return this.channel.call('stopTracing', [windowId, name, persist]); - } - - exitApplication(): Promise { - return this.channel.call('exitApplication'); - } - - dispatchKeybinding(windowId: number, keybinding: string): Promise { - return this.channel.call('dispatchKeybinding', [windowId, keybinding]); - } - - click(windowId: number, selector: string, xoffset: number | undefined, yoffset: number | undefined): Promise { - return this.channel.call('click', [windowId, selector, xoffset, yoffset]); - } - - setValue(windowId: number, selector: string, text: string): Promise { - return this.channel.call('setValue', [windowId, selector, text]); - } - - getTitle(windowId: number): Promise { - return this.channel.call('getTitle', [windowId]); - } - - isActiveElement(windowId: number, selector: string): Promise { - return this.channel.call('isActiveElement', [windowId, selector]); - } - - getElements(windowId: number, selector: string, recursive: boolean): Promise { - return this.channel.call('getElements', [windowId, selector, recursive]); - } - - getElementXY(windowId: number, selector: string, xoffset: number | undefined, yoffset: number | undefined): Promise<{ x: number; y: number }> { - return this.channel.call('getElementXY', [windowId, selector, xoffset, yoffset]); - } - - typeInEditor(windowId: number, selector: string, text: string): Promise { - return this.channel.call('typeInEditor', [windowId, selector, text]); - } - - getTerminalBuffer(windowId: number, selector: string): Promise { - return this.channel.call('getTerminalBuffer', [windowId, selector]); - } - - writeInTerminal(windowId: number, selector: string, text: string): Promise { - return this.channel.call('writeInTerminal', [windowId, selector, text]); - } - - getLocaleInfo(windowId: number): Promise { - return this.channel.call('getLocaleInfo', windowId); - } - - getLocalizedStrings(windowId: number): Promise { - return this.channel.call('getLocalizedStrings', windowId); - } -} - -export class WindowDriverRegistryChannel implements IServerChannel { - - constructor(private registry: IWindowDriverRegistry) { } - - listen(_: unknown, event: string): Event { - throw new Error(`Event not found: ${event}`); - } - - call(_: unknown, command: string, arg?: any): Promise { - switch (command) { - case 'registerWindowDriver': return this.registry.registerWindowDriver(arg); - case 'reloadWindowDriver': return this.registry.reloadWindowDriver(arg); - } - - throw new Error(`Call not found: ${command}`); - } -} - -export async function connect(handle: string): Promise<{ client: Client; driver: IDriver }> { - const client = await connectNet(handle, 'driverClient'); - const channel = client.getChannel('driver'); - const driver = new DriverChannelClient(channel); - return { client, driver }; -} diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index ccac10545b5..b2b4500a614 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -79,10 +79,6 @@ export interface NativeParsedArgs { 'max-memory'?: string; 'file-write'?: boolean; 'file-chmod'?: boolean; - /** - * @deprecated use `enable-smoke-test-driver` - */ - 'driver'?: string; 'enable-smoke-test-driver'?: boolean; 'remote'?: string; 'force'?: boolean; diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 28a690c69bb..56531a3a784 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -132,9 +132,6 @@ export interface INativeEnvironmentService extends IEnvironmentService { extensionsDownloadPath: string; builtinExtensionsPath: string; - // --- smoke test support - driverHandle?: string; - // --- use keytar for credentials disableKeytar?: boolean; diff --git a/src/vs/platform/environment/common/environmentService.ts b/src/vs/platform/environment/common/environmentService.ts index ac02eadcbdb..fcea3f58023 100644 --- a/src/vs/platform/environment/common/environmentService.ts +++ b/src/vs/platform/environment/common/environmentService.ts @@ -230,8 +230,6 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron get crashReporterId(): string | undefined { return this.args['crash-reporter-id']; } get crashReporterDirectory(): string | undefined { return this.args['crash-reporter-directory']; } - get driverHandle(): string | undefined { return this.args['driver']; } - @memoize get telemetryLogResource(): URI { return URI.file(join(this.logsPath, 'telemetry.log')); } get disableTelemetry(): boolean { return !!this.args['disable-telemetry']; } diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 1d7aadc1f14..062ce48a580 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -98,7 +98,6 @@ export const OPTIONS: OptionDescriptions> = { 'inspect-brk-search': { type: 'string', deprecates: ['debugBrkSearch'] }, 'export-default-configuration': { type: 'string' }, 'install-source': { type: 'string' }, - 'driver': { type: 'string' }, 'enable-smoke-test-driver': { type: 'boolean' }, 'logExtensionHostCommunication': { type: 'boolean' }, 'skip-release-notes': { type: 'boolean' }, diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index 77171dabff1..aca98a776db 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -545,7 +545,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } // If we run smoke tests, we never want to show a blocking dialog - if (this.environmentMainService.driverHandle) { + if (this.environmentMainService.args['enable-smoke-test-driver']) { this.destroyWindow(false); return; } diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 8617e328264..dc10761c214 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -63,7 +63,7 @@ import { whenEditorClosed } from 'vs/workbench/browser/editor'; import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { registerLegacyWindowDriver, registerWindowDriver } from 'vs/platform/driver/electron-sandbox/driver'; +import { registerWindowDriver } from 'vs/platform/driver/electron-sandbox/driver'; export class NativeWindow extends Disposable { @@ -640,25 +640,18 @@ export class NativeWindow extends Disposable { } // Smoke Test Driver - this.setupDriver(); + if (this.environmentService.enableSmokeTestDriver) { + this.setupDriver(); + } } private setupDriver(): void { - - // Modern Driver - if (this.environmentService.enableSmokeTestDriver) { - const that = this; - registerWindowDriver({ - async exitApplication(): Promise { - return that.nativeHostService.quit(); - } - }); - } - - // Legacy Driver (TODO@bpasero remove me eventually) - else if (this.environmentService.args.driver) { - this.instantiationService.invokeFunction(async accessor => this._register(await registerLegacyWindowDriver(accessor, this.nativeHostService.windowId))); - } + const that = this; + registerWindowDriver({ + async exitApplication(): Promise { + return that.nativeHostService.quit(); + } + }); } private setupOpenHandlers(): void { diff --git a/test/automation/package.json b/test/automation/package.json index 7a7b3c5a560..96397298161 100644 --- a/test/automation/package.json +++ b/test/automation/package.json @@ -9,13 +9,11 @@ "main": "./out/index.js", "private": true, "scripts": { - "compile": "npm run copy-driver && npm run copy-driver-definition && node ../../node_modules/typescript/bin/tsc", - "watch": "npm-run-all -lp watch-driver watch-driver-definition watch-tsc", + "compile": "npm run copy-driver-definition && node ../../node_modules/typescript/bin/tsc", + "watch": "npm-run-all -lp watch-driver-definition watch-tsc", "watch-tsc": "node ../../node_modules/typescript/bin/tsc --watch --preserveWatchOutput", - "copy-driver": "cpx src/driver.js out/", - "watch-driver": "cpx src/driver.js out/ -w", "copy-driver-definition": "node tools/copy-driver-definition.js", - "watch-driver-definition": "watch \"node tools/copy-driver-definition.js\" ../../src/vs/platform/driver/node", + "watch-driver-definition": "watch \"node tools/copy-driver-definition.js\"", "copy-package-version": "node tools/copy-package-version.js", "prepublishOnly": "npm run copy-package-version" }, diff --git a/test/automation/src/application.ts b/test/automation/src/application.ts index 23c9e630ea2..8914ed55982 100644 --- a/test/automation/src/application.ts +++ b/test/automation/src/application.ts @@ -16,8 +16,7 @@ export const enum Quality { export interface ApplicationOptions extends LaunchOptions { quality: Quality; - workspacePath: string; - waitTime: number; + readonly workspacePath: string; } export class Application { @@ -49,10 +48,6 @@ export class Application { return !!this.options.web; } - get legacy(): boolean { - return !!this.options.legacy; - } - private _workspacePathOrFolder: string; get workspacePathOrFolder(): string { return this._workspacePathOrFolder; @@ -118,9 +113,6 @@ export class Application { private async checkWindowReady(code: Code): Promise { - // This is legacy and will be removed when our old driver removes - await code.waitForWindowIds(ids => ids.length > 0); - // We need a rendered workbench await this.checkWorkbenchReady(code); diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index f50d69e83fa..5e6b52c7d73 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -6,14 +6,14 @@ import { join } from 'path'; import * as os from 'os'; import * as cp from 'child_process'; -import { IDriver, IDisposable, IElement, Thenable, ILocalizedStrings, ILocaleInfo } from './driver'; -import { launch as launchElectron } from './electron'; +import { IElement, ILocalizedStrings, ILocaleInfo } from './driver'; import { launch as launchPlaywrightBrowser } from './playwrightBrowser'; import { launch as launchPlaywrightElectron } from './playwrightElectron'; import { Logger, measureAndLog } from './logger'; import { copyExtension } from './extensions'; import * as treekill from 'tree-kill'; import { teardown } from './processes'; +import { PlaywrightDriver } from './playwrightDriver'; const rootPath = join(__dirname, '../../..'); @@ -28,7 +28,6 @@ export interface LaunchOptions { readonly extraArgs?: string[]; readonly remote?: boolean; readonly web?: boolean; - readonly legacy?: boolean; readonly tracing?: boolean; readonly headless?: boolean; readonly browser?: 'chromium' | 'webkit' | 'firefox'; @@ -80,37 +79,27 @@ export async function launch(options: LaunchOptions): Promise { // Browser smoke tests if (options.web) { - const { serverProcess, client, driver } = await measureAndLog(launchPlaywrightBrowser(options), 'launch playwright (browser)', options.logger); + const { serverProcess, driver } = await measureAndLog(launchPlaywrightBrowser(options), 'launch playwright (browser)', options.logger); registerInstance(serverProcess, options.logger, 'server'); - return new Code(client, driver, options.logger, serverProcess); + return new Code(driver, options.logger, serverProcess); } // Electron smoke tests (playwright) - else if (!options.legacy) { - const { electronProcess, client, driver } = await measureAndLog(launchPlaywrightElectron(options), 'launch playwright (electron)', options.logger); - registerInstance(electronProcess, options.logger, 'electron'); - - return new Code(client, driver, options.logger, electronProcess); - } - - // Electron smoke tests (legacy driver) else { - const { electronProcess, client, driver } = await measureAndLog(launchElectron(options), 'launch electron', options.logger); + const { electronProcess, driver } = await measureAndLog(launchPlaywrightElectron(options), 'launch playwright (electron)', options.logger); registerInstance(electronProcess, options.logger, 'electron'); - return new Code(client, driver, options.logger, electronProcess); + return new Code(driver, options.logger, electronProcess); } } export class Code { - private _activeWindowId: number | undefined = undefined; - readonly driver: IDriver; + readonly driver: PlaywrightDriver; constructor( - private client: IDisposable, - driver: IDriver, + driver: PlaywrightDriver, readonly logger: Logger, private readonly mainProcess: cp.ChildProcess ) { @@ -134,22 +123,15 @@ export class Code { } async startTracing(name: string): Promise { - const windowId = await this.getActiveWindowId(); - return await this.driver.startTracing(windowId, name); + return await this.driver.startTracing(name); } async stopTracing(name: string, persist: boolean): Promise { - const windowId = await this.getActiveWindowId(); - return await this.driver.stopTracing(windowId, name, persist); - } - - async waitForWindowIds(accept: (windowIds: number[]) => boolean): Promise { - await this.poll(() => this.driver.getWindowIds(), accept, `get window ids`); + return await this.driver.stopTracing(name, persist); } async dispatchKeybinding(keybinding: string): Promise { - const windowId = await this.getActiveWindowId(); - await this.driver.dispatchKeybinding(windowId, keybinding); + await this.driver.dispatchKeybinding(keybinding); } async exit(): Promise { @@ -195,17 +177,14 @@ export class Code { } } })(); - }).finally(() => { - this.dispose(); }), 'Code#exit()', this.logger); } async waitForTextContent(selector: string, textContent?: string, accept?: (result: string) => boolean, retryCount?: number): Promise { - const windowId = await this.getActiveWindowId(); accept = accept || (result => textContent !== undefined ? textContent === result : !!result); return await this.poll( - () => this.driver.getElements(windowId, selector).then(els => els.length > 0 ? Promise.resolve(els[0].textContent) : Promise.reject(new Error('Element not found for textContent'))), + () => 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 @@ -213,77 +192,51 @@ export class Code { } async waitAndClick(selector: string, xoffset?: number, yoffset?: number, retryCount: number = 200): Promise { - const windowId = await this.getActiveWindowId(); - await this.poll(() => this.driver.click(windowId, selector, xoffset, yoffset), () => true, `click '${selector}'`, retryCount); + await this.poll(() => this.driver.click(selector, xoffset, yoffset), () => true, `click '${selector}'`, retryCount); } async waitForSetValue(selector: string, value: string): Promise { - const windowId = await this.getActiveWindowId(); - await this.poll(() => this.driver.setValue(windowId, selector, value), () => true, `set value '${selector}'`); + 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 { - const windowId = await this.getActiveWindowId(); - return await this.poll(() => this.driver.getElements(windowId, selector, recursive), accept, `get elements '${selector}'`); + 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 { - const windowId = await this.getActiveWindowId(); - return await this.poll(() => this.driver.getElements(windowId, selector).then(els => els[0]), accept, `get element '${selector}'`, retryCount); + 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 { - const windowId = await this.getActiveWindowId(); - await this.poll(() => this.driver.isActiveElement(windowId, selector), r => r, `is active element '${selector}'`, retryCount); + await this.poll(() => this.driver.isActiveElement(selector), r => r, `is active element '${selector}'`, retryCount); } async waitForTitle(accept: (title: string) => boolean): Promise { - const windowId = await this.getActiveWindowId(); - await this.poll(() => this.driver.getTitle(windowId), accept, `get title`); + await this.poll(() => this.driver.getTitle(), accept, `get title`); } async waitForTypeInEditor(selector: string, text: string): Promise { - const windowId = await this.getActiveWindowId(); - await this.poll(() => this.driver.typeInEditor(windowId, selector, text), () => true, `type in editor '${selector}'`); + await this.poll(() => this.driver.typeInEditor(selector, text), () => true, `type in editor '${selector}'`); } async waitForTerminalBuffer(selector: string, accept: (result: string[]) => boolean): Promise { - const windowId = await this.getActiveWindowId(); - await this.poll(() => this.driver.getTerminalBuffer(windowId, selector), accept, `get terminal buffer '${selector}'`); + await this.poll(() => this.driver.getTerminalBuffer(selector), accept, `get terminal buffer '${selector}'`); } async writeInTerminal(selector: string, value: string): Promise { - const windowId = await this.getActiveWindowId(); - await this.poll(() => this.driver.writeInTerminal(windowId, selector, value), () => true, `writeInTerminal '${selector}'`); + await this.poll(() => this.driver.writeInTerminal(selector, value), () => true, `writeInTerminal '${selector}'`); } async getLocaleInfo(): Promise { - const windowId = await this.getActiveWindowId(); - return this.driver.getLocaleInfo(windowId); + return this.driver.getLocaleInfo(); } async getLocalizedStrings(): Promise { - const windowId = await this.getActiveWindowId(); - return this.driver.getLocalizedStrings(windowId); - } - - private async getActiveWindowId(): Promise { - if (typeof this._activeWindowId !== 'number') { - this.logger.log('getActiveWindowId(): begin'); - const windows = await this.driver.getWindowIds(); - this._activeWindowId = windows[0]; - this.logger.log(`getActiveWindowId(): end (windowId=${this._activeWindowId})`); - } - - return this._activeWindowId; - } - - dispose(): void { - this.client.dispose(); + return this.driver.getLocalizedStrings(); } private async poll( - fn: () => Thenable, + fn: () => Promise, acceptFn: (result: T) => boolean, timeoutMessage: string, retryCount = 200, diff --git a/test/automation/src/debug.ts b/test/automation/src/debug.ts index eedc400451f..b7b7d427f4b 100644 --- a/test/automation/src/debug.ts +++ b/test/automation/src/debug.ts @@ -8,7 +8,7 @@ import { Commands } from './workbench'; import { Code, findElement } from './code'; import { Editors } from './editors'; import { Editor } from './editor'; -import { IElement } from '../src/driver'; +import { IElement } from './driver'; const VIEWLET = 'div[id="workbench.view.debug"]'; const DEBUG_VIEW = `${VIEWLET}`; diff --git a/test/automation/src/driver.js b/test/automation/src/driver.js deleted file mode 100644 index c415029cdf9..00000000000 --- a/test/automation/src/driver.js +++ /dev/null @@ -1,16 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -//@ts-check -'use strict'; - -const path = require('path'); - -exports.connect = function (outPath, handle) { - const bootstrapPath = path.join(outPath, 'bootstrap-amd.js'); - const { load } = require(bootstrapPath); - - return new Promise((resolve, reject) => load('vs/platform/driver/node/driver', ({ connect }) => connect(handle).then(resolve, reject), reject)); -}; diff --git a/test/automation/src/electron.ts b/test/automation/src/electron.ts index efc8ff8da7d..42e46a60a0b 100644 --- a/test/automation/src/electron.ts +++ b/test/automation/src/electron.ts @@ -4,13 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { join } from 'path'; -import { platform } from 'os'; -import { tmpName } from 'tmp'; -import { connect as connectElectronDriver, IDisposable, IDriver } from './driver'; -import { ChildProcess, spawn, SpawnOptions } from 'child_process'; import * as mkdirp from 'mkdirp'; -import { promisify } from 'util'; -import { teardown } from './processes'; import { copyExtension } from './extensions'; import { URI } from 'vscode-uri'; import { measureAndLog } from './logger'; @@ -96,61 +90,6 @@ export async function resolveElectronConfiguration(options: LaunchOptions): Prom }; } -/** - * @deprecated should use the playwright based electron support instead - */ -export async function launch(options: LaunchOptions): Promise<{ electronProcess: ChildProcess; client: IDisposable; driver: IDriver }> { - const { codePath, logger, verbose } = options; - const { env, args, electronPath } = await resolveElectronConfiguration(options); - - const driverIPCHandle = await measureAndLog(createDriverHandle(), 'createDriverHandle', logger); - args.push('--driver', driverIPCHandle); - - const outPath = codePath ? getBuildOutPath(codePath) : getDevOutPath(); - - const spawnOptions: SpawnOptions = { env }; - - if (verbose) { - spawnOptions.stdio = ['ignore', 'inherit', 'inherit']; - } - - const electronProcess = spawn(electronPath, args, spawnOptions); - - logger.log(`Started electron for desktop smoke tests on pid ${electronProcess.pid}`); - - let retries = 0; - - while (true) { - try { - const { client, driver } = await measureAndLog(connectElectronDriver(outPath, driverIPCHandle), 'connectElectronDriver()', logger); - return { - electronProcess, - client, - driver - }; - } catch (err) { - - // give up - if (++retries > 30) { - logger.log(`Error connecting driver: ${err}. Giving up...`); - - await measureAndLog(teardown(electronProcess, logger), 'Kill Electron after failing to connect', logger); - - throw err; - } - - // retry - else { - if ((err as NodeJS.ErrnoException).code !== 'ENOENT' /* ENOENT is expected for as long as the server has not started on the socket */) { - logger.log(`Error connecting driver: ${err}. Attempting to retry...`); - } - - await new Promise(resolve => setTimeout(resolve, 1000)); - } - } - } -} - export function getDevElectronPath(): string { const buildPath = join(root, '.build'); const product = require(join(root, 'product.json')); @@ -192,28 +131,3 @@ export function getBuildVersion(root: string): string { return require(join(root, 'resources', 'app', 'package.json')).version; } } - -function getDevOutPath(): string { - return join(root, 'out'); -} - -function getBuildOutPath(root: string): string { - switch (process.platform) { - case 'darwin': - return join(root, 'Contents', 'Resources', 'app', 'out'); - default: - return join(root, 'resources', 'app', 'out'); - } -} - -async function createDriverHandle(): Promise { - - // Windows - if ('win32' === platform()) { - const name = [...Array(15)].map(() => Math.random().toString(36)[3]).join(''); - return `\\\\.\\pipe\\${name}`; - } - - // Posix - return promisify(tmpName)(); -} diff --git a/test/automation/src/index.ts b/test/automation/src/index.ts index e5ffa4e60cb..ba417bf1f27 100644 --- a/test/automation/src/index.ts +++ b/test/automation/src/index.ts @@ -25,5 +25,4 @@ export * from './terminal'; export * from './viewlet'; export * from './localization'; export * from './workbench'; -export * from './driver'; export { getDevElectronPath, getBuildElectronPath, getBuildVersion } from './electron'; diff --git a/test/automation/src/playwrightBrowser.ts b/test/automation/src/playwrightBrowser.ts index e594efe6688..5d85b0e910a 100644 --- a/test/automation/src/playwrightBrowser.ts +++ b/test/automation/src/playwrightBrowser.ts @@ -8,7 +8,6 @@ import { ChildProcess, spawn } from 'child_process'; import { join } from 'path'; import { mkdir } from 'fs'; import { promisify } from 'util'; -import { IDriver, IDisposable } from './driver'; import { URI } from 'vscode-uri'; import { Logger, measureAndLog } from './logger'; import type { LaunchOptions } from './code'; @@ -18,7 +17,7 @@ const root = join(__dirname, '..', '..', '..'); let port = 9000; -export async function launch(options: LaunchOptions): Promise<{ serverProcess: ChildProcess; client: IDisposable; driver: IDriver }> { +export async function launch(options: LaunchOptions): Promise<{ serverProcess: ChildProcess; driver: PlaywrightDriver }> { // Launch server const { serverProcess, endpoint } = await launchServer(options); @@ -28,9 +27,6 @@ export async function launch(options: LaunchOptions): Promise<{ serverProcess: C return { serverProcess, - client: { - dispose: () => { /* there is no client to dispose for browser, teardown is triggered via exitApplication call */ } - }, driver: new PlaywrightDriver(browser, context, page, serverProcess, options) }; } diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index 8ff2db7b1b3..61e219db4d5 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -5,14 +5,14 @@ import * as playwright from '@playwright/test'; import { join } from 'path'; -import { IDriver, IWindowDriver } from './driver'; +import { IWindowDriver } from './driver'; import { PageFunction } from 'playwright-core/types/structs'; import { measureAndLog } from './logger'; import { LaunchOptions } from './code'; import { teardown } from './processes'; import { ChildProcess } from 'child_process'; -export class PlaywrightDriver implements IDriver { +export class PlaywrightDriver { private static traceCounter = 1; private static screenShotCounter = 1; @@ -31,8 +31,6 @@ export class PlaywrightDriver implements IDriver { esc: 'Escape' }; - _serviceBrand: undefined; - constructor( private readonly application: playwright.Browser | playwright.ElectronApplication, private readonly context: playwright.BrowserContext, @@ -42,11 +40,7 @@ export class PlaywrightDriver implements IDriver { ) { } - async getWindowIds() { - return [1]; - } - - async startTracing(windowId: number, name: string): Promise { + async startTracing(name: string): Promise { if (!this.options.tracing) { return; // tracing disabled } @@ -58,7 +52,7 @@ export class PlaywrightDriver implements IDriver { } } - async stopTracing(windowId: number, name: string, persist: boolean): Promise { + async stopTracing(name: string, persist: boolean): Promise { if (!this.options.tracing) { return; // tracing disabled } @@ -120,7 +114,7 @@ export class PlaywrightDriver implements IDriver { // Desktop: exit via `driver.exitApplication` else { try { - await measureAndLog(this._evaluateWithDriver(([driver]) => driver.exitApplication()), 'driver.exitApplication()', this.options.logger); + await measureAndLog(this.evaluateWithDriver(([driver]) => driver.exitApplication()), 'driver.exitApplication()', this.options.logger); } catch (error) { this.options.logger.log(`Error exiting appliction (${error})`); } @@ -132,7 +126,7 @@ export class PlaywrightDriver implements IDriver { } } - async dispatchKeybinding(windowId: number, keybinding: string) { + async dispatchKeybinding(keybinding: string) { const chords = keybinding.split(' '); for (let i = 0; i < chords.length; i++) { const chord = chords[i]; @@ -162,60 +156,60 @@ export class PlaywrightDriver implements IDriver { await this.timeout(100); } - async click(windowId: number, selector: string, xoffset?: number | undefined, yoffset?: number | undefined) { - const { x, y } = await this.getElementXY(windowId, selector, xoffset, yoffset); + 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(windowId: number, selector: string, text: string) { - return this.page.evaluate(([driver, selector, text]) => driver.setValue(selector, text), [await this._getDriverHandle(), selector, text] as const); + 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(windowId: number) { - return this._evaluateWithDriver(([driver]) => driver.getTitle()); + async getTitle() { + return this.evaluateWithDriver(([driver]) => driver.getTitle()); } - async isActiveElement(windowId: number, selector: string) { - return this.page.evaluate(([driver, selector]) => driver.isActiveElement(selector), [await this._getDriverHandle(), selector] as const); + async isActiveElement(selector: string) { + return this.page.evaluate(([driver, selector]) => driver.isActiveElement(selector), [await this.getDriverHandle(), selector] as const); } - async getElements(windowId: number, selector: string, recursive: boolean = false) { - return this.page.evaluate(([driver, selector, recursive]) => driver.getElements(selector, recursive), [await this._getDriverHandle(), selector, recursive] 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(windowId: number, 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 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(windowId: number, selector: string, text: string) { - return this.page.evaluate(([driver, selector, text]) => driver.typeInEditor(selector, text), [await this._getDriverHandle(), selector, text] 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(windowId: number, selector: string) { - return this.page.evaluate(([driver, selector]) => driver.getTerminalBuffer(selector), [await this._getDriverHandle(), selector] as const); + async getTerminalBuffer(selector: string) { + return this.page.evaluate(([driver, selector]) => driver.getTerminalBuffer(selector), [await this.getDriverHandle(), selector] as const); } - async writeInTerminal(windowId: number, selector: string, text: string) { - return this.page.evaluate(([driver, selector, text]) => driver.writeInTerminal(selector, text), [await this._getDriverHandle(), selector, text] 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(windowId: number) { - return this._evaluateWithDriver(([driver]) => driver.getLocaleInfo()); + async getLocaleInfo() { + return this.evaluateWithDriver(([driver]) => driver.getLocaleInfo()); } - async getLocalizedStrings(windowId: number) { - return this._evaluateWithDriver(([driver]) => driver.getLocalizedStrings()); + async getLocalizedStrings() { + return this.evaluateWithDriver(([driver]) => driver.getLocalizedStrings()); } - private async _evaluateWithDriver(pageFunction: PageFunction[], T>) { - return this.page.evaluate(pageFunction, [await this._getDriverHandle()]); + private async evaluateWithDriver(pageFunction: PageFunction[], T>) { + return this.page.evaluate(pageFunction, [await this.getDriverHandle()]); } private timeout(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } - private async _getDriverHandle(): Promise> { + private async getDriverHandle(): Promise> { return this.page.evaluateHandle('window.driver'); } } diff --git a/test/automation/src/playwrightElectron.ts b/test/automation/src/playwrightElectron.ts index 1569841fdae..3ad47fbb2ec 100644 --- a/test/automation/src/playwrightElectron.ts +++ b/test/automation/src/playwrightElectron.ts @@ -4,14 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as playwright from '@playwright/test'; -import { IDriver, IDisposable } from './driver'; import type { LaunchOptions } from './code'; import { PlaywrightDriver } from './playwrightDriver'; import { IElectronConfiguration, resolveElectronConfiguration } from './electron'; import { measureAndLog } from './logger'; import { ChildProcess } from 'child_process'; -export async function launch(options: LaunchOptions): Promise<{ electronProcess: ChildProcess; client: IDisposable; driver: IDriver }> { +export async function launch(options: LaunchOptions): Promise<{ electronProcess: ChildProcess; driver: PlaywrightDriver }> { // Resolve electron config and update const { electronPath, args, env } = await resolveElectronConfiguration(options); @@ -23,9 +22,6 @@ export async function launch(options: LaunchOptions): Promise<{ electronProcess: return { electronProcess, - client: { - dispose: () => { /* there is no client to dispose for electron, teardown is triggered via exitApplication call */ } - }, driver: new PlaywrightDriver(electron, context, page, undefined /* no server process */, options) }; } diff --git a/test/automation/src/scm.ts b/test/automation/src/scm.ts index 60a33abb93f..7186fee17ca 100644 --- a/test/automation/src/scm.ts +++ b/test/automation/src/scm.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Viewlet } from './viewlet'; -import { IElement } from '../src/driver'; +import { IElement } from './driver'; import { findElement, findElements, Code } from './code'; const VIEWLET = 'div[id="workbench.view.scm"]'; diff --git a/test/automation/tools/copy-driver-definition.js b/test/automation/tools/copy-driver-definition.js index cf757ecbc38..36352008a47 100644 --- a/test/automation/tools/copy-driver-definition.js +++ b/test/automation/tools/copy-driver-definition.js @@ -21,34 +21,14 @@ contents = `/*------------------------------------------------------------------ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/** - * Thenable is a common denominator between ES6 promises, Q, jquery.Deferred, WinJS.Promise, - * and others. This API makes no assumption about what promise library is being used which - * enables reusing existing code without migrating to a specific promise implementation. Still, - * we recommend the use of native promises which are available in this editor. - */ -interface Thenable { - /** - * Attaches callbacks for the resolution and/or rejection of the Promise. - * @param onfulfilled The callback to execute when the Promise is resolved. - * @param onrejected The callback to execute when the Promise is rejected. - * @returns A Promise for the completion of which ever callback is executed. - */ - then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Thenable; - then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Thenable; -} - ${contents} - -export interface IDisposable { - dispose(): void; -} - -export function connect(outPath: string, handle: string): Promise<{ client: IDisposable, driver: IDriver }>; `; const srcPath = path.join(path.dirname(__dirname), 'src'); const outPath = path.join(path.dirname(__dirname), 'out'); +if (!fs.existsSync(outPath)) { + fs.mkdirSync(outPath); +} fs.writeFileSync(path.join(srcPath, 'driver.d.ts'), contents); fs.writeFileSync(path.join(outPath, 'driver.d.ts'), contents); diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index a1226638fc7..7f5accccec7 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -45,7 +45,6 @@ const opts = minimist(args, { 'remote', 'web', 'headless', - 'legacy', 'tracing' ], default: { @@ -56,7 +55,6 @@ const opts = minimist(args, { remote?: boolean; headless?: boolean; web?: boolean; - legacy?: boolean; tracing?: boolean; build?: string; 'stable-build'?: string; @@ -71,9 +69,9 @@ const logsRootPath = (() => { if (opts.web) { logsName = 'smoke-tests-browser'; } else if (opts.remote) { - logsName = opts.legacy ? 'smoke-tests-remote-legacy' : 'smoke-tests-remote'; + logsName = 'smoke-tests-remote'; } else { - logsName = opts.legacy ? 'smoke-tests-electron-legacy' : 'smoke-tests-electron'; + logsName = 'smoke-tests-electron'; } return path.join(logsParentPath, logsName); @@ -326,13 +324,11 @@ before(async function () { workspacePath, userDataDir, extensionsPath, - waitTime: parseInt(opts['wait-time'] || '0') || 20, logger, logsPath: path.join(logsRootPath, 'suite_unknown'), verbose: opts.verbose, remote: opts.remote, web: opts.web, - legacy: opts.legacy, tracing: opts.tracing, headless: opts.headless, browser: opts.browser, @@ -366,7 +362,7 @@ after(async function () { } }); -describe(`VSCode Smoke Tests (${opts.web ? 'Web' : opts.legacy ? 'Electron (legacy)' : 'Electron'})`, () => { +describe(`VSCode Smoke Tests (${opts.web ? 'Web' : 'Electron'})`, () => { if (!opts.web) { setupDataLossTests(() => opts['stable-build'] /* Do not change, deferred for a reason! */, logger); } setupPreferencesTests(logger); setupSearchTests(logger); diff --git a/test/smoke/test/index.js b/test/smoke/test/index.js index 7b2894db1e1..8e9646c0d65 100644 --- a/test/smoke/test/index.js +++ b/test/smoke/test/index.js @@ -12,11 +12,11 @@ const minimist = require('minimist'); const [, , ...args] = process.argv; const opts = minimist(args, { - boolean: ['web', 'legacy'], + boolean: ['web'], string: ['f', 'g'] }); -const suite = opts['web'] ? 'Browser Smoke Tests' : opts['legacy'] ? 'Desktop Smoke Tests (Legacy)' : 'Desktop Smoke Tests'; +const suite = opts['web'] ? 'Browser Smoke Tests' : 'Desktop Smoke Tests'; const options = { color: true,