提交 8eae25a2 编写于 作者: J Joao Moreno

remove selectorExecute

上级 fc0774e4
......@@ -34,7 +34,7 @@ export interface IDriver {
isActiveElement(windowId: number, selector: string): TPromise<boolean>;
getElements(windowId: number, selector: string, recursive: boolean): TPromise<IElement[]>;
typeInEditor(windowId: number, selector: string, text: string): TPromise<void>;
selectorExecute<P>(windowId: number, selector: string, script: (elements: HTMLElement[], ...args: any[]) => P, ...args: any[]): TPromise<P>;
getTerminalBuffer(windowId: number, selector: string): TPromise<string[]>;
}
//*END
......@@ -49,7 +49,7 @@ export interface IDriverChannel extends IChannel {
call(command: 'isActiveElement', arg: [number, string]): TPromise<boolean>;
call(command: 'getElements', arg: [number, string, boolean]): TPromise<IElement[]>;
call(command: 'typeInEditor', arg: [number, string, string]): TPromise<void>;
call(command: 'selectorExecute', arg: [number, string, string, any[]]): TPromise<any>;
call(command: 'getTerminalBuffer', arg: [number, string]): TPromise<string[]>;
call(command: string, arg: any): TPromise<any>;
}
......@@ -69,9 +69,7 @@ export class DriverChannel implements IDriverChannel {
case 'isActiveElement': return this.driver.isActiveElement(arg[0], arg[1]);
case 'getElements': return this.driver.getElements(arg[0], arg[1], arg[2]);
case 'typeInEditor': return this.driver.typeInEditor(arg[0], arg[1], arg[2]);
// TODO@joao
case 'selectorExecute': return this.driver.selectorExecute(arg[0], arg[1], arg[1], ...arg[2]);
case 'getTerminalBuffer': return this.driver.getTerminalBuffer(arg[0], arg[1]);
}
return undefined;
......@@ -124,9 +122,8 @@ export class DriverChannelClient implements IDriver {
return this.channel.call('typeInEditor', [windowId, selector, text]);
}
selectorExecute<P>(windowId: number, selector: string, script: (elements: HTMLElement[], ...args: any[]) => P, ...args: any[]): TPromise<P> {
// TODO@joao
return this.channel.call('selectorExecute', [windowId, selector, script.toString(), args]);
getTerminalBuffer(windowId: number, selector: string): TPromise<string[]> {
return this.channel.call('getTerminalBuffer', [windowId, selector]);
}
}
......@@ -172,7 +169,7 @@ export interface IWindowDriver {
isActiveElement(selector: string): TPromise<boolean>;
getElements(selector: string, recursive: boolean): TPromise<IElement[]>;
typeInEditor(selector: string, text: string): TPromise<void>;
selectorExecute<P>(selector: string, script: (elements: HTMLElement[], ...args: any[]) => P, ...args: any[]): TPromise<P>;
getTerminalBuffer(selector: string): TPromise<string[]>;
}
export interface IWindowDriverChannel extends IChannel {
......@@ -184,7 +181,7 @@ export interface IWindowDriverChannel extends IChannel {
call(command: 'isActiveElement', arg: string): TPromise<boolean>;
call(command: 'getElements', arg: [string, boolean]): TPromise<IElement[]>;
call(command: 'typeInEditor', arg: [string, string]): TPromise<void>;
call(command: 'selectorExecute', arg: [string, string, any[]]): TPromise<any>;
call(command: 'getTerminalBuffer', arg: string): TPromise<string[]>;
call(command: string, arg: any): TPromise<any>;
}
......@@ -202,8 +199,7 @@ export class WindowDriverChannel implements IWindowDriverChannel {
case 'isActiveElement': return this.driver.isActiveElement(arg);
case 'getElements': return this.driver.getElements(arg[0], arg[1]);
case 'typeInEditor': return this.driver.typeInEditor(arg[0], arg[1]);
// TODO@joao
case 'selectorExecute': return this.driver.selectorExecute(arg[0], arg[1], ...arg[2]);
case 'getTerminalBuffer': return this.driver.getTerminalBuffer(arg);
}
return undefined;
......@@ -248,8 +244,7 @@ export class WindowDriverChannelClient implements IWindowDriver {
return this.channel.call('typeInEditor', [selector, text]);
}
selectorExecute<P>(selector: string, script: (elements: HTMLElement[], ...args: any[]) => P, ...args: any[]): TPromise<P> {
// TODO@joao
return this.channel.call('selectorExecute', [selector, script.toString(), args]);
getTerminalBuffer(selector: string): TPromise<string[]> {
return this.channel.call('getTerminalBuffer', selector);
}
}
\ No newline at end of file
......@@ -95,9 +95,6 @@ class WindowDriver implements IWindowDriver {
}
const textarea = element as HTMLTextAreaElement;
console.log(textarea);
const start = textarea.selectionStart;
const newStart = start + text.length;
const value = textarea.value;
......@@ -110,8 +107,22 @@ class WindowDriver implements IWindowDriver {
textarea.dispatchEvent(event);
}
selectorExecute<P>(selector: string, script: (elements: HTMLElement[], ...args: any[]) => P, ...args: any[]): TPromise<P> {
return TPromise.wrapError(new Error('not implemented'));
async getTerminalBuffer(selector: string): TPromise<string[]> {
const element = document.querySelector(selector);
if (!element) {
throw new Error('Terminal not found: ' + selector);
}
const buffer = (element as any).xterm.buffer;
const lines: string[] = [];
for (let i = 0; i < buffer.lines.length; i++) {
lines.push(buffer.translateBufferLineToString(i, true));
}
return lines;
}
}
......
......@@ -132,14 +132,14 @@ export class Driver implements IDriver, IWindowDriverRegistry {
return windowDriver.getElements(selector, recursive);
}
selectorExecute<P>(windowId: number, selector: string, script: (elements: HTMLElement[], ...args: any[]) => P, ...args: any[]): TPromise<P> {
typeInEditor(windowId: number, selector: string, text: string): TPromise<void> {
const windowDriver = this.getWindowDriver(windowId);
return windowDriver.selectorExecute(selector, script, ...args);
return windowDriver.typeInEditor(selector, text);
}
typeInEditor(windowId: number, selector: string, text: string): TPromise<void> {
getTerminalBuffer(windowId: number, selector: string): TPromise<string[]> {
const windowDriver = this.getWindowDriver(windowId);
return windowDriver.typeInEditor(selector, text);
return windowDriver.getTerminalBuffer(selector);
}
private getWindowDriver(windowId: number): IWindowDriver {
......
......@@ -76,14 +76,14 @@ export class API {
return this.driver.getTitle();
}
selectorExecute<P>(selector: string, script: (elements: HTMLElement[], ...args: any[]) => P, ...args: any[]): Promise<P> {
return this.driver.selectorExecute(selector, script, ...args);
}
typeInEditor(selector: string, text: string): Promise<void> {
return this.driver.typeInEditor(selector, text);
}
getTerminalBuffer(selector: string): Promise<string[]> {
return this.driver.getTerminalBuffer(selector);
}
private running = false;
async waitFor<T>(func: () => T | Promise<T | undefined>, accept?: (result: T) => boolean | Promise<boolean>, timeoutMessage?: string, retryCount?: number): Promise<T>;
async waitFor<T>(func: () => T | Promise<T>, accept: (result: T) => boolean | Promise<boolean> = result => !!result, timeoutMessage?: string, retryCount?: number): Promise<T> {
......
......@@ -8,6 +8,7 @@ import { Commands } from '../workbench/workbench';
import { API } from '../../api';
import { Editors } from '../editor/editors';
import { Editor } from '../editor/editor';
import { findElement, Element } from '../../driver';
const VIEWLET = 'div[id="workbench.view.debug"]';
const DEBUG_VIEW = `${VIEWLET} .debug-view-content`;
......@@ -27,7 +28,7 @@ const TOOLBAR_HIDDEN = `.debug-actions-widget.monaco-builder-hidden`;
const STACK_FRAME = `${VIEWLET} .monaco-tree-row .stack-frame`;
const SPECIFIC_STACK_FRAME = filename => `${STACK_FRAME} .file[title$="${filename}"]`;
const VARIABLE = `${VIEWLET} .debug-variables .monaco-tree-row .expression`;
const CONSOLE_OUTPUT = `.repl .output.expression`;
const CONSOLE_OUTPUT = `.repl .output.expression .value`;
const CONSOLE_INPUT_OUTPUT = `.repl .input-output-pair .output.expression .value`;
const REPL_FOCUSED = '.repl-input-wrapper .monaco-editor textarea';
......@@ -37,6 +38,17 @@ export interface IStackFrame {
lineNumber: number;
}
function toStackFrame(element: Element): IStackFrame {
const name = findElement(element, e => /\bfile-name\b/.test(e.className))!;
const line = findElement(element, e => /\bline-number\b/.test(e.className))!;
const lineNumber = line.textContent ? parseInt(line.textContent.split(':').shift() || '0') : 0;
return {
name: name.textContent || '',
lineNumber
};
}
export class Debug extends Viewlet {
constructor(api: API, private commands: Commands, private editors: Editors, private editor: Editor) {
......@@ -64,12 +76,9 @@ export class Debug extends Viewlet {
await this.api.waitForElement(PAUSE);
await this.api.waitForElement(DEBUG_STATUS_BAR);
const portPrefix = 'Port: ';
await this.api.waitFor(async () => {
const output = await this.getConsoleOutput();
return output.join('');
}, text => !!text && text.indexOf(portPrefix) >= 0);
const output = await this.getConsoleOutput();
const lastOutput = output.pop();
const output = await this.waitForOutput(output => output.some(line => line.indexOf(portPrefix) >= 0));
const lastOutput = output.filter(line => line.indexOf(portPrefix) >= 0)[0];
return lastOutput ? parseInt(lastOutput.substr(portPrefix.length)) : 3000;
}
......@@ -98,10 +107,8 @@ export class Debug extends Viewlet {
}
async waitForStackFrame(func: (stackFrame: IStackFrame) => boolean, message: string): Promise<IStackFrame> {
return await this.api.waitFor(async () => {
const stackFrames = await this.getStackFrames();
return stackFrames.filter(func)[0];
}, void 0, `Waiting for Stack Frame: ${message}`);
const elements = await this.api.waitForElements(STACK_FRAME, true, elements => elements.some(e => func(toStackFrame(e))));
return elements.map(toStackFrame).filter(s => func(s))[0];
}
async waitForStackFrameLength(length: number): Promise<any> {
......@@ -122,45 +129,15 @@ export class Debug extends Viewlet {
await this.editor.waitForEditorContents('debug:input', s => s.indexOf(text) >= 0);
await this.api.dispatchKeybinding('enter');
await this.api.waitForElement(CONSOLE_INPUT_OUTPUT);
await this.api.waitFor(async () => {
const result = await this.getConsoleOutput();
return result[result.length - 1] || '';
}, accept);
await this.waitForOutput(output => accept(output[output.length - 1] || ''));
}
async getLocalVariableCount(): Promise<number> {
return await this.api.getElementCount(VARIABLE);
}
private async getStackFrames(): Promise<IStackFrame[]> {
const result = await this.api.selectorExecute(STACK_FRAME,
div => (Array.isArray(div) ? div : [div]).map(element => {
const name = element.querySelector('.file-name') as HTMLElement;
const line = element.querySelector('.line-number') as HTMLElement;
const lineNumber = line.textContent ? parseInt(line.textContent.split(':').shift() || '0') : 0;
return {
name: name.textContent || '',
lineNumber
};
})
);
if (!Array.isArray(result)) {
return [];
}
return result.map(({ name, lineNumber }) => ({ name, lineNumber }));
}
private async getConsoleOutput(): Promise<string[]> {
const result = await this.api.selectorExecute(CONSOLE_OUTPUT,
div => (Array.isArray(div) ? div : [div]).map(element => {
const value = element.querySelector('.value') as HTMLElement;
return value && value.textContent;
}).filter(line => !!line) as string[]
);
return result;
private async waitForOutput(fn: (output: string[]) => boolean): Promise<string[]> {
const elements = await this.api.waitForElements(CONSOLE_OUTPUT, false, elements => fn(elements.map(e => e.textContent)));
return elements.map(e => e.textContent);
}
}
......@@ -6,6 +6,7 @@
import { Viewlet } from '../workbench/viewlet';
import { API } from '../../api';
import { Commands } from '../workbench/workbench';
import { Element, findElement, findElements } from '../../driver';
const VIEWLET = 'div[id="workbench.view.scm"]';
const SCM_INPUT = `${VIEWLET} .scm-editor textarea`;
......@@ -23,6 +24,21 @@ interface Change {
actions: string[];
}
function toChange(element: Element): Change {
const name = findElement(element, e => /\blabel-name\b/.test(e.className))!;
const type = element.attributes['data-tooltip'] || '';
const actionElementList = findElements(element, e => /\baction-label\b/.test(e.className));
const actions = actionElementList.map(e => e.attributes['title']);
return {
name: name.textContent || '',
type,
actions
};
}
export class SCM extends Viewlet {
constructor(api: API, private commands: Commands) {
......@@ -34,53 +50,15 @@ export class SCM extends Viewlet {
await this.api.waitForElement(SCM_INPUT);
}
waitForChange(name: string, type?: string): Promise<void> {
return this.api.waitFor(async () => {
const changes = await this.queryChanges(name, type);
return changes.length;
}, l => l > 0, 'Getting SCM changes') as Promise<any> as Promise<void>;
async waitForChange(name: string, type?: string): Promise<void> {
const func = (change: Change) => change.name === name && (!type || change.type === type);
await this.api.waitForElements(SCM_RESOURCE, true, elements => elements.some(e => func(toChange(e))));
}
async refreshSCMViewlet(): Promise<any> {
await this.api.waitAndClick(REFRESH_COMMAND);
}
private async queryChanges(name: string, type?: string): Promise<Change[]> {
const result = await this.api.selectorExecute(SCM_RESOURCE, (div, name, type) => {
return (Array.isArray(div) ? div : [div])
.map(element => {
const name = element.querySelector('.label-name') as HTMLElement;
const type = element.getAttribute('data-tooltip') || '';
const actionElementList = element.querySelectorAll('.actions .action-label');
const actions: string[] = [];
for (let i = 0; i < actionElementList.length; i++) {
const element = actionElementList.item(i) as HTMLElement;
actions.push(element.title);
}
return {
name: name.textContent || '',
type,
actions
};
})
.filter(change => {
if (change.name !== name) {
return false;
}
if (type && (change.type !== type)) {
return false;
}
return true;
});
}, name, type);
return result;
}
async openChange(name: string): Promise<void> {
await this.api.waitAndClick(SCM_RESOURCE_CLICK(name));
}
......
......@@ -31,31 +31,15 @@ export class Terminal {
await this.api.dispatchKeybinding('enter');
}
async waitForTerminalText(fn: (text: string[]) => boolean, timeOutDescription: string = 'Getting Terminal Text'): Promise<string[]> {
return this.api.waitFor(async () => {
const terminalText = await this.getTerminalText();
if (fn(terminalText)) {
return terminalText;
}
return undefined;
async waitForTerminalText(fn: (text: string[]) => boolean, timeOutDescription: string = 'Getting Terminal Text'): Promise<void> {
await this.api.waitFor(async () => {
const terminalText = await this.api.getTerminalBuffer(XTERM_SELECTOR);
return fn(terminalText);
}, void 0, timeOutDescription);
}
getCurrentLineNumber(): Promise<number> {
return this.getTerminalText().then(text => text.length);
}
private async getTerminalText(): Promise<string[]> {
return await this.api.selectorExecute(XTERM_SELECTOR,
div => {
const xterm = (<any>(Array.isArray(div) ? div[0] : div)).xterm;
const buffer = xterm.buffer;
const lines: string[] = [];
for (let i = 0; i < buffer.lines.length; i++) {
lines.push(buffer.translateBufferLineToString(i, true));
}
return lines;
}
);
async getCurrentLineNumber(): Promise<number> {
const terminalText = await this.api.getTerminalBuffer(XTERM_SELECTOR);
return terminalText.length;
}
}
\ No newline at end of file
......@@ -11,6 +11,7 @@ export interface Element {
className: string;
textContent: string;
attributes: { [name: string]: string };
children: Element[];
}
export interface Driver {
......@@ -24,7 +25,7 @@ export interface Driver {
isActiveElement(selector: string): Promise<boolean>;
getElements(selector: string, recursive?: boolean): Promise<Element[]>;
typeInEditor(selector: string, text: string): Promise<void>;
selectorExecute<P>(selector: string, script: (elements: HTMLElement[], ...args: any[]) => P, ...args: any[]): Promise<P>;
getTerminalBuffer(selector: string): Promise<string[]>;
}
export class SpectronDriver implements Driver {
......@@ -107,7 +108,8 @@ export class SpectronDriver implements Driver {
tagName: element.tagName,
className: element.className,
textContent: element.textContent || '',
attributes: {}
attributes: {},
children: []
});
}
......@@ -121,13 +123,8 @@ export class SpectronDriver implements Driver {
throw new Error('Method not implemented.');
}
async selectorExecute<P>(selector: string, script: (elements: HTMLElement[], ...args: any[]) => P, ...args: any[]): Promise<P> {
if (this.verbose) {
console.log('- selectorExecute:', selector);
}
let _script = (element, script, ...args) => script(Array.isArray(element) ? element : [element], ...args);
return this.spectronClient.selectorExecute(selector, _script, script, ...args);
getTerminalBuffer(selector: string): Promise<string[]> {
throw new Error('Method not implemented.');
}
}
......@@ -209,26 +206,25 @@ export class CodeDriver implements Driver {
}
const windowId = await this.getWindowId();
const result = await this.driver.getElements(windowId, selector, recursive);
return result;
return await this.driver.getElements(windowId, selector, recursive);
}
async selectorExecute<P>(selector: string, script: (elements: HTMLElement[], ...args: any[]) => P, ...args: any[]): Promise<P> {
async typeInEditor(selector: string, text: string): Promise<void> {
if (this.verbose) {
console.log('- selectorExecute:', selector);
console.log('- typeInEditor:', selector, text);
}
const windowId = await this.getWindowId();
return await this.driver.selectorExecute(windowId, selector, script, ...args);
return await this.driver.typeInEditor(windowId, selector, text);
}
async typeInEditor(selector: string, text: string): Promise<void> {
async getTerminalBuffer(selector: string): Promise<string[]> {
if (this.verbose) {
console.log('- typeInEditor:', selector, text);
console.log('- getTerminalBuffer:', selector);
}
const windowId = await this.getWindowId();
return await this.driver.typeInEditor(windowId, selector, text);
return await this.driver.getTerminalBuffer(windowId, selector);
}
private async getWindowId(): Promise<number> {
......@@ -239,4 +235,37 @@ export class CodeDriver implements Driver {
return this._activeWindowId;
}
}
export function findElement(element: Element, fn: (element: Element) => boolean): Element | null {
const queue = [element];
while (queue.length > 0) {
const element = queue.shift()!;
if (fn(element)) {
return element;
}
queue.push(...element.children);
}
return null;
}
export function findElements(element: Element, fn: (element: Element) => boolean): Element[] {
const result: Element[] = [];
const queue = [element];
while (queue.length > 0) {
const element = queue.shift()!;
if (fn(element)) {
result.push(element);
}
queue.push(...element.children);
}
return result;
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册