driver.ts 6.8 KB
Newer Older
J
Joao Moreno 已提交
1 2 3 4 5 6 7 8
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

import { TPromise } from 'vs/base/common/winjs.base';
J
Joao Moreno 已提交
9
import { IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
J
Joao Moreno 已提交
10 11
import { IWindowDriver, IElement, WindowDriverChannel, WindowDriverRegistryChannelClient } from 'vs/platform/driver/common/driver';
import { IPCClient } from 'vs/base/parts/ipc/common/ipc';
J
Joao Moreno 已提交
12
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
J
Joao Moreno 已提交
13 14
import { getTopLeftOffset, getClientArea } from 'vs/base/browser/dom';
import * as electron from 'electron';
15
import { IWindowService } from 'vs/platform/windows/common/windows';
J
Joao Moreno 已提交
16

17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
function serializeElement(element: Element, recursive: boolean): IElement {
	const attributes = Object.create(null);

	for (let j = 0; j < element.attributes.length; j++) {
		const attr = element.attributes.item(j);
		attributes[attr.name] = attr.value;
	}

	const children = [];

	if (recursive) {
		for (let i = 0; i < element.children.length; i++) {
			children.push(serializeElement(element.children.item(i), true));
		}
	}

	return {
		tagName: element.tagName,
		className: element.className,
		textContent: element.textContent || '',
		attributes,
		children
	};
}

J
Joao Moreno 已提交
42 43
class WindowDriver implements IWindowDriver {

44 45 46
	constructor(
		@IWindowService private windowService: IWindowService
	) { }
J
Joao Moreno 已提交
47

J
Joao Moreno 已提交
48
	async click(selector: string, xoffset?: number, yoffset?: number): TPromise<void> {
J
Joao Moreno 已提交
49 50 51 52 53 54 55
		return this._click(selector, 1, xoffset, yoffset);
	}

	doubleClick(selector: string): TPromise<void> {
		return this._click(selector, 2);
	}

J
Joao Moreno 已提交
56
	private async _getElementXY(selector: string, xoffset?: number, yoffset?: number): TPromise<{ x: number; y: number; }> {
J
Joao Moreno 已提交
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
		const element = document.querySelector(selector);

		if (!element) {
			throw new Error('Element not found');
		}

		const { left, top } = getTopLeftOffset(element as HTMLElement);
		const { width, height } = getClientArea(element as HTMLElement);
		let x: number, y: number;

		if ((typeof xoffset === 'number') || (typeof yoffset === 'number')) {
			x = left + xoffset;
			y = top + yoffset;
		} else {
			x = left + (width / 2);
			y = top + (height / 2);
		}

J
Joao Moreno 已提交
75 76 77
		x = Math.round(x);
		y = Math.round(y);

J
Joao Moreno 已提交
78 79 80 81 82
		return { x, y };
	}

	private async _click(selector: string, clickCount: number, xoffset?: number, yoffset?: number): TPromise<void> {
		const { x, y } = await this._getElementXY(selector, xoffset, yoffset);
J
Joao Moreno 已提交
83
		const webContents = electron.remote.getCurrentWebContents();
J
Joao Moreno 已提交
84 85
		webContents.sendInputEvent({ type: 'mouseDown', x, y, button: 'left', clickCount } as any);
		webContents.sendInputEvent({ type: 'mouseUp', x, y, button: 'left', clickCount } as any);
J
Joao Moreno 已提交
86 87

		await TPromise.timeout(100);
J
Joao Moreno 已提交
88 89
	}

J
Joao Moreno 已提交
90 91 92 93 94 95
	async move(selector: string): TPromise<void> {
		const { x, y } = await this._getElementXY(selector);
		const webContents = electron.remote.getCurrentWebContents();
		webContents.sendInputEvent({ type: 'mouseMove', x, y } as any);

		await TPromise.timeout(100);
J
Joao Moreno 已提交
96 97 98 99 100 101 102 103 104
	}

	async setValue(selector: string, text: string): TPromise<void> {
		const element = document.querySelector(selector);

		if (!element) {
			throw new Error('Element not found');
		}

J
Joao Moreno 已提交
105 106 107 108 109
		const inputElement = element as HTMLInputElement;
		inputElement.value = text;

		const event = new Event('input', { bubbles: true, cancelable: true });
		inputElement.dispatchEvent(event);
J
Joao Moreno 已提交
110 111 112 113 114 115 116 117
	}

	async getTitle(): TPromise<string> {
		return document.title;
	}

	async isActiveElement(selector: string): TPromise<boolean> {
		const element = document.querySelector(selector);
118 119 120 121 122 123 124 125 126 127 128 129

		if (element !== document.activeElement) {
			const el = document.activeElement;
			const tagName = el.tagName;
			const id = el.id ? `#${el.id}` : '';
			const classes = el.className.split(/\W+/g).map(c => c.trim()).filter(c => !!c).map(c => `.${c}`).join('');
			const current = `${tagName}${id}${classes}`;

			throw new Error(`Active element not found. Current active element is '${current}'`);
		}

		return true;
J
Joao Moreno 已提交
130 131
	}

132
	async getElements(selector: string, recursive: boolean): TPromise<IElement[]> {
J
Joao Moreno 已提交
133 134 135 136 137
		const query = document.querySelectorAll(selector);
		const result: IElement[] = [];

		for (let i = 0; i < query.length; i++) {
			const element = query.item(i);
138
			result.push(serializeElement(element, recursive));
J
Joao Moreno 已提交
139 140 141 142
		}

		return result;
	}
J
Joao Moreno 已提交
143

J
Joao Moreno 已提交
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
	async typeInEditor(selector: string, text: string): TPromise<void> {
		const element = document.querySelector(selector);

		if (!element) {
			throw new Error('Editor not found: ' + selector);
		}

		const textarea = element as HTMLTextAreaElement;
		const start = textarea.selectionStart;
		const newStart = start + text.length;
		const value = textarea.value;
		const newValue = value.substr(0, start) + text + value.substr(start);

		textarea.value = newValue;
		textarea.setSelectionRange(newStart, newStart);

		const event = new Event('input', { 'bubbles': true, 'cancelable': true });
		textarea.dispatchEvent(event);
	}

J
Joao Moreno 已提交
164 165 166 167 168 169 170
	async getTerminalBuffer(selector: string): TPromise<string[]> {
		const element = document.querySelector(selector);

		if (!element) {
			throw new Error('Terminal not found: ' + selector);
		}

J
Joao Moreno 已提交
171 172 173 174 175
		const xterm = (element as any).xterm;

		if (!xterm) {
			throw new Error('Xterm not found: ' + selector);
		}
J
Joao Moreno 已提交
176 177 178

		const lines: string[] = [];

J
Joao Moreno 已提交
179 180
		for (let i = 0; i < xterm.buffer.lines.length; i++) {
			lines.push(xterm.buffer.translateBufferLineToString(i, true));
J
Joao Moreno 已提交
181 182 183
		}

		return lines;
J
Joao Moreno 已提交
184
	}
185

J
Joao Moreno 已提交
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
	async writeInTerminal(selector: string, text: string): TPromise<void> {
		const element = document.querySelector(selector);

		if (!element) {
			throw new Error('Element not found');
		}

		const xterm = (element as any).xterm;

		if (!xterm) {
			throw new Error('Xterm not found');
		}

		xterm.send(text);
	}

202 203 204
	async openDevTools(): TPromise<void> {
		await this.windowService.openDevTools({ mode: 'detach' });
	}
J
Joao Moreno 已提交
205 206
}

J
Joao Moreno 已提交
207 208 209 210 211 212
export async function registerWindowDriver(
	client: IPCClient,
	windowId: number,
	instantiationService: IInstantiationService
): TPromise<IDisposable> {
	const windowDriver = instantiationService.createInstance(WindowDriver);
J
Joao Moreno 已提交
213 214 215 216 217 218
	const windowDriverChannel = new WindowDriverChannel(windowDriver);
	client.registerChannel('windowDriver', windowDriverChannel);

	const windowDriverRegistryChannel = client.getChannel('windowDriverRegistry');
	const windowDriverRegistry = new WindowDriverRegistryChannelClient(windowDriverRegistryChannel);

219 220 221 222 223
	const options = await windowDriverRegistry.registerWindowDriver(windowId);

	if (options.verbose) {
		windowDriver.openDevTools();
	}
J
Joao Moreno 已提交
224

J
Joao Moreno 已提交
225 226
	const disposable = toDisposable(() => windowDriverRegistry.reloadWindowDriver(windowId));
	return combinedDisposable([disposable, client]);
J
Joao Moreno 已提交
227
}