Refresh master with initial release/0.24 snapshot (#332)

* Initial port of release/0.24 source code

* Fix additional headers

* Fix a typo in launch.json
This commit is contained in:
Karl Burtram
2017-12-15 15:38:57 -08:00
committed by GitHub
parent 271b3a0b82
commit 6ad0df0e3e
7118 changed files with 107999 additions and 56466 deletions

View File

@@ -0,0 +1,154 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as http from 'http';
import * as os from 'os';
import * as path from 'path';
import * as fs from 'fs';
import * as stripJsonComments from 'strip-json-comments';
import { SpectronApplication, Quality } from '../../spectron/application';
describe('Debug', () => {
before(async function () {
const app = this.app as SpectronApplication;
if (app.quality === Quality.Dev) {
const extensionsPath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions');
const debugPath = path.join(extensionsPath, 'vscode-node-debug');
const debugExists = fs.existsSync(debugPath);
const debug2Path = path.join(extensionsPath, 'vscode-node-debug2');
const debug2Exists = fs.existsSync(debug2Path);
if (!debugExists) {
console.warn(`Skipping debug tests because vscode-node-debug extension was not found in ${extensionsPath}`);
return;
}
if (!debug2Exists) {
console.warn(`Skipping debug tests because vscode-node-debug2 extension was not found in ${extensionsPath}`);
return;
}
await new Promise((c, e) => fs.symlink(debugPath, path.join(app.extensionsPath, 'vscode-node-debug'), err => err ? e(err) : c()));
await new Promise((c, e) => fs.symlink(debug2Path, path.join(app.extensionsPath, 'vscode-node-debug2'), err => err ? e(err) : c()));
await app.reload();
}
this.app.suiteName = 'Debug';
});
it('configure launch json', async function () {
const app = this.app as SpectronApplication;
await app.workbench.debug.openDebugViewlet();
await app.workbench.quickopen.openFile('app.js');
await app.workbench.debug.configure();
const launchJsonPath = path.join(app.workspacePath, '.vscode', 'launch.json');
const content = fs.readFileSync(launchJsonPath, 'utf8');
const config = JSON.parse(stripJsonComments(content));
config.configurations[0].protocol = 'inspector';
fs.writeFileSync(launchJsonPath, JSON.stringify(config, undefined, 4), 'utf8');
await app.workbench.editor.waitForEditorContents('launch.json', contents => /"protocol": "inspector"/.test(contents));
await app.screenCapturer.capture('launch.json file');
assert.equal(config.configurations[0].request, 'launch');
assert.equal(config.configurations[0].type, 'node');
if (process.platform === 'win32') {
assert.equal(config.configurations[0].program, '${workspaceFolder}\\bin\\www');
} else {
assert.equal(config.configurations[0].program, '${workspaceFolder}/bin/www');
}
});
it('breakpoints', async function () {
const app = this.app as SpectronApplication;
await app.workbench.quickopen.openFile('index.js');
await app.workbench.debug.setBreakpointOnLine(6);
await app.screenCapturer.capture('breakpoints are set');
});
let port: number;
it('start debugging', async function () {
const app = this.app as SpectronApplication;
port = await app.workbench.debug.startDebugging();
await app.screenCapturer.capture('debugging has started');
await new Promise((c, e) => {
const request = http.get(`http://localhost:${port}`);
request.on('error', e);
app.workbench.debug.waitForStackFrame(sf => sf.name === 'index.js' && sf.lineNumber === 6, 'looking for index.js and line 6').then(c, e);
});
await app.screenCapturer.capture('debugging is paused');
});
it('focus stack frames and variables', async function () {
const app = this.app as SpectronApplication;
await app.client.waitFor(() => app.workbench.debug.getLocalVariableCount(), c => c === 4, 'there should be 4 local variables');
await app.workbench.debug.focusStackFrame('layer.js', 'looking for layer.js');
await app.client.waitFor(() => app.workbench.debug.getLocalVariableCount(), c => c === 5, 'there should be 5 local variables');
await app.workbench.debug.focusStackFrame('route.js', 'looking for route.js');
await app.client.waitFor(() => app.workbench.debug.getLocalVariableCount(), c => c === 3, 'there should be 3 local variables');
await app.workbench.debug.focusStackFrame('index.js', 'looking for index.js');
await app.client.waitFor(() => app.workbench.debug.getLocalVariableCount(), c => c === 4, 'there should be 4 local variables');
});
it('stepOver, stepIn, stepOut', async function () {
const app = this.app as SpectronApplication;
await app.workbench.debug.stepIn();
await app.screenCapturer.capture('debugging has stepped in');
const first = await app.workbench.debug.waitForStackFrame(sf => sf.name === 'response.js', 'looking for response.js');
await app.workbench.debug.stepOver();
await app.screenCapturer.capture('debugging has stepped over');
await app.workbench.debug.waitForStackFrame(sf => sf.name === 'response.js' && sf.lineNumber === first.lineNumber + 1, `looking for response.js and line ${first.lineNumber + 1}`);
await app.workbench.debug.stepOut();
await app.screenCapturer.capture('debugging has stepped out');
await app.workbench.debug.waitForStackFrame(sf => sf.name === 'index.js' && sf.lineNumber === 7, `looking for index.js and line 7`);
});
it('continue', async function () {
const app = this.app as SpectronApplication;
await app.workbench.debug.continue();
await app.screenCapturer.capture('debugging has continued');
await new Promise((c, e) => {
const request = http.get(`http://localhost:${port}`);
request.on('error', e);
app.workbench.debug.waitForStackFrame(sf => sf.name === 'index.js' && sf.lineNumber === 6, `looking for index.js and line 6`).then(c, e);
});
await app.screenCapturer.capture('debugging is paused');
});
it('debug console', async function () {
const app = this.app as SpectronApplication;
await app.workbench.debug.waitForReplCommand('2 + 2', r => r === '4');
});
it('stop debugging', async function () {
const app = this.app as SpectronApplication;
await app.workbench.debug.stopDebugging();
await app.screenCapturer.capture('debugging has stopped');
});
});

View File

@@ -0,0 +1,171 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SpectronApplication } from '../../spectron/application';
import { Viewlet } from '../workbench/viewlet';
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"]`;
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`;
const GLYPH_AREA = '.margin-view-overlays>:nth-child';
const BREAKPOINT_GLYPH = '.debug-breakpoint-glyph';
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`;
const CONSOLE_OUTPUT = `.repl .output.expression`;
const CONSOLE_INPUT_OUTPUT = `.repl .input-output-pair .output.expression .value`;
const REPL_FOCUSED = '.repl-input-wrapper .monaco-editor textarea';
export interface IStackFrame {
id: string;
name: string;
lineNumber: number;
}
export class Debug extends Viewlet {
constructor(spectron: SpectronApplication) {
super(spectron);
}
async openDebugViewlet(): Promise<any> {
await this.spectron.runCommand('workbench.view.debug');
await this.spectron.client.waitForElement(DEBUG_VIEW);
}
async configure(): Promise<any> {
await this.spectron.client.waitAndClick(CONFIGURE);
await this.spectron.workbench.waitForEditorFocus('launch.json');
}
async setBreakpointOnLine(lineNumber: number): Promise<any> {
await this.spectron.client.waitForElement(`${GLYPH_AREA}(${lineNumber})`);
await this.spectron.client.leftClick(`${GLYPH_AREA}(${lineNumber})`, 5, 5);
await this.spectron.client.waitForElement(BREAKPOINT_GLYPH);
}
async startDebugging(): Promise<number> {
await this.spectron.client.waitAndClick(START);
await this.spectron.client.waitForElement(PAUSE);
await this.spectron.client.waitForElement(DEBUG_STATUS_BAR);
const portPrefix = 'Port: ';
await this.spectron.client.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();
return lastOutput ? parseInt(lastOutput.substr(portPrefix.length)) : 3000;
}
async stepOver(): Promise<any> {
await this.spectron.client.waitAndClick(STEP_OVER);
}
async stepIn(): Promise<any> {
await this.spectron.client.waitAndClick(STEP_IN);
}
async stepOut(): Promise<any> {
await this.spectron.client.waitAndClick(STEP_OUT);
}
async continue(): Promise<any> {
await this.spectron.client.waitAndClick(CONTINUE);
await this.waitForStackFrameLength(0);
}
async stopDebugging(): Promise<any> {
await this.spectron.client.waitAndClick(STOP);
await this.spectron.client.waitForElement(TOOLBAR_HIDDEN);
await this.spectron.client.waitForElement(NOT_DEBUG_STATUS_BAR);
}
async waitForStackFrame(func: (stackFrame: IStackFrame) => boolean, message: string): Promise<IStackFrame> {
return await this.spectron.client.waitFor(async () => {
const stackFrames = await this.getStackFrames();
return stackFrames.filter(func)[0];
}, void 0, `Waiting for Stack Frame: ${message}`);
}
async waitForStackFrameLength(length: number): Promise<any> {
return await this.spectron.client.waitFor(() => this.getStackFrames(), stackFrames => stackFrames.length === length);
}
async focusStackFrame(name: string, message: string): Promise<any> {
const stackFrame = await this.waitForStackFrame(sf => sf.name === name, message);
await this.spectron.client.spectron.client.elementIdClick(stackFrame.id);
await this.spectron.workbench.waitForTab(name);
}
async waitForReplCommand(text: string, accept: (result: string) => boolean): Promise<void> {
await this.spectron.workbench.quickopen.runCommand('Debug: Focus Debug Console');
await this.spectron.client.waitForActiveElement(REPL_FOCUSED);
await this.spectron.client.setValue(REPL_FOCUSED, text);
// Wait for the keys to be picked up by the editor model such that repl evalutes what just got typed
await this.spectron.workbench.editor.waitForEditorContents('debug:input', s => s.indexOf(text) >= 0);
await this.spectron.client.keys(['Enter', 'NULL']);
await this.spectron.client.waitForElement(CONSOLE_INPUT_OUTPUT);
await this.spectron.client.waitFor(async () => {
const result = await this.getConsoleOutput();
return result[result.length - 1] || '';
}, accept);
}
async getLocalVariableCount(): Promise<number> {
return await this.spectron.webclient.selectorExecute(VARIABLE, div => (Array.isArray(div) ? div : [div]).length);
}
async getStackFramesLength(): Promise<number> {
const stackFrames = await this.getStackFrames();
return stackFrames.length;
}
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
};
})
);
if (!Array.isArray(result)) {
return [];
}
return result
.map(({ name, lineNumber, element }) => ({ name, lineNumber, id: element.ELEMENT }));
}
private async getConsoleOutput(): Promise<string[]> {
const result = await this.spectron.webclient.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)
);
return result;
}
}