mirror of
https://github.com/Microsoft/vscode
synced 2024-10-02 09:18:59 +00:00
Remove legacy smoke test driver (#147393)
* wip * fix imports * more lipstick * fix? * fix
This commit is contained in:
parent
675b8c3da0
commit
c4cdb552a5
|
@ -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',
|
||||
|
|
|
@ -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')
|
||||
];
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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<number[]>;
|
||||
startTracing(windowId: number, name: string): Promise<void>;
|
||||
stopTracing(windowId: number, name: string, persist: boolean): Promise<void>;
|
||||
exitApplication(): Promise<void>;
|
||||
dispatchKeybinding(windowId: number, keybinding: string): Promise<void>;
|
||||
click(windowId: number, selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise<void>;
|
||||
setValue(windowId: number, selector: string, text: string): Promise<void>;
|
||||
getTitle(windowId: number): Promise<string>;
|
||||
isActiveElement(windowId: number, selector: string): Promise<boolean>;
|
||||
getElements(windowId: number, selector: string, recursive?: boolean): Promise<IElement[]>;
|
||||
getElementXY(windowId: number, selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number }>;
|
||||
typeInEditor(windowId: number, selector: string, text: string): Promise<void>;
|
||||
getTerminalBuffer(windowId: number, selector: string): Promise<string[]>;
|
||||
writeInTerminal(windowId: number, selector: string, text: string): Promise<void>;
|
||||
getLocaleInfo(windowId: number): Promise<ILocaleInfo>;
|
||||
getLocalizedStrings(windowId: number): Promise<ILocalizedStrings>;
|
||||
}
|
||||
|
||||
export interface IWindowDriver {
|
||||
click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise<void>;
|
||||
setValue(selector: string, text: string): Promise<void>;
|
||||
|
@ -72,11 +42,3 @@ export interface IWindowDriver {
|
|||
exitApplication(): Promise<void>;
|
||||
}
|
||||
//*END
|
||||
|
||||
export const ID = 'driverService';
|
||||
export const IDriver = createDecorator<IDriver>(ID);
|
||||
|
||||
export interface IWindowDriverRegistry {
|
||||
registerWindowDriver(windowId: number): Promise<void>;
|
||||
reloadWindowDriver(windowId: number): Promise<void>;
|
||||
}
|
||||
|
|
|
@ -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<T>(_: unknown, event: string): Event<T> {
|
||||
throw new Error(`No event found: ${event}`);
|
||||
}
|
||||
|
||||
call(_: unknown, command: string, arg?: any): Promise<any> {
|
||||
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<void> {
|
||||
return this.channel.call('click', [selector, xoffset, yoffset]);
|
||||
}
|
||||
|
||||
setValue(selector: string, text: string): Promise<void> {
|
||||
return this.channel.call('setValue', [selector, text]);
|
||||
}
|
||||
|
||||
getTitle(): Promise<string> {
|
||||
return this.channel.call('getTitle');
|
||||
}
|
||||
|
||||
isActiveElement(selector: string): Promise<boolean> {
|
||||
return this.channel.call('isActiveElement', selector);
|
||||
}
|
||||
|
||||
getElements(selector: string, recursive: boolean): Promise<IElement[]> {
|
||||
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<void> {
|
||||
return this.channel.call('typeInEditor', [selector, text]);
|
||||
}
|
||||
|
||||
getTerminalBuffer(selector: string): Promise<string[]> {
|
||||
return this.channel.call('getTerminalBuffer', selector);
|
||||
}
|
||||
|
||||
writeInTerminal(selector: string, text: string): Promise<void> {
|
||||
return this.channel.call('writeInTerminal', [selector, text]);
|
||||
}
|
||||
|
||||
getLocaleInfo(): Promise<ILocaleInfo> {
|
||||
return this.channel.call('getLocaleInfo');
|
||||
}
|
||||
|
||||
getLocalizedStrings(): Promise<ILocalizedStrings> {
|
||||
return this.channel.call('getLocalizedStrings');
|
||||
}
|
||||
|
||||
exitApplication(): Promise<void> {
|
||||
return this.channel.call('exitApplication');
|
||||
}
|
||||
}
|
||||
|
||||
export class WindowDriverRegistryChannelClient implements IWindowDriverRegistry {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
constructor(private channel: IChannel) { }
|
||||
|
||||
registerWindowDriver(windowId: number): Promise<void> {
|
||||
return this.channel.call('registerWindowDriver', windowId);
|
||||
}
|
||||
|
||||
reloadWindowDriver(windowId: number): Promise<void> {
|
||||
return this.channel.call('reloadWindowDriver', windowId);
|
||||
}
|
||||
}
|
|
@ -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<number>();
|
||||
private reloadingWindowIds = new Set<number>();
|
||||
private readonly onDidReloadingChange = new Emitter<void>();
|
||||
|
||||
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<void> {
|
||||
this.logService.info(`[driver] registerWindowDriver(${windowId})`);
|
||||
|
||||
this.registeredWindowIds.add(windowId);
|
||||
this.reloadingWindowIds.delete(windowId);
|
||||
this.onDidReloadingChange.fire();
|
||||
}
|
||||
|
||||
async reloadWindowDriver(windowId: number): Promise<void> {
|
||||
this.logService.info(`[driver] reloadWindowDriver(${windowId})`);
|
||||
|
||||
this.reloadingWindowIds.add(windowId);
|
||||
}
|
||||
|
||||
async getWindowIds(): Promise<number[]> {
|
||||
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<void> {
|
||||
// ignore - tracing is not implemented yet
|
||||
}
|
||||
|
||||
async stopTracing(windowId: number, name: string, persist: boolean): Promise<void> {
|
||||
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<string> {
|
||||
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<void> {
|
||||
this.logService.info(`[driver] exitApplication()`);
|
||||
|
||||
await this.lifecycleMainService.quit();
|
||||
}
|
||||
|
||||
async dispatchKeybinding(windowId: number, keybinding: string): Promise<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
const windowDriver = await this.getWindowDriver(windowId);
|
||||
await windowDriver.click(selector, xoffset, yoffset);
|
||||
}
|
||||
|
||||
async setValue(windowId: number, selector: string, text: string): Promise<void> {
|
||||
const windowDriver = await this.getWindowDriver(windowId);
|
||||
await windowDriver.setValue(selector, text);
|
||||
}
|
||||
|
||||
async getTitle(windowId: number): Promise<string> {
|
||||
const windowDriver = await this.getWindowDriver(windowId);
|
||||
return await windowDriver.getTitle();
|
||||
}
|
||||
|
||||
async isActiveElement(windowId: number, selector: string): Promise<boolean> {
|
||||
const windowDriver = await this.getWindowDriver(windowId);
|
||||
return await windowDriver.isActiveElement(selector);
|
||||
}
|
||||
|
||||
async getElements(windowId: number, selector: string, recursive: boolean): Promise<IElement[]> {
|
||||
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<void> {
|
||||
const windowDriver = await this.getWindowDriver(windowId);
|
||||
await windowDriver.typeInEditor(selector, text);
|
||||
}
|
||||
|
||||
async getTerminalBuffer(windowId: number, selector: string): Promise<string[]> {
|
||||
const windowDriver = await this.getWindowDriver(windowId);
|
||||
return await windowDriver.getTerminalBuffer(selector);
|
||||
}
|
||||
|
||||
async writeInTerminal(windowId: number, selector: string, text: string): Promise<void> {
|
||||
const windowDriver = await this.getWindowDriver(windowId);
|
||||
await windowDriver.writeInTerminal(selector, text);
|
||||
}
|
||||
|
||||
async getLocaleInfo(windowId: number): Promise<ILocaleInfo> {
|
||||
const windowDriver = await this.getWindowDriver(windowId);
|
||||
return await windowDriver.getLocaleInfo();
|
||||
}
|
||||
|
||||
async getLocalizedStrings(windowId: number): Promise<ILocalizedStrings> {
|
||||
const windowDriver = await this.getWindowDriver(windowId);
|
||||
return await windowDriver.getLocalizedStrings();
|
||||
}
|
||||
|
||||
private async getWindowDriver(windowId: number): Promise<IWindowDriver> {
|
||||
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<void> {
|
||||
while (this.reloadingWindowIds.has(windowId)) {
|
||||
await Event.toPromise(this.onDidReloadingChange.event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function serve(
|
||||
windowServer: IPCServer,
|
||||
handle: string,
|
||||
instantiationService: IInstantiationService
|
||||
): Promise<IDisposable> {
|
||||
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);
|
||||
}
|
|
@ -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<void>;
|
||||
|
@ -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<void> {
|
||||
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<void> {
|
||||
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<IDisposable> {
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -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<T>(_: unknown, event: string): Event<T> {
|
||||
throw new Error('No event found');
|
||||
}
|
||||
|
||||
call(_: unknown, command: string, arg?: any): Promise<any> {
|
||||
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<number[]> {
|
||||
return this.channel.call('getWindowIds');
|
||||
}
|
||||
|
||||
startTracing(windowId: number, name: string): Promise<void> {
|
||||
return this.channel.call('startTracing', [windowId, name]);
|
||||
}
|
||||
|
||||
stopTracing(windowId: number, name: string, persist: boolean): Promise<void> {
|
||||
return this.channel.call('stopTracing', [windowId, name, persist]);
|
||||
}
|
||||
|
||||
exitApplication(): Promise<void> {
|
||||
return this.channel.call('exitApplication');
|
||||
}
|
||||
|
||||
dispatchKeybinding(windowId: number, keybinding: string): Promise<void> {
|
||||
return this.channel.call('dispatchKeybinding', [windowId, keybinding]);
|
||||
}
|
||||
|
||||
click(windowId: number, selector: string, xoffset: number | undefined, yoffset: number | undefined): Promise<void> {
|
||||
return this.channel.call('click', [windowId, selector, xoffset, yoffset]);
|
||||
}
|
||||
|
||||
setValue(windowId: number, selector: string, text: string): Promise<void> {
|
||||
return this.channel.call('setValue', [windowId, selector, text]);
|
||||
}
|
||||
|
||||
getTitle(windowId: number): Promise<string> {
|
||||
return this.channel.call('getTitle', [windowId]);
|
||||
}
|
||||
|
||||
isActiveElement(windowId: number, selector: string): Promise<boolean> {
|
||||
return this.channel.call('isActiveElement', [windowId, selector]);
|
||||
}
|
||||
|
||||
getElements(windowId: number, selector: string, recursive: boolean): Promise<IElement[]> {
|
||||
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<void> {
|
||||
return this.channel.call('typeInEditor', [windowId, selector, text]);
|
||||
}
|
||||
|
||||
getTerminalBuffer(windowId: number, selector: string): Promise<string[]> {
|
||||
return this.channel.call('getTerminalBuffer', [windowId, selector]);
|
||||
}
|
||||
|
||||
writeInTerminal(windowId: number, selector: string, text: string): Promise<void> {
|
||||
return this.channel.call('writeInTerminal', [windowId, selector, text]);
|
||||
}
|
||||
|
||||
getLocaleInfo(windowId: number): Promise<ILocaleInfo> {
|
||||
return this.channel.call('getLocaleInfo', windowId);
|
||||
}
|
||||
|
||||
getLocalizedStrings(windowId: number): Promise<ILocalizedStrings> {
|
||||
return this.channel.call('getLocalizedStrings', windowId);
|
||||
}
|
||||
}
|
||||
|
||||
export class WindowDriverRegistryChannel implements IServerChannel {
|
||||
|
||||
constructor(private registry: IWindowDriverRegistry) { }
|
||||
|
||||
listen<T>(_: unknown, event: string): Event<T> {
|
||||
throw new Error(`Event not found: ${event}`);
|
||||
}
|
||||
|
||||
call(_: unknown, command: string, arg?: any): Promise<any> {
|
||||
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 };
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -132,9 +132,6 @@ export interface INativeEnvironmentService extends IEnvironmentService {
|
|||
extensionsDownloadPath: string;
|
||||
builtinExtensionsPath: string;
|
||||
|
||||
// --- smoke test support
|
||||
driverHandle?: string;
|
||||
|
||||
// --- use keytar for credentials
|
||||
disableKeytar?: boolean;
|
||||
|
||||
|
|
|
@ -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']; }
|
||||
|
|
|
@ -98,7 +98,6 @@ export const OPTIONS: OptionDescriptions<Required<NativeParsedArgs>> = {
|
|||
'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' },
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<void> {
|
||||
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<void> {
|
||||
return that.nativeHostService.quit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private setupOpenHandlers(): void {
|
||||
|
|
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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<void> {
|
||||
|
||||
// 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);
|
||||
|
||||
|
|
|
@ -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<Code> {
|
|||
|
||||
// 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<void> {
|
||||
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<void> {
|
||||
const windowId = await this.getActiveWindowId();
|
||||
return await this.driver.stopTracing(windowId, name, persist);
|
||||
}
|
||||
|
||||
async waitForWindowIds(accept: (windowIds: number[]) => boolean): Promise<void> {
|
||||
await this.poll(() => this.driver.getWindowIds(), accept, `get window ids`);
|
||||
return await this.driver.stopTracing(name, persist);
|
||||
}
|
||||
|
||||
async dispatchKeybinding(keybinding: string): Promise<void> {
|
||||
const windowId = await this.getActiveWindowId();
|
||||
await this.driver.dispatchKeybinding(windowId, keybinding);
|
||||
await this.driver.dispatchKeybinding(keybinding);
|
||||
}
|
||||
|
||||
async exit(): Promise<void> {
|
||||
|
@ -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<string> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<IElement[]> {
|
||||
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<IElement> {
|
||||
const windowId = await this.getActiveWindowId();
|
||||
return await this.poll<IElement>(() => this.driver.getElements(windowId, selector).then(els => els[0]), accept, `get element '${selector}'`, retryCount);
|
||||
return await this.poll<IElement>(() => this.driver.getElements(selector).then(els => els[0]), accept, `get element '${selector}'`, retryCount);
|
||||
}
|
||||
|
||||
async waitForActiveElement(selector: string, retryCount: number = 200): Promise<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<ILocaleInfo> {
|
||||
const windowId = await this.getActiveWindowId();
|
||||
return this.driver.getLocaleInfo(windowId);
|
||||
return this.driver.getLocaleInfo();
|
||||
}
|
||||
|
||||
async getLocalizedStrings(): Promise<ILocalizedStrings> {
|
||||
const windowId = await this.getActiveWindowId();
|
||||
return this.driver.getLocalizedStrings(windowId);
|
||||
}
|
||||
|
||||
private async getActiveWindowId(): Promise<number> {
|
||||
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<T>(
|
||||
fn: () => Thenable<T>,
|
||||
fn: () => Promise<T>,
|
||||
acceptFn: (result: T) => boolean,
|
||||
timeoutMessage: string,
|
||||
retryCount = 200,
|
||||
|
|
|
@ -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}`;
|
||||
|
|
|
@ -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));
|
||||
};
|
|
@ -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<string> {
|
||||
|
||||
// Windows
|
||||
if ('win32' === platform()) {
|
||||
const name = [...Array(15)].map(() => Math.random().toString(36)[3]).join('');
|
||||
return `\\\\.\\pipe\\${name}`;
|
||||
}
|
||||
|
||||
// Posix
|
||||
return promisify(tmpName)();
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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<void> {
|
||||
async startTracing(name: string): Promise<void> {
|
||||
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<void> {
|
||||
async stopTracing(name: string, persist: boolean): Promise<void> {
|
||||
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<T>(pageFunction: PageFunction<playwright.JSHandle<IWindowDriver>[], T>) {
|
||||
return this.page.evaluate(pageFunction, [await this._getDriverHandle()]);
|
||||
private async evaluateWithDriver<T>(pageFunction: PageFunction<playwright.JSHandle<IWindowDriver>[], T>) {
|
||||
return this.page.evaluate(pageFunction, [await this.getDriverHandle()]);
|
||||
}
|
||||
|
||||
private timeout(ms: number): Promise<void> {
|
||||
return new Promise<void>(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
private async _getDriverHandle(): Promise<playwright.JSHandle<IWindowDriver>> {
|
||||
private async getDriverHandle(): Promise<playwright.JSHandle<IWindowDriver>> {
|
||||
return this.page.evaluateHandle('window.driver');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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"]';
|
||||
|
|
|
@ -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<T> {
|
||||
/**
|
||||
* 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<TResult>(onfulfilled?: (value: T) => TResult | Thenable<TResult>, onrejected?: (reason: any) => TResult | Thenable<TResult>): Thenable<TResult>;
|
||||
then<TResult>(onfulfilled?: (value: T) => TResult | Thenable<TResult>, onrejected?: (reason: any) => void): Thenable<TResult>;
|
||||
}
|
||||
|
||||
${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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue