debug.ts 5.9 KB
Newer Older
I
isidor 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import { SpectronApplication } from '../../spectron/application';
7
import { Viewlet } from '../workbench/viewlet';
I
isidor 已提交
8 9 10 11 12

const VIEWLET = 'div[id="workbench.view.debug"]';
const DEBUG_VIEW = `${VIEWLET} .debug-view-content`;
const CONFIGURE = `div[id="workbench.parts.sidebar"] .actions-container .configure`;
const START = `.icon[title="Start Debugging"]`;
I
isidor 已提交
13 14 15 16 17
const STOP = `.debug-actions-widget .debug-action.stop`;
const STEP_OVER = `.debug-actions-widget .debug-action.step-over`;
const STEP_IN = `.debug-actions-widget .debug-action.step-into`;
const STEP_OUT = `.debug-actions-widget .debug-action.step-out`;
const CONTINUE = `.debug-actions-widget .debug-action.continue`;
I
isidor 已提交
18 19
const GLYPH_AREA = '.margin-view-overlays>:nth-child';
const BREAKPOINT_GLYPH = '.debug-breakpoint-glyph';
I
isidor 已提交
20 21 22 23 24 25
const PAUSE = `.debug-actions-widget .debug-action.pause`;
const DEBUG_STATUS_BAR = `.statusbar.debugging`;
const NOT_DEBUG_STATUS_BAR = `.statusbar:not(debugging)`;
const TOOLBAR_HIDDEN = `.debug-actions-widget.builder-hidden`;
const STACK_FRAME = `${VIEWLET} .monaco-tree-row .stack-frame`;
const VARIABLE = `${VIEWLET} .debug-variables .monaco-tree-row .expression`;
J
Joao 已提交
26
const CONSOLE_OUTPUT = `.repl .output.expression`;
I
isidor 已提交
27
const CONSOLE_INPUT_OUTPUT = `.repl .input-output-pair .output.expression .value`;
J
Joao 已提交
28
const SCOPE = `${VIEWLET} .debug-variables .scope`;
I
isidor 已提交
29 30 31 32 33 34 35 36

const REPL_FOCUSED = '.repl-input-wrapper .monaco-editor.focused';

export interface IStackFrame {
	id: string;
	name: string;
	lineNumber: number;
}
I
isidor 已提交
37

38
export class Debug extends Viewlet {
I
isidor 已提交
39

40 41
	constructor(spectron: SpectronApplication) {
		super(spectron);
I
isidor 已提交
42 43 44 45 46 47 48 49
	}

	async openDebugViewlet(): Promise<any> {
		await this.spectron.command('workbench.view.debug');
		await this.spectron.client.waitForElement(DEBUG_VIEW);
	}

	async configure(): Promise<any> {
50
		await this.spectron.client.waitAndClick(CONFIGURE);
I
isidor 已提交
51 52 53 54
		await this.spectron.workbench.waitForEditorFocus('launch.json');
	}

	async setBreakpointOnLine(lineNumber: number): Promise<any> {
55
		await this.spectron.client.waitForElement(`${GLYPH_AREA}(${lineNumber})`);
I
isidor 已提交
56 57 58 59 60
		await this.spectron.client.leftClick(`${GLYPH_AREA}(${lineNumber})`, 5, 5);
		await this.spectron.client.waitForElement(BREAKPOINT_GLYPH);
	}

	async startDebugging(): Promise<any> {
61
		await this.spectron.client.waitAndClick(START);
I
isidor 已提交
62 63
		await this.spectron.client.waitForElement(PAUSE);
		await this.spectron.client.waitForElement(DEBUG_STATUS_BAR);
I
isidor 已提交
64 65
		await this.spectron.client.waitFor(async () => {
			const output = await this.getConsoleOutput();
I
isidor 已提交
66 67
			return output.join('');
		}, text => text.indexOf('Debugger listening on') >= 0);
I
isidor 已提交
68 69 70
	}

	async stepOver(): Promise<any> {
71
		await this.spectron.client.waitAndClick(STEP_OVER);
I
isidor 已提交
72 73 74
	}

	async stepIn(): Promise<any> {
75
		await this.spectron.client.waitAndClick(STEP_IN);
I
isidor 已提交
76 77 78
	}

	async stepOut(): Promise<any> {
79
		await this.spectron.client.waitAndClick(STEP_OUT);
I
isidor 已提交
80 81 82
	}

	async continue(): Promise<any> {
83
		await this.spectron.client.waitAndClick(CONTINUE);
J
Joao 已提交
84
		await this.waitForStackFrameLength(0);
I
isidor 已提交
85 86 87
	}

	async stopDebugging(): Promise<any> {
88
		await this.spectron.client.waitAndClick(STOP);
I
isidor 已提交
89 90 91 92 93 94 95 96
		await this.spectron.client.waitForElement(TOOLBAR_HIDDEN);
		await this.spectron.client.waitForElement(NOT_DEBUG_STATUS_BAR);
	}

	async waitForStackFrame(func: (stackFrame: IStackFrame) => boolean): Promise<IStackFrame> {
		return await this.spectron.client.waitFor(async () => {
			const stackFrames = await this.getStackFrames();
			return stackFrames.filter(func)[0];
97
		}, void 0, 'Waiting for Stack Frame');
I
isidor 已提交
98 99
	}

J
Joao 已提交
100 101 102 103
	async waitForStackFrameLength(length: number): Promise<any> {
		return await this.spectron.client.waitFor(() => this.getStackFrames(), stackFrames => stackFrames.length === length);
	}

I
isidor 已提交
104 105 106
	async focusStackFrame(name: string): Promise<any> {
		const stackFrame = await this.waitForStackFrame(sf => sf.name === name);
		await this.spectron.client.spectron.client.elementIdClick(stackFrame.id);
107
		await this.spectron.workbench.waitForTab(name);
I
isidor 已提交
108 109
	}

I
isidor 已提交
110
	async console(text: string): Promise<string> {
111
		await this.spectron.workbench.quickopen.runCommand('Debug: Focus Debug Console');
I
isidor 已提交
112 113
		await this.spectron.client.waitForElement(REPL_FOCUSED);
		await this.spectron.client.type(text);
I
isidor 已提交
114
		await this.spectron.client.waitForElement(CONSOLE_INPUT_OUTPUT);
I
isidor 已提交
115

J
Joao 已提交
116 117
		const result = await this.getConsoleOutput();
		return result[result.length - 1] || '';
I
isidor 已提交
118 119 120
	}

	async getLocalVariableCount(): Promise<number> {
J
Joao 已提交
121
		await this.spectron.client.waitForElement(SCOPE);
I
isidor 已提交
122 123 124
		return await this.spectron.webclient.selectorExecute(VARIABLE, div => (Array.isArray(div) ? div : [div]).length);
	}

J
Joao 已提交
125 126 127 128 129
	async getStackFramesLength(): Promise<number> {
		const stackFrames = await this.getStackFrames();
		return stackFrames.length;
	}

I
isidor 已提交
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
	private async getStackFrames(): Promise<IStackFrame[]> {
		const result = await this.spectron.webclient.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,
					element
				};
			})
		);

J
Joao 已提交
145 146 147 148 149 150
		if (!Array.isArray(result)) {
			return [];
		}

		return result
			.map(({ name, lineNumber, element }) => ({ name, lineNumber, id: element.ELEMENT }));
I
isidor 已提交
151 152
	}

J
Joao 已提交
153
	private async getConsoleOutput(): Promise<string[]> {
I
isidor 已提交
154 155 156
		const result = await this.spectron.webclient.selectorExecute(CONSOLE_OUTPUT,
			div => (Array.isArray(div) ? div : [div]).map(element => {
				const value = element.querySelector('.value') as HTMLElement;
J
Joao 已提交
157 158
				return value && value.textContent;
			}).filter(line => !!line)
I
isidor 已提交
159 160
		);

J
Joao 已提交
161
		return result;
I
isidor 已提交
162
	}
I
isidor 已提交
163
}