提交 056db452 编写于 作者: J Joao Moreno

driver: dispatchKeybindings

上级 f53e87d7
...@@ -360,7 +360,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { ...@@ -360,7 +360,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService {
this.toDispose.push(dom.addDisposableListener(domNode, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { this.toDispose.push(dom.addDisposableListener(domNode, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => {
let keyEvent = new StandardKeyboardEvent(e); let keyEvent = new StandardKeyboardEvent(e);
let shouldPreventDefault = this._dispatch(keyEvent, keyEvent.target); let shouldPreventDefault = this.dispatchEvent(keyEvent, keyEvent.target);
if (shouldPreventDefault) { if (shouldPreventDefault) {
keyEvent.preventDefault(); keyEvent.preventDefault();
} }
......
...@@ -17,7 +17,7 @@ suite('StandaloneKeybindingService', () => { ...@@ -17,7 +17,7 @@ suite('StandaloneKeybindingService', () => {
class TestStandaloneKeybindingService extends StandaloneKeybindingService { class TestStandaloneKeybindingService extends StandaloneKeybindingService {
public testDispatch(e: IKeyboardEvent): void { public testDispatch(e: IKeyboardEvent): void {
super._dispatch(e, null); super.dispatchEvent(e, null);
} }
} }
......
...@@ -23,12 +23,14 @@ export interface IDriver { ...@@ -23,12 +23,14 @@ export interface IDriver {
_serviceBrand: any; _serviceBrand: any;
getWindowIds(): TPromise<number[]>; getWindowIds(): TPromise<number[]>;
getElements(windowId: number, selector: string): TPromise<IElement[]>; getElements(windowId: number, selector: string): TPromise<IElement[]>;
dispatchKeybinding(windowId: number, keybinding: string): TPromise<void>;
} }
//*END //*END
export interface IDriverChannel extends IChannel { export interface IDriverChannel extends IChannel {
call(command: 'getWindowIds'): TPromise<number[]>; call(command: 'getWindowIds'): TPromise<number[]>;
call(command: 'getElements', arg: [number, string]): TPromise<IElement[]>; call(command: 'getElements', arg: [number, string]): TPromise<IElement[]>;
call(command: 'dispatchKeybinding', arg: [number, string]): TPromise<void>;
call(command: string, arg: any): TPromise<any>; call(command: string, arg: any): TPromise<any>;
} }
...@@ -40,6 +42,7 @@ export class DriverChannel implements IDriverChannel { ...@@ -40,6 +42,7 @@ export class DriverChannel implements IDriverChannel {
switch (command) { switch (command) {
case 'getWindowIds': return this.driver.getWindowIds(); case 'getWindowIds': return this.driver.getWindowIds();
case 'getElements': return this.driver.getElements(arg[0], arg[1]); case 'getElements': return this.driver.getElements(arg[0], arg[1]);
case 'dispatchKeybinding': return this.driver.dispatchKeybinding(arg[0], arg[1]);
} }
return undefined; return undefined;
...@@ -59,6 +62,10 @@ export class DriverChannelClient implements IDriver { ...@@ -59,6 +62,10 @@ export class DriverChannelClient implements IDriver {
getElements(windowId: number, selector: string): TPromise<IElement[]> { getElements(windowId: number, selector: string): TPromise<IElement[]> {
return this.channel.call('getElements', [windowId, selector]); return this.channel.call('getElements', [windowId, selector]);
} }
dispatchKeybinding(windowId: number, keybinding: string): TPromise<void> {
return this.channel.call('dispatchKeybinding', [windowId, keybinding]);
}
} }
export interface IWindowDriverRegistry { export interface IWindowDriverRegistry {
...@@ -96,10 +103,12 @@ export class WindowDriverRegistryChannelClient implements IWindowDriverRegistry ...@@ -96,10 +103,12 @@ export class WindowDriverRegistryChannelClient implements IWindowDriverRegistry
export interface IWindowDriver { export interface IWindowDriver {
getElements(selector: string): TPromise<IElement[]>; getElements(selector: string): TPromise<IElement[]>;
dispatchKeybinding(keybinding: string): TPromise<void>;
} }
export interface IWindowDriverChannel extends IChannel { export interface IWindowDriverChannel extends IChannel {
call(command: 'getElements', arg: string): TPromise<IElement[]>; call(command: 'getElements', arg: string): TPromise<IElement[]>;
call(command: 'dispatchKeybinding', arg: string): TPromise<void>;
call(command: string, arg: any): TPromise<any>; call(command: string, arg: any): TPromise<any>;
} }
...@@ -110,6 +119,7 @@ export class WindowDriverChannel implements IWindowDriverChannel { ...@@ -110,6 +119,7 @@ export class WindowDriverChannel implements IWindowDriverChannel {
call(command: string, arg?: any): TPromise<any> { call(command: string, arg?: any): TPromise<any> {
switch (command) { switch (command) {
case 'getElements': return this.driver.getElements(arg); case 'getElements': return this.driver.getElements(arg);
case 'dispatchKeybinding': return this.driver.dispatchKeybinding(arg);
} }
return undefined; return undefined;
...@@ -125,4 +135,8 @@ export class WindowDriverChannelClient implements IWindowDriver { ...@@ -125,4 +135,8 @@ export class WindowDriverChannelClient implements IWindowDriver {
getElements(selector: string): TPromise<IElement[]> { getElements(selector: string): TPromise<IElement[]> {
return this.channel.call('getElements', selector); return this.channel.call('getElements', selector);
} }
dispatchKeybinding(keybinding: string): TPromise<void> {
return this.channel.call('dispatchKeybinding', keybinding);
}
} }
\ No newline at end of file
...@@ -9,9 +9,53 @@ import { TPromise } from 'vs/base/common/winjs.base'; ...@@ -9,9 +9,53 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable } from 'vs/base/common/lifecycle'; import { IDisposable } from 'vs/base/common/lifecycle';
import { IWindowDriver, IElement, WindowDriverChannel, WindowDriverRegistryChannelClient } from 'vs/platform/driver/common/driver'; import { IWindowDriver, IElement, WindowDriverChannel, WindowDriverRegistryChannelClient } from 'vs/platform/driver/common/driver';
import { IPCClient } from 'vs/base/parts/ipc/common/ipc'; import { IPCClient } from 'vs/base/parts/ipc/common/ipc';
import { KeybindingIO } from 'vs/workbench/services/keybinding/common/keybindingIO';
import { SimpleKeybinding } from 'vs/base/common/keyCodes';
import { ScanCodeBinding, IMMUTABLE_KEY_CODE_TO_CODE, ScanCodeUtils } from 'vs/workbench/services/keybinding/common/scanCode';
import { IKeybindingService, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
class WindowDriver implements IWindowDriver { class WindowDriver implements IWindowDriver {
constructor(
@IKeybindingService private keybindingService: IKeybindingService
) { }
async dispatchKeybinding(rawKeybinding: string): TPromise<void> {
const [first, second] = KeybindingIO._readUserBinding(rawKeybinding);
this._dispatchKeybinding(first);
if (second) {
this._dispatchKeybinding(second);
}
}
private _dispatchKeybinding(keybinding: SimpleKeybinding | ScanCodeBinding): void {
if (keybinding instanceof ScanCodeBinding) {
throw new Error('ScanCodeBindings not supported');
}
const scanCode = IMMUTABLE_KEY_CODE_TO_CODE[keybinding.keyCode];
const event: IKeyboardEvent = {
ctrlKey: keybinding.ctrlKey,
altKey: keybinding.altKey,
shiftKey: keybinding.shiftKey,
metaKey: keybinding.metaKey,
keyCode: keybinding.keyCode,
code: ScanCodeUtils.toString(scanCode)
};
this.keybindingService.dispatchEvent(event, document.activeElement);
// console.log(keybinding);
// const e = new KeyboardEvent('keydown', event);
// console.log('dispatching', e);
// document.activeElement.dispatchEvent(e);
// document.activeElement.dispatchEvent(new KeyboardEvent('keyup', event));
}
async getElements(selector: string): TPromise<IElement[]> { async getElements(selector: string): TPromise<IElement[]> {
const query = document.querySelectorAll(selector); const query = document.querySelectorAll(selector);
const result: IElement[] = []; const result: IElement[] = [];
...@@ -30,8 +74,12 @@ class WindowDriver implements IWindowDriver { ...@@ -30,8 +74,12 @@ class WindowDriver implements IWindowDriver {
} }
} }
export async function registerWindowDriver(client: IPCClient, windowId: number): TPromise<IDisposable> { export async function registerWindowDriver(
const windowDriver = new WindowDriver(); client: IPCClient,
windowId: number,
instantiationService: IInstantiationService
): TPromise<IDisposable> {
const windowDriver = instantiationService.createInstance(WindowDriver);
const windowDriverChannel = new WindowDriverChannel(windowDriver); const windowDriverChannel = new WindowDriverChannel(windowDriver);
client.registerChannel('windowDriver', windowDriverChannel); client.registerChannel('windowDriver', windowDriverChannel);
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
'use strict'; 'use strict';
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
import { IDriver, DriverChannel, IElement, IWindowDriverChannel, WindowDriverChannelClient, IWindowDriverRegistry, WindowDriverRegistryChannel } from 'vs/platform/driver/common/driver'; import { IDriver, DriverChannel, IElement, IWindowDriverChannel, WindowDriverChannelClient, IWindowDriverRegistry, WindowDriverRegistryChannel, IWindowDriver } from 'vs/platform/driver/common/driver';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
import { serve as serveNet } from 'vs/base/parts/ipc/node/ipc.net'; import { serve as serveNet } from 'vs/base/parts/ipc/node/ipc.net';
import { combinedDisposable, IDisposable } from 'vs/base/common/lifecycle'; import { combinedDisposable, IDisposable } from 'vs/base/common/lifecycle';
...@@ -45,11 +45,19 @@ export class Driver implements IDriver, IWindowDriverRegistry { ...@@ -45,11 +45,19 @@ export class Driver implements IDriver, IWindowDriverRegistry {
} }
getElements(windowId: number, selector: string): TPromise<IElement[], any> { getElements(windowId: number, selector: string): TPromise<IElement[], any> {
const windowDriver = this.getWindowDriver(windowId);
return windowDriver.getElements(selector);
}
dispatchKeybinding(windowId: number, keybinding: string): TPromise<void> {
const windowDriver = this.getWindowDriver(windowId);
return windowDriver.dispatchKeybinding(keybinding);
}
private getWindowDriver(windowId: number): IWindowDriver {
const router = new WindowRouter(windowId); const router = new WindowRouter(windowId);
const windowDriverChannel = this.windowServer.getChannel<IWindowDriverChannel>('windowDriver', router); const windowDriverChannel = this.windowServer.getChannel<IWindowDriverChannel>('windowDriver', router);
const windowDriver = new WindowDriverChannelClient(windowDriverChannel); return new WindowDriverChannelClient(windowDriverChannel);
return windowDriver.getElements(selector);
} }
} }
......
...@@ -114,7 +114,7 @@ export abstract class AbstractKeybindingService implements IKeybindingService { ...@@ -114,7 +114,7 @@ export abstract class AbstractKeybindingService implements IKeybindingService {
return this._getResolver().resolve(contextValue, currentChord, firstPart); return this._getResolver().resolve(contextValue, currentChord, firstPart);
} }
protected _dispatch(e: IKeyboardEvent, target: IContextKeyServiceTarget): boolean { dispatchEvent(e: IKeyboardEvent, target: IContextKeyServiceTarget): boolean {
let shouldPreventDefault = false; let shouldPreventDefault = false;
const keybinding = this.resolveKeyboardEvent(e); const keybinding = this.resolveKeyboardEvent(e);
......
...@@ -77,5 +77,10 @@ export interface IKeybindingService { ...@@ -77,5 +77,10 @@ export interface IKeybindingService {
getKeybindings(): ResolvedKeybindingItem[]; getKeybindings(): ResolvedKeybindingItem[];
customKeybindingsCount(): number; customKeybindingsCount(): number;
/**
* For simulation purposes (eg, smoke test)
*/
dispatchEvent(e: IKeyboardEvent, target: IContextKeyServiceTarget): boolean;
} }
...@@ -70,7 +70,7 @@ suite('AbstractKeybindingService', () => { ...@@ -70,7 +70,7 @@ suite('AbstractKeybindingService', () => {
public testDispatch(kb: number): boolean { public testDispatch(kb: number): boolean {
const keybinding = createSimpleKeybinding(kb, OS); const keybinding = createSimpleKeybinding(kb, OS);
return this._dispatch({ return this.dispatchEvent({
ctrlKey: keybinding.ctrlKey, ctrlKey: keybinding.ctrlKey,
shiftKey: keybinding.shiftKey, shiftKey: keybinding.shiftKey,
altKey: keybinding.altKey, altKey: keybinding.altKey,
......
...@@ -120,4 +120,8 @@ export class MockKeybindingService implements IKeybindingService { ...@@ -120,4 +120,8 @@ export class MockKeybindingService implements IKeybindingService {
public softDispatch(keybinding: IKeyboardEvent, target: IContextKeyServiceTarget): IResolveResult { public softDispatch(keybinding: IKeyboardEvent, target: IContextKeyServiceTarget): IResolveResult {
return null; return null;
} }
dispatchEvent(e: IKeyboardEvent, target: IContextKeyServiceTarget): boolean {
return false;
}
} }
...@@ -95,7 +95,6 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; ...@@ -95,7 +95,6 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { DialogService } from 'vs/workbench/services/dialogs/electron-browser/dialogService'; import { DialogService } from 'vs/workbench/services/dialogs/electron-browser/dialogService';
import { DialogChannel } from 'vs/platform/dialogs/common/dialogIpc'; import { DialogChannel } from 'vs/platform/dialogs/common/dialogIpc';
import { EventType, addDisposableListener, addClass, getClientArea } from 'vs/base/browser/dom'; import { EventType, addDisposableListener, addClass, getClientArea } from 'vs/base/browser/dom';
import { registerWindowDriver } from 'vs/platform/driver/electron-browser/driver';
/** /**
* Services that we require for the Shell * Services that we require for the Shell
...@@ -140,7 +139,7 @@ export class WorkbenchShell { ...@@ -140,7 +139,7 @@ export class WorkbenchShell {
private configuration: IWindowConfiguration; private configuration: IWindowConfiguration;
private workbench: Workbench; private workbench: Workbench;
constructor(container: HTMLElement, coreServices: ICoreServices, mainProcessServices: ServiceCollection, mainProcessClient: IPCClient, configuration: IWindowConfiguration) { constructor(container: HTMLElement, coreServices: ICoreServices, mainProcessServices: ServiceCollection, private mainProcessClient: IPCClient, configuration: IWindowConfiguration) {
this.container = container; this.container = container;
this.configuration = configuration; this.configuration = configuration;
...@@ -156,11 +155,6 @@ export class WorkbenchShell { ...@@ -156,11 +155,6 @@ export class WorkbenchShell {
this.toUnbind = []; this.toUnbind = [];
this.previousErrorTime = 0; this.previousErrorTime = 0;
if (coreServices.environmentService.driverHandle) {
registerWindowDriver(mainProcessClient, configuration.windowId)
.then(disposable => this.toUnbind.push(disposable));
}
} }
private createContents(parent: HTMLElement): HTMLElement { private createContents(parent: HTMLElement): HTMLElement {
...@@ -195,7 +189,7 @@ export class WorkbenchShell { ...@@ -195,7 +189,7 @@ export class WorkbenchShell {
private createWorkbench(instantiationService: IInstantiationService, serviceCollection: ServiceCollection, parent: HTMLElement, workbenchContainer: HTMLElement): Workbench { private createWorkbench(instantiationService: IInstantiationService, serviceCollection: ServiceCollection, parent: HTMLElement, workbenchContainer: HTMLElement): Workbench {
try { try {
const workbench = instantiationService.createInstance(Workbench, parent, workbenchContainer, this.configuration, serviceCollection, this.lifecycleService); const workbench = instantiationService.createInstance(Workbench, parent, workbenchContainer, this.configuration, serviceCollection, this.lifecycleService, this.mainProcessClient);
// Set lifecycle phase to `Restoring` // Set lifecycle phase to `Restoring`
this.lifecycleService.phase = LifecyclePhase.Restoring; this.lifecycleService.phase = LifecyclePhase.Restoring;
......
...@@ -106,6 +106,8 @@ import { NotificationsAlerts } from 'vs/workbench/browser/parts/notifications/no ...@@ -106,6 +106,8 @@ import { NotificationsAlerts } from 'vs/workbench/browser/parts/notifications/no
import { NotificationsStatus } from 'vs/workbench/browser/parts/notifications/notificationsStatus'; import { NotificationsStatus } from 'vs/workbench/browser/parts/notifications/notificationsStatus';
import { registerNotificationCommands } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; import { registerNotificationCommands } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
import { NotificationsToasts } from 'vs/workbench/browser/parts/notifications/notificationsToasts'; import { NotificationsToasts } from 'vs/workbench/browser/parts/notifications/notificationsToasts';
import { IPCClient } from 'vs/base/parts/ipc/common/ipc';
import { registerWindowDriver } from 'vs/platform/driver/electron-browser/driver';
export const EditorsVisibleContext = new RawContextKey<boolean>('editorIsOpen', false); export const EditorsVisibleContext = new RawContextKey<boolean>('editorIsOpen', false);
export const InZenModeContext = new RawContextKey<boolean>('inZenMode', false); export const InZenModeContext = new RawContextKey<boolean>('inZenMode', false);
...@@ -231,9 +233,10 @@ export class Workbench implements IPartService { ...@@ -231,9 +233,10 @@ export class Workbench implements IPartService {
constructor( constructor(
parent: HTMLElement, parent: HTMLElement,
container: HTMLElement, container: HTMLElement,
configuration: IWindowConfiguration, private configuration: IWindowConfiguration,
serviceCollection: ServiceCollection, serviceCollection: ServiceCollection,
private lifecycleService: LifecycleService, private lifecycleService: LifecycleService,
private mainProcessClient: IPCClient,
@IInstantiationService private instantiationService: IInstantiationService, @IInstantiationService private instantiationService: IInstantiationService,
@IWorkspaceContextService private contextService: IWorkspaceContextService, @IWorkspaceContextService private contextService: IWorkspaceContextService,
@IStorageService private storageService: IStorageService, @IStorageService private storageService: IStorageService,
...@@ -318,6 +321,12 @@ export class Workbench implements IPartService { ...@@ -318,6 +321,12 @@ export class Workbench implements IPartService {
// Workbench Layout // Workbench Layout
this.createWorkbenchLayout(); this.createWorkbenchLayout();
// Driver
if (this.environmentService.driverHandle) {
registerWindowDriver(this.mainProcessClient, this.configuration.windowId, this.instantiationService)
.then(disposable => this.toUnbind.push(disposable));
}
// Restore Parts // Restore Parts
return this.restoreParts(); return this.restoreParts();
} }
......
...@@ -313,7 +313,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { ...@@ -313,7 +313,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService {
this.toDispose.push(dom.addDisposableListener(windowElement, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { this.toDispose.push(dom.addDisposableListener(windowElement, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => {
let keyEvent = new StandardKeyboardEvent(e); let keyEvent = new StandardKeyboardEvent(e);
let shouldPreventDefault = this._dispatch(keyEvent, keyEvent.target); let shouldPreventDefault = this.dispatchEvent(keyEvent, keyEvent.target);
if (shouldPreventDefault) { if (shouldPreventDefault) {
keyEvent.preventDefault(); keyEvent.preventDefault();
} }
......
...@@ -11,7 +11,7 @@ export class API { ...@@ -11,7 +11,7 @@ export class API {
// waitFor calls should not take more than 200 * 100 = 20 seconds to complete, excluding // waitFor calls should not take more than 200 * 100 = 20 seconds to complete, excluding
// the time it takes for the actual retry call to complete // the time it takes for the actual retry call to complete
private retryCount: number; private retryCount: number;
private readonly retryDuration = 100; // in milliseconds private readonly retryDuration = 1000; // in milliseconds
constructor( constructor(
private driver: Driver, private driver: Driver,
...@@ -21,8 +21,8 @@ export class API { ...@@ -21,8 +21,8 @@ export class API {
this.retryCount = (waitTime * 1000) / this.retryDuration; this.retryCount = (waitTime * 1000) / this.retryDuration;
} }
keys(keys: string[]): Promise<void> { dispatchKeybinding(keybinding: string): Promise<void> {
return this.driver.keys(keys); return this.driver.dispatchKeybinding(keybinding);
} }
waitForTextContent(selector: string, textContent?: string, accept?: (result: string) => boolean): Promise<string> { waitForTextContent(selector: string, textContent?: string, accept?: (result: string) => boolean): Promise<string> {
......
...@@ -120,7 +120,7 @@ export class Debug extends Viewlet { ...@@ -120,7 +120,7 @@ export class Debug extends Viewlet {
// Wait for the keys to be picked up by the editor model such that repl evalutes what just got typed // Wait for the keys to be picked up by the editor model such that repl evalutes what just got typed
await this.editor.waitForEditorContents('debug:input', s => s.indexOf(text) >= 0); await this.editor.waitForEditorContents('debug:input', s => s.indexOf(text) >= 0);
await this.api.keys(['Enter', 'NULL']); await this.api.dispatchKeybinding('enter');
await this.api.waitForElement(CONSOLE_INPUT_OUTPUT); await this.api.waitForElement(CONSOLE_INPUT_OUTPUT);
await this.api.waitFor(async () => { await this.api.waitFor(async () => {
const result = await this.getConsoleOutput(); const result = await this.getConsoleOutput();
......
...@@ -34,7 +34,7 @@ export class Editor { ...@@ -34,7 +34,7 @@ export class Editor {
await this.api.waitForActiveElement(RENAME_INPUT); await this.api.waitForActiveElement(RENAME_INPUT);
await this.api.setValue(RENAME_INPUT, to); await this.api.setValue(RENAME_INPUT, to);
await this.api.keys(['Enter', 'NULL']); await this.api.dispatchKeybinding('enter');
} }
async gotoDefinition(term: string, line: number): Promise<void> { async gotoDefinition(term: string, line: number): Promise<void> {
......
...@@ -34,7 +34,7 @@ export class References { ...@@ -34,7 +34,7 @@ export class References {
} }
async close(): Promise<void> { async close(): Promise<void> {
await this.api.keys(['Escape', 'NULL']); await this.api.dispatchKeybinding('escape');
await this.api.waitForElement(References.REFERENCES_WIDGET, element => !element); await this.api.waitForElement(References.REFERENCES_WIDGET, element => !element);
} }
} }
\ No newline at end of file
...@@ -25,7 +25,7 @@ export function setup() { ...@@ -25,7 +25,7 @@ export function setup() {
await app.workbench.quickopen.openQuickOpen('.js'); await app.workbench.quickopen.openQuickOpen('.js');
await app.workbench.quickopen.waitForQuickOpenElements(names => expectedNames.every(n => names.some(m => n === m))); await app.workbench.quickopen.waitForQuickOpenElements(names => expectedNames.every(n => names.some(m => n === m)));
await app.api.keys(['Escape', 'NULL']); await app.api.dispatchKeybinding('escape');
}); });
it('quick open respects fuzzy matching', async function () { it('quick open respects fuzzy matching', async function () {
...@@ -38,7 +38,7 @@ export function setup() { ...@@ -38,7 +38,7 @@ export function setup() {
await app.workbench.quickopen.openQuickOpen('a.s'); await app.workbench.quickopen.openQuickOpen('a.s');
await app.workbench.quickopen.waitForQuickOpenElements(names => expectedNames.every(n => names.some(m => n === m))); await app.workbench.quickopen.waitForQuickOpenElements(names => expectedNames.every(n => names.some(m => n === m)));
await app.api.keys(['Escape', 'NULL']); await app.api.dispatchKeybinding('escape');
}); });
}); });
} }
\ No newline at end of file
...@@ -12,7 +12,7 @@ export class KeybindingsEditor { ...@@ -12,7 +12,7 @@ export class KeybindingsEditor {
constructor(private api: API, private commands: Commands) { } constructor(private api: API, private commands: Commands) { }
async updateKeybinding(command: string, keys: string[], ariaLabel: string): Promise<any> { async updateKeybinding(command: string, keybinding: string, ariaLabel: string): Promise<any> {
await this.commands.runCommand('workbench.action.openGlobalKeybindings'); await this.commands.runCommand('workbench.action.openGlobalKeybindings');
await this.api.waitForActiveElement(SEARCH_INPUT); await this.api.waitForActiveElement(SEARCH_INPUT);
await this.api.setValue(SEARCH_INPUT, command); await this.api.setValue(SEARCH_INPUT, command);
...@@ -23,7 +23,8 @@ export class KeybindingsEditor { ...@@ -23,7 +23,8 @@ export class KeybindingsEditor {
await this.api.waitAndClick('div[aria-label="Keybindings"] .monaco-list-row.keybinding-item .action-item .icon.add'); await this.api.waitAndClick('div[aria-label="Keybindings"] .monaco-list-row.keybinding-item .action-item .icon.add');
await this.api.waitForElement('.defineKeybindingWidget .monaco-inputbox.synthetic-focus'); await this.api.waitForElement('.defineKeybindingWidget .monaco-inputbox.synthetic-focus');
await this.api.keys([...keys, 'NULL', 'Enter', 'NULL']); await this.api.dispatchKeybinding(keybinding);
await this.api.dispatchKeybinding('enter');
await this.api.waitForElement(`div[aria-label="Keybindings"] div[aria-label="Keybinding is ${ariaLabel}."]`); await this.api.waitForElement(`div[aria-label="Keybindings"] div[aria-label="Keybinding is ${ariaLabel}."]`);
} }
} }
\ No newline at end of file
...@@ -32,9 +32,9 @@ export function setup() { ...@@ -32,9 +32,9 @@ export function setup() {
const app = this.app as SpectronApplication; const app = this.app as SpectronApplication;
assert.ok(await app.workbench.activitybar.getActivityBar(ActivityBarPosition.LEFT), 'Activity bar should be positioned on the left.'); assert.ok(await app.workbench.activitybar.getActivityBar(ActivityBarPosition.LEFT), 'Activity bar should be positioned on the left.');
await app.workbench.keybindingsEditor.updateKeybinding('workbench.action.toggleSidebarPosition', ['Control', 'u'], 'Control+U'); await app.workbench.keybindingsEditor.updateKeybinding('workbench.action.toggleSidebarPosition', 'ctrl+u', 'Control+U');
await app.api.keys(['Control', 'u', 'NULL']); await app.api.dispatchKeybinding('ctrl+u');
assert.ok(await app.workbench.activitybar.getActivityBar(ActivityBarPosition.RIGHT), 'Activity bar was not moved to right after toggling its position.'); assert.ok(await app.workbench.activitybar.getActivityBar(ActivityBarPosition.RIGHT), 'Activity bar was not moved to right after toggling its position.');
}); });
......
...@@ -27,11 +27,10 @@ export class SettingsEditor { ...@@ -27,11 +27,10 @@ export class SettingsEditor {
await this.api.waitAndClick(SEARCH_INPUT); await this.api.waitAndClick(SEARCH_INPUT);
await this.api.waitForActiveElement(SEARCH_INPUT); await this.api.waitForActiveElement(SEARCH_INPUT);
await this.api.keys(['ArrowDown', 'NULL']); await this.api.dispatchKeybinding('down');
await this.api.waitForActiveElement(EDITOR); await this.api.waitForActiveElement(EDITOR);
await this.api.keys(['ArrowRight', 'NULL']); await this.api.dispatchKeybinding('right');
await this.editor.waitForTypeInEditor('settings.json', `"${setting}": ${value}`, '.editable-preferences-editor-container'); await this.editor.waitForTypeInEditor('settings.json', `"${setting}": ${value}`, '.editable-preferences-editor-container');
await this.editors.saveOpenedFile(); await this.editors.saveOpenedFile();
} }
......
...@@ -36,7 +36,7 @@ export class QuickOpen { ...@@ -36,7 +36,7 @@ export class QuickOpen {
await this.openQuickOpen(fileName); await this.openQuickOpen(fileName);
await this.waitForQuickOpenElements(names => names.some(n => n === fileName)); await this.waitForQuickOpenElements(names => names.some(n => n === fileName));
await this.api.keys(['Enter', 'NULL']); await this.api.dispatchKeybinding('enter');
await this.editors.waitForActiveTab(fileName); await this.editors.waitForActiveTab(fileName);
await this.editors.waitForEditorFocus(fileName); await this.editors.waitForEditorFocus(fileName);
} }
...@@ -51,16 +51,16 @@ export class QuickOpen { ...@@ -51,16 +51,16 @@ export class QuickOpen {
async submit(text: string): Promise<void> { async submit(text: string): Promise<void> {
await this.api.setValue(QuickOpen.QUICK_OPEN_INPUT, text); await this.api.setValue(QuickOpen.QUICK_OPEN_INPUT, text);
await this.api.keys(['Enter', 'NULL']); await this.api.dispatchKeybinding('enter');
await this.waitForQuickOpenClosed(); await this.waitForQuickOpenClosed();
} }
async selectQuickOpenElement(index: number): Promise<void> { async selectQuickOpenElement(index: number): Promise<void> {
await this.waitForQuickOpenOpened(); await this.waitForQuickOpenOpened();
for (let from = 0; from < index; from++) { for (let from = 0; from < index; from++) {
await this.api.keys(['ArrowDown', 'NULL']); await this.api.dispatchKeybinding('down');
} }
await this.api.keys(['Enter', 'NULL']); await this.api.dispatchKeybinding('enter');
await this.waitForQuickOpenClosed(); await this.waitForQuickOpenClosed();
} }
......
...@@ -33,7 +33,7 @@ export class Search extends Viewlet { ...@@ -33,7 +33,7 @@ export class Search extends Viewlet {
await this.api.waitAndClick(INPUT); await this.api.waitAndClick(INPUT);
await this.api.waitForActiveElement(INPUT); await this.api.waitForActiveElement(INPUT);
await this.api.keys(['Enter', 'NULL']); await this.api.dispatchKeybinding('enter');
await this.api.waitForElement(`${VIEWLET} .messages[aria-hidden="false"]`); await this.api.waitForElement(`${VIEWLET} .messages[aria-hidden="false"]`);
} }
......
...@@ -28,7 +28,7 @@ export class Terminal { ...@@ -28,7 +28,7 @@ export class Terminal {
async runCommand(commandText: string): Promise<void> { async runCommand(commandText: string): Promise<void> {
// TODO@Tyriar fix this. we should not use type but setValue // TODO@Tyriar fix this. we should not use type but setValue
// await this.spectron.client.type(commandText); // await this.spectron.client.type(commandText);
await this.api.keys(['Enter', 'NULL']); await this.api.dispatchKeybinding('enter');
} }
async waitForTerminalText(fn: (text: string[]) => boolean, timeOutDescription: string = 'Getting Terminal Text'): Promise<string[]> { async waitForTerminalText(fn: (text: string[]) => boolean, timeOutDescription: string = 'Getting Terminal Text'): Promise<string[]> {
......
...@@ -68,32 +68,7 @@ export class Workbench implements Commands { ...@@ -68,32 +68,7 @@ export class Workbench implements Commands {
return; return;
} }
const keys: string = binding.key; return this.api.dispatchKeybinding(binding.key);
let keysToPress: string[] = [];
const chords = keys.split(' ');
chords.forEach((chord) => {
const keys = chord.split('+');
keys.forEach((key) => keysToPress.push(this.transliterate(key)));
keysToPress.push('NULL');
});
return this.api.keys(keysToPress);
}
/**
* Transliterates key names from keybindings file to WebdriverIO keyboard actions defined in:
* https://w3c.github.io/webdriver/webdriver-spec.html#keyboard-actions
*/
private transliterate(key: string): string {
switch (key) {
case 'ctrl':
return 'Control';
case 'cmd':
return 'Meta';
default:
return key.length === 1 ? key : key.charAt(0).toUpperCase() + key.slice(1);
}
} }
} }
...@@ -13,7 +13,7 @@ export interface Element { ...@@ -13,7 +13,7 @@ export interface Element {
} }
export interface Driver { export interface Driver {
keys(keys: string[]): Promise<void>; dispatchKeybinding(keybinding: string): Promise<void>;
click(selector: string, xoffset?: number, yoffset?: number): Promise<any>; click(selector: string, xoffset?: number, yoffset?: number): Promise<any>;
doubleClick(selector: string): Promise<any>; doubleClick(selector: string): Promise<any>;
move(selector: string): Promise<any>; move(selector: string): Promise<any>;
...@@ -32,13 +32,8 @@ export class SpectronDriver implements Driver { ...@@ -32,13 +32,8 @@ export class SpectronDriver implements Driver {
private verbose: boolean private verbose: boolean
) { } ) { }
keys(keys: string[]): Promise<void> { dispatchKeybinding(keybinding: string): Promise<void> {
if (this.verbose) { return Promise.reject(new Error('not implemented'));
console.log('- keys:', keys);
}
this.spectronClient.keys(keys);
return Promise.resolve();
} }
async click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise<void> { async click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise<void> {
...@@ -147,12 +142,13 @@ export class CodeDriver implements Driver { ...@@ -147,12 +142,13 @@ export class CodeDriver implements Driver {
return this._activeWindowId; return this._activeWindowId;
} }
keys(keys: string[]): Promise<void> { async dispatchKeybinding(keybinding: string): Promise<void> {
if (this.verbose) { if (this.verbose) {
console.log('- keys:', keys); console.log('- dispatchKeybinding:', keybinding);
} }
throw new Error('Method not implemented.'); const windowId = await this.getWindowId();
await this.driver.dispatchKeybinding(windowId, keybinding);
} }
click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise<any> { click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise<any> {
......
...@@ -13,6 +13,7 @@ export interface IDriver { ...@@ -13,6 +13,7 @@ export interface IDriver {
_serviceBrand: any; _serviceBrand: any;
getWindowIds(): Promise<number[]>; getWindowIds(): Promise<number[]>;
getElements(windowId: number, selector: string): Promise<IElement[]>; getElements(windowId: number, selector: string): Promise<IElement[]>;
dispatchKeybinding(windowId: number, keybinding: string): Promise<void>;
} }
export interface IDisposable { export interface IDisposable {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册